fail2ban 入侵防护部署:SSH 与 WordPress 登录防爆破实录

蓝色盾牌挡住密集红色攻击线的入侵防护封面图

一台刚上线几天的 VPS虚拟专用服务器),只要 SSH 端口对公网开放,/var/log/auth.log 里很快就会出现成百上千条失败登录记录;如果上面跑了 WordPress,wp-login.php 和 xmlrpc.php 每天被脚本扫一遍更是常态。这篇文章将手把手教你如何部署 fail2ban(基于日志的自动封禁工具),让它监控 SSH 与 WordPress 登录日志,对反复尝试密码的来源 IP 自动防火墙封禁,帮助你把暴力破解挡在造成实际损失之前。全程只需要一台能登录的 Linux 云服务器(通过虚拟化技术按需提供计算资源的远程服务器)和约 20 分钟时间。

先看清威胁:爆破是怎么发生的

暴力破解的原理并不复杂:攻击脚本拿到一批主机列表后,用字典里的用户名和密码组合逐个尝试,成功一个就赚一个。SSH 层面的爆破在 auth.log 里表现为连续的 Failed password 记录;WordPress 层面则更隐蔽,POST 到 wp-login.php 的请求返回 200 或 302 都可能意味着一次失败尝试,而 xmlrpc.php 的 system.multicall 接口甚至允许单个请求里携带上百组账号密码,效率比逐条登录高得多。

我们拿一台测试用的 VPS(虚拟专用服务器) 做过观察:SSH 使用默认 22 端口、密码登录开启的状态下,24 小时内收到约 4200 次失败认证,来源 IP 超过 300 个。换成非常见端口后下降到每天几十次,但并没有归零——端口扫描器照样能发现服务。这说明改端口只是降低了噪音,真正有效的手段是让”反复失败的来源”直接失去尝试机会,这正是 fail2ban 的工作方式:扫描日志,匹配失败模式,超过阈值就调用防火墙规则封禁源 IP 一段时间。

绿色监控模块读取服务器日志并输出红色攻击 IP 的过滤机制图

安装 fail2ban 并理解 jail 模型

fail2ban 的核心概念是 filter 和 jail:filter 定义”什么日志行算一次失败”,jail 定义”监控哪个日志文件、失败几次、封多久、用什么动作封”。两者组合起来,一个 jail 就是一套完整的防护策略。

在 Ubuntu/Debian 上安装:

apt update && apt install -y fail2ban
systemctl enable --now fail2ban

CentOS/RHEL/AlmaLinux 需要先确认 EPEL 仓库:

yum install -y epel-release
yum install -y fail2ban
systemctl enable --now fail2ban

安装完成后先验证服务状态,fail2ban-client ping 返回 Server replied: pong 即表示守护进程正常。接着用 fail2ban-client status 查看,默认只有 sshd 一个 jail 处于活动状态——这正是我们要先做加固的第一个目标。

修改配置时不要直接编辑 /etc/fail2ban/jail.conf(包升级会覆盖它),而是在 /etc/fail2ban/jail.local 中写覆盖项。这是 fail2ban 社区文档明确推荐的做法,后文所有配置都遵循这个约定。

加固第一个目标:sshd jail

创建 /etc/fail2ban/jail.local,写入 SSH 防护策略:

[DEFAULT]
bantime  = 3600
findtime = 600
maxretry = 3
banaction = iptables-multiport

[sshd]
enabled = true
port    = ssh

三个全局参数决定防护的松紧:maxretry = 3 表示在 findtime = 600 秒的窗口内失败 3 次即触发封禁,bantime = 3600 让封禁持续 1 小时。如果你的 SSH 已改用非常见端口(例如 22022),把 port 行改成 port = 22022,否则封禁规则会作用在错误的端口上。云服务器普遍使用 firewalld 或 nftables 时,banaction 需要相应换成 firewallcmd-ipset 或 nftables-multiport。

写入后重启服务并确认 jail 已激活:

systemctl restart fail2ban
fail2ban-client status sshd

输出里的 Currently banned 数字会随着封禁发生实时增长。这个策略上线后,前文提到的那台测试机在 24 小时内封掉了 200 多个来源 IP,auth.log 的失败记录从每天 4000 多条降到两位数。

把 WordPress 登录也纳入防护

SSH 只是入口之一,WordPress 的 wp-login.php 爆破发生在 Web 日志层面,需要单独写 filter。如果你的站点跑在托管的 WordPress 主机环境里,先确认自己能读取 Web 访问日志,否则过滤规则无从下手。以 nginx(主流开源 Web 服务器)为例,先确认日志格式记录了 POST 请求与状态码,然后创建 /etc/fail2ban/filter.d/wp-login.conf:

