Fail2Ban 是 Linux 服务器上很常见的一类安全防护工具,主要用来应对 SSH、Web 登录、邮件服务等场景里的暴力破解和重复异常请求。它不会替代防火墙、密钥登录、最小权限这些基础安全措施,但非常适合做一层自动化封禁:发现某个 IP 在短时间内频繁失败,就把它临时加入黑名单,过一段时间后再自动释放。

这篇记录一下 Fail2Ban 的工作原理、安装和开机启动方式、常用规则配置,以及误封后如何把某个 IP 从黑名单里移出来。

Fail2Ban 的工作原理#

Fail2Ban 的核心逻辑可以概括成四步:

  1. 监听日志文件或 systemd journal。
  2. 用 filter 里的正则表达式匹配失败行为。
  3. 在指定时间窗口内统计同一个 IP 的失败次数。
  4. 达到阈值后执行 action,把 IP 加入防火墙黑名单。

这里有几个关键概念。

jail#

jail 是 Fail2Ban 的规则单元。一个 jail 通常对应一个服务,例如 SSH、Nginx、Postfix、Dovecot 等。每个 jail 会指定:

  • 监控哪个服务。
  • 读取哪个日志。
  • 使用哪个 filter。
  • 允许失败多少次。
  • 统计时间窗口是多少。
  • 封禁多久。
  • 使用什么 action 封禁 IP。

例如 SSH 的 jail 就是监听 SSH 登录日志,匹配登录失败记录,当同一个 IP 在一段时间内失败太多,就通过防火墙封禁。

filter#

filter 负责识别“什么日志算失败”。它通常放在:

/etc/fail2ban/filter.d/

例如 SSH 常见 filter 是:

/etc/fail2ban/filter.d/sshd.conf

filter 文件里最重要的是 failregex,也就是匹配失败日志的正则表达式。Fail2Ban 会从日志行里提取 IP,并把它计入失败次数。

一般情况下,SSH、Nginx、Apache、Postfix 等常见服务已经有内置 filter,不建议一上来就自己写正则。只有业务日志格式比较特殊时,才需要自定义 filter。

action#

action 负责“封禁时具体做什么”。常见动作包括:

  • 使用 iptables 添加规则。
  • 使用 nftables 添加规则。
  • 使用 firewalld 添加规则。
  • 发送邮件通知。
  • 执行自定义脚本。

在现代 Linux 发行版里,很多系统默认已经转向 nftables,但 Fail2Ban 仍然可以通过不同 action 适配不同防火墙后端。

bantime、findtime、maxretry#

Fail2Ban 最常调的三个参数是:

bantime  = 1h
findtime = 10m
maxretry = 5

它们的意思是:

  • findtime:统计窗口。比如 10 分钟内。
  • maxretry:最大失败次数。比如失败 5 次。
  • bantime:封禁时间。比如封禁 1 小时。

合起来就是:同一个 IP 在 10 分钟内失败 5 次,就封禁 1 小时。

Fail2Ban 支持比较直观的时间写法,例如:

10m
1h
1d

也可以直接写秒数。

安装 Fail2Ban#

Debian / Ubuntu:

sudo apt update
sudo apt install -y fail2ban

RHEL / CentOS / Rocky Linux / AlmaLinux:

sudo dnf install -y epel-release
sudo dnf install -y fail2ban

有些较老系统使用 yum

sudo yum install -y epel-release
sudo yum install -y fail2ban

安装完成后确认版本:

fail2ban-client version

启动和开机自启#

Fail2Ban 通常由 systemd 管理。启动服务:

sudo systemctl start fail2ban

设置开机自启:

sudo systemctl enable fail2ban

也可以合并成一条:

sudo systemctl enable --now fail2ban

查看服务状态:

sudo systemctl status fail2ban

确认是否开机自启:

systemctl is-enabled fail2ban

如果输出是:

enabled

说明已经设置为开机启动。

配置文件应该怎么改#

Fail2Ban 的主配置文件通常是:

/etc/fail2ban/jail.conf

但不建议直接修改 jail.conf。因为软件升级时它可能被覆盖。推荐做法是使用:

/etc/fail2ban/jail.local

或者拆成多个文件放到:

/etc/fail2ban/jail.d/*.conf

推荐结构是:

/etc/fail2ban/jail.local
/etc/fail2ban/jail.d/sshd.local
/etc/fail2ban/jail.d/nginx.local

小型服务器可以只用 jail.local,规则多了以后再拆到 jail.d/

一个基础的 jail.local 示例#

可以创建或编辑:

sudo vim /etc/fail2ban/jail.local

写入:

[DEFAULT]
ignoreip = 127.0.0.1/8 ::1
bantime = 1h
findtime = 10m
maxretry = 5
backend = systemd

[sshd]
enabled = true
port = ssh
filter = sshd
logpath = %(sshd_log)s
maxretry = 5

这里的含义是:

  • [DEFAULT] 是全局默认配置。
  • ignoreip 是永远不封禁的 IP 或网段。
  • backend = systemd 表示从 systemd journal 读取日志。
  • [sshd] 是 SSH 服务的 jail。
  • enabled = true 表示启用这条规则。
  • filter = sshd 表示使用内置的 SSH filter。

如果系统不是通过 journal 读取 SSH 日志,也可以使用传统日志路径。例如 Debian / Ubuntu 常见是:

logpath = /var/log/auth.log

RHEL 系常见是:

logpath = /var/log/secure

具体以系统实际日志位置为准。

配置 SSH 防爆破规则#

SSH 是最常见的 Fail2Ban 使用场景。一个稍严格一点的配置可以这样写:

[sshd]
enabled = true
port = ssh
filter = sshd
backend = systemd
maxretry = 4
findtime = 10m
bantime = 2h

这表示同一个 IP 在 10 分钟内 SSH 登录失败 4 次,就封禁 2 小时。

如果 SSH 使用了非默认端口,可以写成:

port = 2222

或者多个端口:

port = ssh,2222

不过更推荐配合 SSH 自身安全策略一起做:

  • 禁止 root 密码登录。
  • 优先使用密钥登录。
  • 关闭不需要的账号。
  • 限制安全组或防火墙来源。
  • Fail2Ban 作为自动封禁补充。

配置 Nginx 登录失败规则#

如果 Nginx 后面有登录接口,并且日志里会出现明确的 401、403 或业务失败特征,可以启用或自定义 Nginx 规则。

常见内置规则包括:

[nginx-http-auth]
enabled = true
port = http,https
logpath = /var/log/nginx/error.log
maxretry = 5
findtime = 10m
bantime = 1h

这个规则主要用于 HTTP Basic Auth 失败。

如果是自己的业务登录接口,通常需要自定义 filter。比如创建:

sudo vim /etc/fail2ban/filter.d/app-login.conf

示例:

[Definition]
failregex = ^.*login failed.*client=<HOST>.*$
ignoreregex =

然后在 jail 里引用:

[app-login]
enabled = true
port = http,https
filter = app-login
logpath = /var/log/myapp/login.log
maxretry = 5
findtime = 10m
bantime = 1h

这里的重点不是照抄正则,而是确保业务日志里真的有稳定的失败标识和客户端 IP。否则可能匹配不到,也可能误封。

检查配置是否有效#

修改配置后,先检查配置是否能被加载:

sudo fail2ban-client -d

这个命令会输出 Fail2Ban 解析后的配置。它比较长,但适合排查配置是否生效。

重启服务:

sudo systemctl restart fail2ban

查看服务状态:

sudo systemctl status fail2ban

查看 Fail2Ban 日志:

sudo journalctl -u fail2ban -e

或者:

sudo tail -f /var/log/fail2ban.log

不同系统日志位置可能不同,以实际环境为准。

查看当前启用的 jail#

使用:

sudo fail2ban-client status

输出类似:

Status
|- Number of jail:      1
`- Jail list:           sshd

查看某个 jail 的详细状态:

sudo fail2ban-client status sshd

输出里通常会看到:

Currently failed
Total failed
Currently banned
Total banned
Banned IP list

其中 Banned IP list 就是当前被封禁的 IP 列表。

手动封禁某个 IP#

有时发现某个 IP 明显异常,可以手动加入某个 jail:

sudo fail2ban-client set sshd banip 203.0.113.10

这里的 sshd 是 jail 名称,203.0.113.10 是示例 IP。实际使用时替换成目标 IP。

封禁后查看:

sudo fail2ban-client status sshd

将某个 IP 移出黑名单#

如果误封了自己的 IP,或者确认某个 IP 已经不需要继续封禁,可以用 unbanip 移除。

例如从 SSH jail 里移除:

sudo fail2ban-client set sshd unbanip 203.0.113.10

再查看状态:

sudo fail2ban-client status sshd

如果不确定 IP 是被哪个 jail 封禁的,可以先查看全部 jail:

sudo fail2ban-client status

然后逐个查看:

sudo fail2ban-client status sshd
sudo fail2ban-client status nginx-http-auth
sudo fail2ban-client status app-login

找到对应 jail 后再执行 unbanip

一次性移除某个 jail 的所有封禁#

如果确认要清空某个 jail 的封禁列表,可以执行:

sudo fail2ban-client unban --all

不过这会解除所有 jail 的封禁,影响范围比较大。更稳妥的方式还是针对某个 IP 做 unbanip

部分版本也支持针对 jail 操作,具体可以先查看帮助:

fail2ban-client --help

生产环境里不要随手全量解封,除非确认封禁规则本身配置错了。

白名单设置#

如果有固定办公出口 IP、堡垒机 IP、监控机 IP,可以加入 ignoreip,避免被误封。

示例:

[DEFAULT]
ignoreip = 127.0.0.1/8 ::1 192.0.2.10 198.51.100.0/24

修改后重启:

sudo systemctl restart fail2ban

注意:已经被封禁的 IP,不一定会因为加入 ignoreip 立刻自动解封。稳妥做法是配置白名单后,再手动执行一次 unbanip

常见排查思路#

jail 没有启动#

先看状态:

sudo fail2ban-client status

如果 jail list 里没有目标 jail,检查:

  • 配置文件是否放在正确位置。
  • jail 里是否写了 enabled = true
  • 配置语法是否有错。
  • 服务是否已经重启。

一直没有封禁#

检查几个方向:

  • logpath 是否正确。
  • backend 是否适合当前系统。
  • filter 正则是否能匹配日志。
  • 失败次数是否达到 maxretry
  • 时间窗口是否落在 findtime 内。

可以用 fail2ban-regex 测试日志和 filter 是否匹配:

sudo fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf

如果使用 systemd backend,也可以先确认 journal 里确实有相关日志:

sudo journalctl -u ssh -e
sudo journalctl -u sshd -e

不同发行版服务名可能是 sshsshd

误封频繁#

如果误封比较多,可以适当放宽:

maxretry = 8
findtime = 10m
bantime = 30m

也可以把可信 IP 加到 ignoreip。如果是业务登录接口误封,优先检查 filter 正则是否过宽。

防火墙规则没有生效#

检查 action 和系统防火墙后端是否匹配。比如系统使用 nftables,但配置里指定了不合适的 iptables action,就可能出现规则不生效或行为不符合预期。

可以查看当前 action 配置,也可以查看系统防火墙规则:

sudo iptables -S
sudo nft list ruleset
sudo firewall-cmd --list-all

具体使用哪个命令取决于系统防火墙后端。

推荐的基础配置#

如果只是保护 SSH,可以从这个配置开始:

[DEFAULT]
ignoreip = 127.0.0.1/8 ::1
bantime = 1h
findtime = 10m
maxretry = 5
backend = systemd

[sshd]
enabled = true
port = ssh
filter = sshd
maxretry = 5
findtime = 10m
bantime = 1h

如果服务器经常被扫,可以逐步加严:

[sshd]
enabled = true
port = ssh
filter = sshd
maxretry = 4
findtime = 10m
bantime = 6h

如果担心误封,可以先设置短一点的 bantime,观察几天日志,再逐步调整。

生产环境建议#

Fail2Ban 很实用,但不要把它当成唯一安全措施。比较稳妥的组合是:

  • SSH 使用密钥登录。
  • 禁止 root 远程密码登录。
  • 只开放必要端口。
  • 安全组或防火墙限制管理入口来源。
  • Fail2Ban 自动封禁爆破来源。
  • 对业务登录接口做限流、验证码或二次验证。
  • 定期查看日志和封禁统计。

Fail2Ban 更像是“自动响应层”。它能降低重复攻击的噪音,也能减少暴力破解带来的风险,但前提是日志可靠、规则准确、防火墙 action 生效。

总结#

Fail2Ban 的工作方式并不复杂:读日志、匹配失败、累计次数、执行封禁。真正需要注意的是规则是否匹配真实日志、封禁动作是否适配当前系统、防误封策略是否合理。

日常使用时,最常用的命令可以记住这几条:

sudo systemctl enable --now fail2ban
sudo fail2ban-client status
sudo fail2ban-client status sshd
sudo fail2ban-client set sshd unbanip 203.0.113.10
sudo journalctl -u fail2ban -e

掌握这些之后,Fail2Ban 就可以作为服务器基础安全配置里很稳定的一环。