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