给我的小服务器装上 Fail2ban:SSH 防爆破 + 网站防扫描

服务器:阿里云轻量 139.196.31.78(Alibaba Cloud Linux 3 + 1Panel + Docker/openresty)
时间:2026-09-15 凌晨,全程实测通过

一、为什么装

服务器目前没有任何防火墙,22 端口开着密码登录裸奔。虽然日志里暂时只有零星爆破尝试(218.81.98.8 尝试 ubuntu 用户),但公网 22 端口被扫只是时间问题。Fail2ban 的思路很简单:谁犯错,就封谁的 IP——比防火墙白名单更"自动",也更适合我这种懒人运维。

二、装了什么、怎么配的

安装

dnf install -y fail2ban    # EPEL 源,1.0.2

配置(/etc/fail2ban/jail.local)

EPEL 的包默认把封禁动作指向 firewalld,而这台机器没启用 firewalld,直接用会静默失效——这是第一个必须覆盖的坑。在 jail.local(优先级高于 jail.d/*.conf)里统一改用 iptables-ipset-proto6:用 ipset 承载黑名单,iptables 里只留一条规则,几千个恶意 IP 也不拖慢网络。

两个 jail:

Jail监控触发条件封禁
sshd/var/log/secure10 分钟内密码错 3 次4h 起,累犯 ×2 递增,最长 2 周
openresty-scan5 个站点的 access.log10 分钟内命中 5 次恶意路径且返回 403/4041h 起,同样递增

自定义 filter(filter.d/openresty-scan.conf)只抓明确的恶意探测特征:.env、.git、.svn、.ssh、.DS_Store、wp-login.php、phpmyadmin、actuator、druid 等。两个刻意的设计:

  1. 不含 xmlrpc——博客是 Typecho,我平时走 XML-RPC 发文章,万一某次请求失败返回 404 会把自己封了;
  2. 不含宽泛的 admin——Typecho 后台是 /admin/,不能误伤。

白名单:回环 + 全部内网网段,永不封禁。封禁记录存 SQLite(/var/lib/fail2ban/fail2ban.sqlite3),服务重启后自动恢复。

踩的最大一个坑:filter 正则"看起来对但永不匹配"

第一版自定义 filter 跑测试样本 8 行命中 0 行,而正则用 Python 单测却能命中。排查了好几轮(还一度怀疑 fail2ban 的方括号转义有问题),最后逐字符二分定位,真相特别朴素:

nginx 日志里协议版本后面紧跟闭合引号,中间没有空格:"GET /.env HTTP/1.1" 404。

我正则里写成了 HTTP/[\d.]+ \"(多了一个空格),所以永不匹配。改成 HTTP/[\d.]+\" 后,8 行测试样本精准命中 5 行恶意、0 行误报。

教训:规则类配置必须用真实样本实测匹配率,"看着对"和"真的对"是两回事。

三、实测验证

  1. fail2ban-client -t 语法自检通过
  2. 手动 ban 一个测试 IP → ipset list 里出现、iptables -L INPUT 立即插入 REJECT ... match-set f2b-sshd src,解封后同步清空
  3. systemctl restart fail2ban → 封禁记录与防火墙规则全部自动恢复
  4. 上线即抓到真家伙:leakix 的扫描器 46.101.1.225 在 01:19 连探 /.DS_Store、/.env、/.git/config、/actuator/env、/.vscode/sftp.json 共 5 条,全被精准命中——已手动封禁。零误报。

顺带确认了一个关键架构点:1Panel 的 openresty 容器是 host 网络模式,网站流量直接走宿主机 INPUT 链,所以 iptables 封禁对网站同样有效。如果哪天改成 bridge 模式,就需要把 action 挂到 DOCKER-USER 链了。

四、日常使用速查

# 看有哪些 jail 在跑
fail2ban-client status

# 看 SSH / 网站分别封了谁
fail2ban-client status sshd
fail2ban-client status openresty-scan

# 实时拦截日志(最直观)
tail -f /var/log/fail2ban.log

# 内核层确认
iptables -L INPUT -n
ipset list f2b-sshd
ipset list f2b-openresty-scan

# 手动封 / 解封
fail2ban-client set sshd banip 1.2.3.4
fail2ban-client set sshd unbanip 1.2.3.4
fail2ban-client unban --all        # 全部解封(慎用)

改完配置记得 systemctl restart fail2ban。

五、几句要紧的提醒

  1. Fail2ban ≠ 防火墙。它只封"犯过事"的 IP,不做端口管控。更根本的防线是在阿里云控制台的安全组里只放行 22/80/443/管理端口。
  2. 别在 1Panel 里启用自带的防火墙功能,它会清掉 fail2ban 写入的 iptables 规则;真要启用,启用后执行 systemctl restart fail2ban 重建。
  3. 万一哪天把自己 SSH 封了:阿里云控制台 VNC 登录,执行 fail2ban-client unban --all。
  4. 下一步可以考虑:SSH 改非标端口 + 只留密钥登录关掉密码认证——那是比 fail2ban 更彻底的解法。