前阵子我一台测试服务器放着没怎么管,某天上去看了一眼 auth.log,直接傻眼——SSH 的暴力破解记录每分钟几十条,IP 来自五湖四海。虽说密码设得够复杂没被攻破,但这刷屏看着就烦,而且万一哪天有个弱口令的账号被加上去,后果不堪设想。
之前我写过 iptables 手动配防火墙规则的文章,但那套方案有个硬伤:你得提前知道哪些 IP 段要封,而且规则写死了不会变。暴力破解的 IP 每天都在换,手动维护一份黑名单根本跟不上节奏。后来我上了 fail2ban,才算真正把这事给自动化了。
fail2ban 到底是干什么的
简单说,fail2ban 就是个日志分析 + 自动封禁的工具。它盯着你指定的日志文件(比如 SSH 的 /var/log/auth.log),用正则匹配失败登录的模式,达到阈值就自动调 iptables 把那个 IP 给封了。过一段时间还能自动解封,不用你手动去删规则。
装起来很简单:
# Debian/Ubuntu
apt install fail2ban -y
# CentOS/RHEL
yum install epel-release -y
yum install fail2ban -y
# 启动并设为开机自启
systemctl enable --now fail2ban
装完先别急着改配置,先看看默认行为是否已经在工作:
fail2ban-client status
默认会启用 sshd jail,如果你的 SSH 端口是标准的 22,装完就有一定保护了。但默认参数太宽松——maxretry=5、findtime=10m、bantime=10m,攻击者换个 IP 等十分钟就能继续试。所以自定义配置是必须的。
自定义 jail.local
fail2ban 的配置分三层:jail.conf(默认)→ jail.d/*.conf(覆盖)→ jail.local(最高优先级)。永远不要直接改 jail.conf,系统更新会覆盖你的改动。正确做法是复制一份:
cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
然后编辑 jail.local。我最开始改的配置长这样:
[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 3
banaction = iptables-multiport
backend = systemd
[sshd]
enabled = true
port = 22
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
这里有几个细节值得说一下。
backend = systemd 这个我踩了个坑。默认值是 auto,fail2ban 会自动选择 backend。在较新的 Debian/Ubuntu 上,日志可能不走 /var/log/auth.log 而是直接进 systemd journal。如果你设了 logpath 但日志实际在 journal 里,fail2ban 会启动成功但封不到任何人——因为它在读一个空的或不存在的文件。backend = systemd 让它直接读 journal,比 logpath 更可靠。
banaction = iptables-multiport 比默认的 iptables 好,因为它可以同时封多个端口,而且生成的规则更干净。
改完重启:
systemctl restart fail2ban
fail2ban-client status sshd
进阶:封得更久,封得更狠
默认的 bantime = 3600(1 小时)对于那些持续扫描的 IP 来说太短了。我后来换成了递增封禁:
[DEFAULT]
bantime = 1h
bantime.increment = true
bantime.factor = 2
bantime.maxtime = 30d
这样第一次封 1 小时,第二次封 2 小时,第三次 4 小时……最多封 30 天。那些反复来试探的 IP 会被越封越久,基本等于永久拉黑。
但这里有个坑:bantime.increment 的数据存在内存里,fail2ban 重启就丢了。如果你经常重启服务或者服务器重启,递增效果会归零。解决办法是配一个持久化数据库:
[DEFAULT]
dbfile = /var/lib/fail2ban/fail2ban.sqlite3
dbpurgeage = 30d
这样封禁记录会写到 SQLite 文件里,重启也不丢。
不只防 SSH:nginx 和其他服务
fail2ban 自带的 filter 远不止 SSH。我的 nginx 也配了两个 jail:
[nginx-http-auth]
enabled = true
port = http,https
filter = nginx-http-auth
logpath = /var/log/nginx/error.log
maxretry = 3
[nginx-botsearch]
enabled = true
port = http,https
filter = nginx-botsearch
logpath = /var/log/nginx/access.log
maxretry = 4
nginx-http-auth 封那些尝试暴力破解你 Basic Auth 的 IP。nginx-botsearch 封那些疯狂扫你后台路径的爬虫——比如 /admin、/wp-login.php、/.env 这种。
如果你跑的是自定义应用,还可以自己写 filter。格式很简单:
# /etc/fail2ban/filter.d/myapp.conf
[Definition]
failregex = ^<HOST> - .* "POST /api/login.* 401
ignoreregex =
<HOST> 是 fail2ban 的占位符,会自动匹配 IP 地址。把正则写到 failregex 里,然后在 jail.local 里引用:
[myapp]
enabled = true
port = http,https
filter = myapp
logpath = /var/log/nginx/access.log
maxretry = 5
findtime = 60
写完之后用 fail2ban-regex 命令测试正则是否能匹配到日志:
fail2ban-regex /var/log/nginx/access.log /etc/fail2ban/filter.d/myapp.conf
这一步千万别省。我有一次写错了正则,结果 fail2ban 启动了但什么都没封,过了两周才发现。
解封和排查
偶尔会有正常用户被封的情况(比如自己输错密码太多次)。手动解封:
# 查看当前被封的 IP
fail2ban-client status sshd
# 解封指定 IP
fail2ban-client set sshd unbanip 1.2.3.4
如果你发现 fail2ban 没有在工作,先看日志:
journalctl -u fail2ban --since "1 hour ago"
常见的问题就那几个:
- 日志路径不对:用
backend = systemd或者确认logpath指向正确的文件 - 正则匹配不到:用
fail2ban-regex测试 - iptables 规则冲突:检查
iptables -L -n里有没有和 fail2ban 冲突的 ACCEPT 规则 - 时间不同步:fail2ban 依赖日志时间戳,服务器时间不准会导致
findtime窗口错位
配合白名单使用
别把自己也封了。在 [DEFAULT] 段加上你常用的 IP:
[DEFAULT]
ignoreip = 127.0.0.1/8 ::1 10.0.0.0/8 你的固定IP/32
如果你有跳板机或者 VPN,把跳板机的出口 IP 也加进去。我曾经因为忘了加白名单,在公司测试的时候把自己封了,最后得用手机热点 SSH 上去解封。
现在的配置全貌
整理一下我目前在用的完整 jail.local 核心段:
[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 3
banaction = iptables-multiport
backend = systemd
bantime.increment = true
bantime.factor = 2
bantime.maxtime = 30d
dbfile = /var/lib/fail2ban/fail2ban.sqlite3
dbpurgeage = 30d
ignoreip = 127.0.0.1/8 ::1
[sshd]
enabled = true
port = 22
filter = sshd
maxretry = 3
[nginx-http-auth]
enabled = true
port = http,https
filter = nginx-http-auth
logpath = /var/log/nginx/error.log
[nginx-botsearch]
enabled = true
port = http,https
filter = nginx-botsearch
logpath = /var/log/nginx/access.log
maxretry = 4
跑了两个月,SSH 暴力破解的日志从每天几千条降到了几十条——不是攻击者放弃了,是大部分来源 IP 都被自动封了。偶尔有新 IP 来试几下,触发阈值后也很快被拉黑。
fail2ban 不是万能的,它本质是被动防御——等人来了再封。但对于中小规模的服务器来说,这种自动化的被动防御已经能挡住 90% 的扫描和暴力破解了。剩下的 10%,该上密钥登录上密钥登录,该改端口改端口,配合 iptables 一起用才完整。
评论一下?