侧边栏壁纸
  • 累计撰写 123 篇文章
  • 累计收到 2 条评论

服务器日志里全是暴力破解记录?我用 fail2ban 配了一套自动封禁,踩了几个坑说清楚

2026-5-25 / 0 评论 / 8 阅读
🤖AI摘要
本文介绍了如何使用fail2ban工具自动封禁SSH暴力破解攻击。作者通过实际案例阐述了fail2ban的安装、配置和优化过程,包括自定义jail.local文件、设置递增封禁时间等,并分享了在配置过程中遇到的坑和解决方案。文章强调了fail2ban在自动化防御暴力破解方面的优势,以及如何通过合理配置提高安全性。

前阵子我一台测试服务器放着没怎么管,某天上去看了一眼 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=5findtime=10mbantime=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"

常见的问题就那几个:

  1. 日志路径不对:用 backend = systemd 或者确认 logpath 指向正确的文件
  2. 正则匹配不到:用 fail2ban-regex 测试
  3. iptables 规则冲突:检查 iptables -L -n 里有没有和 fail2ban 冲突的 ACCEPT 规则
  4. 时间不同步: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 一起用才完整。

评论一下?

OωO
取消