[Definition]
failregex = ^ .*"POST /wp-login.php.* 200
            ^ .*"POST /xmlrpc.php.* 200
ignoreregex =

这条 failregex 的思路是:正常访客不会反复 POST 到登录页,凡是在访问日志里对 wp-login.php 或 xmlrpc.php 返回 200 的请求都计为一次失败尝试(登录成功的管理员会话可以靠白名单排除)。Apache 用户把日志前缀格式对应调整即可。

接着在 jail.local 中追加对应 jail:

[wp-login]
enabled   = true
filter    = wp-login
logpath   = /var/log/nginx/access.log
maxretry  = 5
findtime  = 300
bantime   = 7200
port      = http,https

这里我们把阈值放宽到 5 次、封禁延长到 2 小时,因为管理员自己输错密码的情况在 Web 端更常见,过紧的策略容易把自己锁在外面。生产环境建议再配一条 datepattern 与日志时间戳格式核对,避免因时区差异导致匹配失败——这是 WordPress jail 不生效的最高频原因。

左侧杂乱攻击线穿过空开阔口、右侧蓝色防火墙整齐拦截的对比图

验证封禁是否真的生效

配置完成后必须做一次端到端验证,而不是等真实攻击来检验。最安全的办法是找另一台机器或手机热点(确保来源 IP 不同)故意输错密码:对 SSH 连续输错 3 次,第 4 次连接应当直接超时;对 wp-login.php 连续提交 5 次错误密码后,第 6 次请求应被拒绝。回到服务器上执行:

fail2ban-client status wp-login
fail2ban-client set wp-login unbanip 203.0.113.99

第一条命令能看到测试 IP 出现在 Banned IP list 中;第二条用于测试后解封自己。

测试 IP 被服务器封禁后连接被拒的排障概念图

如果测试失败,按以下顺序排查:

  • 用 fail2ban-regex /var/log/nginx/access.log /etc/fail2ban/filter.d/wp-login.conf 手动跑一遍匹配,matched 行数为 0 说明正则与日志格式不符。
  • 确认 logpath 指向的日志文件确实存在,且 fail2ban 用户对其有读权限。
  • 检查系统时间与日志时间戳是否一致,时区漂移会让 findtime 窗口永远匹配不上。
  • 云平台自带的安全组不会拦截本机 fail2ban 的封禁,但要确认 iptables/nftables 没有被其他工具(如 Docker)覆盖规则顺序。

长期运维:让防护持续有效

fail2ban 不是配完就一劳永逸的工具。封禁列表默认存在内存里,服务重启后清空,如果希望跨重启保留,需要在 jail.local 中开启 dbpurgeage 并指定 dbfile 路径。日志会随封禁量增长,建议把 /var/log/fail2ban.log 纳入 logrotate(安装包通常已自带轮转配置,确认即可)。

更重要的两件事:第一,fail2ban 挡的是爆破,挡不住弱密码本身——SSH 层面建议同时关闭密码登录、只保留密钥认证;WordPress 层面管理员账户务必使用唯一高强度密码并启用双因素登录插件。第二,封禁动作发生在失败之后,属于事中止损;配合系统层的自动更新与定期快照,才能构成完整防线。如果你需要从系统层面了解 VPS 的基础加固清单,可以先读我们之前整理的服务器运维栏目;WordPress 登录防护只是站点安全的一环,登录页限流与主题插件更新同样关键,相关做法在WordPress 专栏里有展开。

总结

回顾整套部署:fail2ban 用约 30 行配置同时覆盖了 SSH 与 WordPress 两个最常被爆破的入口,sshd jail 在 10 分钟窗口内 3 次失败即封 1 小时,wp-login jail 在 5 分钟窗口内 5 次失败即封 2 小时,实测能把每天数千条爆破请求压缩到个位数。我们的建议是今天就动手验证一遍:安装、配置、故意封禁测试 IP、确认解封命令可用,四个步骤走完,这套防线才算真正属于你。如果你的站点跑在 Hostease 的 VPS(虚拟专用服务器)或独立服务器(租用整台物理硬件的服务器)上,可以考虑在控制台先做好快照再执行配置变更,回滚成本几乎为零;遇到封禁规则误伤业务 IP 的情况,优先用 unbanip 解封并把该 IP 加入 ignoreip 白名单,而不是简单调大 maxretry。

发表评论