fail2ban —— 暴力破解自动封禁

运维工具手册 11-安全与审计 5.4 KB 原文

fail2ban —— 暴力破解自动封禁

分类:11-安全与审计 适用:Linux(fail2ban 0.11 / 1.x),基于日志 + 防火墙 一句话:盯着日志里的失败登录,超过阈值就自动封 IP,SSH 暴露公网的第一道防线。

1. 它解决什么问题

  • SSH 每天被扫几千次,日志刷屏。
  • 想在 Nginx、MySQL、Postfix 被爆破时自动止血。
  • 手工 iptables 封 IP 太慢,攻击者换 IP 又快。
  • 需要"封一段时间自动解封",避免永久误封。

原理:fail2ban 定时读日志 → 用正则匹配失败记录 → 计数超阈值 → 调用 action(iptables/nftables/firewalld)封禁 → 到期解封。

2. 安装

apt install -y fail2ban
dnf install -y fail2ban
systemctl enable --now fail2ban

版本检查:

fail2ban-client version

3. 配置结构(不要改 jail.conf)

/etc/fail2ban/
├── fail2ban.conf        # 主配置(日志级别等)
├── jail.conf            # 默认 jail 定义(升级会覆盖,别改)
├── jail.local           # 本地覆盖(你该改这里)
├── jail.d/*.conf        # 分片配置
├── filter.d/*.conf      # 日志匹配规则
└── action.d/*.conf      # 封禁动作

/etc/fail2ban/jail.local:

[DEFAULT]
# 白名单,绝不封禁(管理机、内网、监控)
ignoreip = 127.0.0.1/8 ::1 10.0.0.0/8 192.168.0.0/16

bantime  = 1h
findtime = 10m
maxretry = 5

# 递增封禁:重复犯事时间翻倍,最长 1 周
bantime.increment = true
bantime.factor    = 2
bantime.maxtime   = 1w

# 后端:有 systemd journal 用 systemd,否则 auto
backend = systemd

[sshd]
enabled  = true
port     = ssh
filter   = sshd
logpath  = %(sshd_log)s
maxretry = 3
bantime  = 24h

三个核心时间:

参数 含义
findtime 统计窗口(10 分钟内)
maxretry 窗口内失败次数阈值
bantime 封禁时长

4. 常用命令

# 状态
fail2ban-client status
fail2ban-client status sshd

# 手动封禁/解封
fail2ban-client set sshd banip 1.2.3.4
fail2ban-client set sshd unbanip 1.2.3.4

# 重载配置(不重启服务)
fail2ban-client reload
fail2ban-client reload sshd

# 测试正则(写自定义 filter 时必用)
fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf
fail2ban-regex 'Failed password for root from 1.2.3.4 port 22 ssh2' 'sshd'

# 看日志
journalctl -u fail2ban -f
tail -f /var/log/fail2ban.log

fail2ban-client status sshd 输出:

Status for the jail: sshd
|- Filter
|  |- Currently failed: 3
|  |- Total failed:     1245
|  `- File list:        /var/log/auth.log
`- Actions
   |- Currently banned: 12
   |- Total banned:     340
   `- Banned IP list:   1.2.3.4 5.6.7.8 ...

5. 自定义 filter(保护 Nginx / 应用)

/etc/fail2ban/filter.d/nginx-limit.conf:

[Definition]
failregex = ^<HOST> - .* "(GET|POST) .*" 429 
ignoreregex =

/etc/fail2ban/jail.d/nginx.conf:

[nginx-limit]
enabled  = true
filter   = nginx-limit
logpath  = /var/log/nginx/access.log
maxretry = 20
findtime = 1m
bantime  = 1h

验证正则:

fail2ban-regex /var/log/nginx/access.log /etc/fail2ban/filter.d/nginx-limit.conf
# 输出里 Matches 数量 > 0 才说明规则有效

6. 保护其他服务

服务 jail 名 备注
SSH sshd 最常用
Nginx 认证失败 nginx-http-auth basic auth 401
Nginx 恶意扫描 nginx-badbots、nginx-botsearch 内置
MySQL mysqld-auth 需开 log_error_verbosity=3
Postfix postfix 邮件爆破
WordPress wordpress-auth 需插件写日志
通用 recidive 封"屡次被封"的 IP 更久

recidive(对付惯犯):

[recidive]
enabled  = true
logpath  = /var/log/fail2ban.log
bantime  = 1w
findtime = 1d
maxretry = 3

7. 与防火墙的配合

action.d/ 决定怎么封:

action 适用
iptables-multiport iptables(默认)
nftables-multiport nftables(新系统)
firewallcmd-rich-rules firewalld
route 加黑路由(不依赖防火墙)
[sshd]
action = nftables-multiport[name=sshd, port="ssh", protocol=tcp]

查看封禁规则:

iptables -L -n | grep f2b
nft list ruleset | grep -A5 f2b
firewall-cmd --list-rich-rules | grep f2b

8. 实战场景

8.1 SSH 防爆破(最常见)

# /etc/fail2ban/jail.local
[DEFAULT]
ignoreip = 127.0.0.1/8 10.0.0.0/8
bantime  = 1h
findtime = 10m
maxretry = 5
backend  = systemd

[sshd]
enabled = true
maxretry = 3
bantime  = 24h
fail2ban-client reload
fail2ban-client status sshd

8.2 改过 SSH 端口

[sshd]
enabled = true
port    = 2222          # 必须改成实际端口,否则封禁规则端口不对

或 port = ssh,2222 同时支持。

8.3 误封自己,紧急解封

# 从另一台机器或控制台
fail2ban-client set sshd unbanip <你的IP>
# 或
fail2ban-client unban <你的IP>

# 永久白名单
# 在 jail.local 的 ignoreip 里加入你的 IP/网段,然后 reload

上线前务必把管理机 IP 加入 ignoreip。

8.4 封禁后验证

fail2ban-client status sshd | grep 'Banned IP'
iptables -L -n | grep <被封IP>
# 从被封机器测试
ssh -o ConnectTimeout=3 user@host    # 应超时

8.5 容器/云环境注意

  • 容器内 fail2ban 无法操作宿主机 iptables,需在宿主机运行并挂载日志。
  • 云厂商安全组优先级高于本机 iptables,封禁仍有效但看不到安全组层面。
  • K8s 节点上封禁可能影响 Pod 网络,慎用大网段。

9. 常见坑

  1. 改 jail.conf:升级被覆盖,必须写 jail.local 或 jail.d/。
  2. backend=auto 找不到日志:systemd 系统日志不在 /var/log/auth.log,改 backend = systemd。
  3. 改了 SSH 端口没改 jail 的 port:封禁规则端口错,实际没封住。
  4. 把自己封了:ignoreip 没配。上线前先加白名单。
  5. maxretry 太小:正常用户输错 3 次就被封,体验差;SSH 建议 3~5。
  6. 正则不匹配:不同发行版的日志格式不同,用 fail2ban-regex 验证。
  7. 封禁不生效:action 与防火墙不匹配(nftables 系统用了 iptables action)。
  8. bantime 太长:误封影响大,配合 bantime.increment 更合理。
  9. 日志轮转导致读取中断:fail2ban 支持 pyinotify/systemd 后端,用 auto 可能丢行。
  10. IPv6 未覆盖:ignoreip 与 action 都要考虑 ::1。
  11. recidive 依赖 fail2ban.log:日志级别需为 INFO,且轮转后要能读到历史。
  12. 性能:日志量极大时 fail2ban 会吃 CPU,考虑改用 nftables 集合或 WAF。

10. 速查表

systemctl enable --now fail2ban
fail2ban-client status
fail2ban-client status sshd
fail2ban-client set sshd banip <ip>
fail2ban-client set sshd unbanip <ip>
fail2ban-client reload
fail2ban-regex <log> <filter>
journalctl -u fail2ban -f

# 配置
/etc/fail2ban/jail.local        # 改这里
[DEFAULT]
ignoreip = 127.0.0.1/8 10.0.0.0/8
bantime = 1h
findtime = 10m
maxretry = 5
backend = systemd
[sshd]
enabled = true
port = ssh
maxretry = 3
bantime = 24h

# 验证封禁
iptables -L -n | grep f2b
nft list ruleset | grep f2b