只要把一台 Linux 服务器暴露到公网,/var/log/auth.log 里很快就会出现成片的 SSH 暴力破解尝试:陌生 IP 拿着字典不停地猜用户名和密码。Fail2ban 就是为解决这个问题而生的入侵防御工具——它实时扫描日志,发现短时间内失败次数超过阈值的 IP,便自动调用 iptables / nftables 临时封禁。本文从安装到自定义规则,手把手教你用 Fail2ban 守住 SSH 与 Web 服务的入口,把自动化攻击挡在门外。
一、Fail2ban 工作原理
Fail2ban 不是防火墙本身,而是一个”日志驱动的封禁调度器”。它由三个核心概念组成:filter(过滤器)负责从日志里用正则提取出”这是一次失败登录”;action(动作)负责真正执行封禁(默认调用 iptables 加一条 DROP 规则);jail(监狱)把某个 filter 和某个 action 绑定到具体的日志路径与封禁策略上。三者组合,就构成了一条”发现异常 → 触发动作”的闭环。
1.1 监控与过滤
Fail2ban 默认每隔一秒轮询被监控的日志文件(也支持 systemd 的 journal 后端)。filter 用一组正则匹配”失败”行,例如 SSH 的 Failed password for。匹配上的源 IP 会被记入一个内部计数表,当单位时间内的失败次数达到 maxretry 阈值,就触发 ban。
1.2 封禁与解封
封禁动作默认通过 iptables 在 filter 表加一条针对该 IP 的 REJECT / DROP 规则,bantime 到期后自动移除。这种设计的好处是”临时惩罚”——既挡住暴力破解,又不会把误操作的自己永久关在门外。
二、安装与基础配置
绝大多数发行版仓库都自带 Fail2ban,一条命令即可安装。以 Debian / Ubuntu 为例:
# Ubuntu / Debian
sudo apt-get update && sudo apt-get install -y fail2ban
# CentOS / Rocky(需先启用 EPEL)
sudo dnf install -y epel-release && sudo dnf install -y fail2ban
# 启动并设置开机自启
sudo systemctl enable --now fail2ban
Fail2ban 的主配置在 /etc/fail2ban/jail.conf,但千万不要直接改它——升级时会被覆盖。正确做法是在 /etc/fail2ban/jail.d/ 下新建 *.local 文件,它会以”覆盖”的方式合并进主配置。最基础的一层保护,先确保 SSH 监狱被打开:
# /etc/fail2ban/jail.d/sshd.local
[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 5
bantime = 1h
findtime = 10m
上面这段的含义是:在 10 分钟(findtime)内 SSH 登录失败 5 次(maxretry),就封禁该 IP 1 小时(bantime)。改完配置后重载:sudo fail2ban-client reload。
三、加固 SSH 防护
在开启 Fail2ban 之前,建议先把 SSH 的基础安全做扎实:禁用 root 直登、改用密钥登录、修改默认 22 端口。Fail2ban 是第二道防线,而不是替代措施。若你用的是 Nginx 反向代理 统一入口,也别忘了后端 SSH 的防护。
当服务器把 SSH 端口改成了 2222,需要告诉 jail 正确的端口,否则 Fail2ban 会去封一个没人连的 22 端口:
# /etc/fail2ban/jail.d/sshd.local
[sshd]
enabled = true
port = 2222
filter = sshd
logpath = /var/log/auth.log
backend = systemd
maxretry = 4
bantime = 24h
findtime = 15m
这里把 backend 设为 systemd,让 Fail2ban 直接从 journald 读日志,避免某些系统日志路径不一致导致漏判。把 bantime 拉长到 24 小时,能显著抬高攻击者”换 IP 再来”的成本。
四、保护 Web 服务(Nginx)
SSH 之外,Web 服务同样会被扫:有人专门用脚本遍历 /wp-admin、/.env、/phpmyadmin 等敏感路径。我们可以为 Nginx 单独配一个 jail,把高频 404 / 403 的 IP 封掉。先写一个自定义 filter:
# /etc/fail2ban/filter.d/nginx-botsearch.conf
[Definition]
failregex = ^<HOST> -.*"(GET|POST).*(\.env|/wp-admin|/wp-login|/phpmyadmin|/admin).*" (404|403)
ignoreregex =
再为它开一个 jail,监控 Nginx 的访问日志:
# /etc/fail2ban/jail.d/nginx.local
[nginx-botsearch]
enabled = true
port = http,https
filter = nginx-botsearch
logpath = /var/log/nginx/access.log
maxretry = 10
bantime = 12h
findtime = 10m
如果你是配合 Nginx 负载均衡 在 multiple upstream 后面跑业务,记得每个节点都部署同样的 Fail2ban 规则,或者把访问日志集中后再统一判断,避免攻击者在节点之间轮换绕过。
五、日常运维与排查
fail2ban-client 是与运行中的 Fail2ban 交互的命令行工具,掌握下面几条就能应付 90% 的运维场景:
# 查看所有已启用的 jail
sudo fail2ban-client status
# 查看某个 jail 的封禁详情(被封 IP、总数)
sudo fail2ban-client status sshd
# 手动解封一个 IP(误封自救)
sudo fail2ban-client set sshd unbanip 203.0.113.45
# 手动封禁一个 IP(应急)
sudo fail2ban-client set sshd banip 198.51.100.23
# 重新加载配置
sudo fail2ban-client reload
如果某天你把自己关在门外(比如公司动态 IP 被 ban),最稳妥的”自救”方式是:通过云控制台的串行控制台(VNC / Serial Console)登录,再用上面的 unbanip 解封,而不是反复重试 SSH 把失败次数越刷越高。
六、生产环境注意事项
在容器化和云原生环境里,Fail2ban 的部署有几个要点需要留意:
| 场景 | 建议做法 | 原因 |
|---|---|---|
| Docker 容器内的服务 | Fail2ban 跑在宿主机,监控宿主机的 docker 日志或挂载出来的日志 | 容器网络是 NAT,iptables 在容器内封禁对外部无效 |
| 云服务器(阿里云 / 腾讯云) | Fail2ban 与云安全组 / 云防火墙配合,关键端口同时做白名单 | 云防火墙优先级更高,能挡掉大流量扫描 |
| Kubernetes 节点 | 用 DaemonSet 在每个节点部署,或直接用 Cilium / eBPF 方案 | Pod IP 漂移,传统 iptables 封禁难追踪 |
| 高并发 Web | filter 正则要精准,避免把正常爬虫 / CDN 回源误判 | 误封会直接影响线上可用性 |
在 Docker 部署的场景下,最常见的问题是”Fail2ban 装在容器里却封不住攻击”——因为容器用的是 bridge 网络,它加的 iptables 规则只作用在容器命名空间内。正确姿势是把 Fail2ban 放在宿主机,监控宿主机上 /var/lib/docker/containers/*/ 的 JSON 日志,或干脆让业务容器把日志 log-driver 打到宿主机的 syslog。
七、常见坑与排障
| 现象 | 根因 | 解决办法 |
|---|---|---|
| 改了 SSH 端口但没改 jail 的 port | Fail2ban 仍监控 22,攻击打在 2222 上不触发 | jail 里把 port = 2222 写对 |
| status 显示 jail 未启用 | .local 文件名或路径错,未合并进配置 | 确认放在 /etc/fail2ban/jail.d/ 且以 .local 结尾,再 reload |
| systemd 后端报 unable to read journal | fail2ban 用户无 journal 读取权限 | 把 fail2ban 加入 systemd-journal 组 |
| 封禁了 CDN / 正常爬虫 | filter 正则过宽 | 收紧正则或在 jail 里加 ignoreip 白名单 |
| 重启后规则丢失 | 用了非持久化 action 或 iptables 被重置 | 确认 action 用默认的 iptables-multiport,并启用开机自启 |
最后提醒一句:Fail2ban 是”缓解”而非”根治”。它和 服务器安全加固、密钥登录、最小权限是同一套防御体系的不同层次。把这几层叠加起来,一台暴露在公网的服务器才能既方便自己登录,又让自动化攻击无功而返。若你关注更内核层的可观测性,也可以结合 eBPF 做更细粒度的异常连接追踪。
八、recidive 监狱:专门收拾屡教不改的 IP
普通 jail 在 bantime 到期后会自动解封,但有一类攻击者”解封即再来”,循环刷屏,短期封禁几乎没威慑力。Fail2ban 自带一个名为 recidive 的监狱专门对付这种顽固行为:它监控 Fail2ban 自己的日志,把”被 ban 过多次又反复出现”的 IP 拉黑更长时间,甚至按周计。启用方式很简单,新建一个 jail 即可:
# /etc/fail2ban/jail.d/recidive.local
[recidive]
enabled = true
logpath = /var/log/fail2ban.log
bantime = 1w
findtime = 1d
maxretry = 5
backend = auto
与此同时,务必在全局 [DEFAULT] 段加 ignoreip,把你自己的办公出口 IP、跳板机、内网段全部加白,避免误封后把自己锁在门外——这是生产环境踩过最多的坑之一:
# /etc/fail2ban/jail.d/default.local
[DEFAULT]
ignoreip = 127.0.0.1/8 10.0.0.0/8 203.0.113.0/24
bantime = 1h
maxretry = 5
九、封禁后发通知:把攻击事件接进监控
自动封禁只是第一步,你还需要知道”此刻谁在打我”。Fail2ban 的 action 机制允许在 ban / unban 时触发自定义命令,最常见的做法是在默认动作之后追加一段 curl,把事件打到飞书、钉钉或 Slack 的 Webhook。示例如下:
# 在 jail 的 action 末尾追加通知
action = %(action_)s
%(action_mwl)s
curl -s -X POST "https://oapi.dingtalk.com/robot/send?access_token=XXX" \
-H 'Content-Type: application/json' \
-d '{"msgtype":"text","text":{"content":"Fail2ban banned on "}}'
把封禁事件接进你的告警链路后,再配合 Prometheus 之类的指标采集,就能把”被动封禁”升级为”主动感知”——当某个时间段 ban 数量陡增时,你能第一时间收到通知,在攻击规模化之前介入。至此,从 SSH 到 Web、从单机到集群、从封禁到告警,一套可落地的 Fail2ban 入侵防御体系就搭建完成了。



