SSH暴力破解不是会不会发生的问题,而是每天发生多少次的问题。任何一台暴露在公网的Debian服务器,只要开了22端口,日志里很快就会塞满来自世界各地的失败登录记录。这些攻击大多由自动化僵尸网络驱动,使用常见用户名和密码字典进行穷举尝试。Fail2ban是目前应对这类威胁最直接、最轻量的防御方案,它通过监控日志文件,匹配预设的失败模式,当某个IP的失败次数达到阈值时,自动调用iptables或nftables将该IP临时或永久封禁。
理解Fail2ban的防御逻辑Fail2ban本质上是一个日志解析引擎和防火墙策略管理器的结合体。它不替代你的防火墙,而是动态地向防火墙中注入规则。它的工作流程很简单:后台守护进程持续读取指定的日志文件,用正则表达式匹配每一行新产生的内容。如果匹配到了“认证失败”的模式,就会给来源IP记一分。当这个IP在设定的时间窗口内累积的分数达到阈值,Fail2ban就执行预设的动作——最常见的是调用iptables在INPUT链中插入一条DROP规则,或者使用nftables添加一条拒绝规则。封禁时间到期后,Fail2ban会自动移除这条规则,IP恢复访问权限。这个机制的精妙之处在于,它只对表现出恶意行为的IP进行干预,正常用户即使偶尔输错密码,只要不超过阈值就不会被误封。
在Debian上安装和基础配置Debian 12的官方仓库已经包含了Fail2ban,安装过程非常直接。先更新软件包列表,然后安装fail2ban主程序。如果你计划使用nftables而非传统的iptables,建议同时安装nftables包,因为Debian 12默认使用nftables作为防火墙后端。
sudo apt update sudo apt install fail2ban nftables -y
安装完成后,Fail2ban服务会自动启动。但此时它使用的是默认配置,只启用了sshd这一个jail,而且封禁策略比较保守。我们需要基于默认配置进行定制,而不是直接修改主配置文件。Fail2ban的设计哲学是“复制并覆盖”:主配置文件在/etc/fail2ban/jail.conf,这个文件在升级时可能被覆盖,所以正确的做法是创建一个jail.local文件,把需要覆盖的配置项放进去。Fail2ban读取配置的优先级是:jail.local > jail.conf > jail.d/目录下的文件。
先复制默认配置为local版本,然后编辑它:
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local sudo nano /etc/fail2ban/jail.local
在jail.local中,我们主要关注几个关键参数。bantime是封禁时长,默认600秒(10分钟),对于SSH暴力破解来说偏短。攻击者通常使用慢速扫描,10分钟后解封,他们又会卷土重来。建议将bantime设置为3600秒(1小时)或更长,比如86400秒(24小时)。findtime是统计失败次数的观察窗口,默认600秒。maxretry是触发封禁的失败次数阈值,默认5次。这三个参数的组合决定了防御的灵敏度:在findtime窗口内,同一IP失败达到maxretry次,就封禁bantime秒。一个更严格的配置可以是:findtime设为600秒,maxretry设为3次,bantime设为86400秒。这样任何IP在10分钟内输错3次密码就会被封一整天。
[DEFAULT] bantime = 86400 findtime = 600 maxretry = 3 banaction = nftables-multiport banaction_allports = nftables-allports [sshd] enabled = true port = ssh logpath = /var/log/auth.log backend = systemd
这里有一个重要细节:banaction的选择。Debian 12默认防火墙后端是nftables,所以banaction应该设置为nftables-multiport。如果你还在使用iptables,则保持iptables-multiport。可以通过检查系统防火墙状态来确认:sudo nft list ruleset 如果有输出,说明nftables正在使用。backend参数指定了日志来源,Debian 12使用systemd journal,所以设置为systemd可以让Fail2ban直接从journal读取日志,而不必依赖传统的syslog文件。如果你同时保留了rsyslog,也可以继续使用logpath指向/var/log/auth.log,但systemd方式更加原生和高效。
自定义过滤规则应对高级攻击模式默认的sshd过滤器位于/etc/fail2ban/filter.d/sshd.conf,它已经覆盖了绝大多数常见的SSH认证失败场景,包括密码错误、密钥认证失败、用户名无效等。但攻击者也在进化,有些慢速暴力破解会刻意将尝试间隔拉长到数分钟,以躲过默认的findtime窗口。还有一些攻击会使用大量分布式IP,每个IP只尝试一两次,传统的基于单IP失败计数的策略对此效果有限。
针对慢速攻击,我们可以将findtime拉长到1800秒甚至3600秒,同时保持maxretry不变或略微降低。这样即使攻击间隔很长,只要在半小时或一小时内累计失败达到阈值,依然会被封禁。编辑jail.local中的sshd段落:
[sshd] enabled = true port = ssh logpath = /var/log/auth.log backend = systemd maxretry = 3 findtime = 1800 bantime = 86400
对于分布式低速攻击,单靠Fail2ban本身确实有局限性,但我们可以结合其他手段。一个有效的策略是启用Fail2ban的recidive jail,这是一个专门用于识别累犯的过滤器。当一个IP在较长时间内被多次封禁,recidive jail会将其识别为惯犯,并施加更长的封禁期。在jail.local中添加:
[recidive] enabled = true logpath = /var/log/fail2ban.log banaction = nftables-allports bantime = 604800 findtime = 86400 maxretry = 5
这个配置的含义是:如果一个IP在一天内被Fail2ban封禁了5次,就将其判定为累犯,直接封禁一周,而且封禁范围扩大到所有端口。这对于那些反复更换攻击目标的僵尸节点非常有效。
创建针对SSH的强化过滤规则默认的sshd过滤器虽然全面,但有时我们需要更精细的控制。比如,有些攻击会尝试使用不存在的用户名,这种尝试几乎不可能是合法用户的误操作。我们可以在/etc/fail2ban/filter.d/目录下创建一个自定义过滤器,专门捕获“无效用户”的登录尝试,并给予更低的容忍阈值。
创建文件/etc/fail2ban/filter.d/sshd-invaliduser.conf:
[Definition] failregex = ^%(__prefix_line)s[iI](?:nvalid user|llelgal user) .* from(?: port \d+)?\s*$ ignoreregex =
然后在jail.local中新增一个jail,使用这个过滤器,并设置更严格的阈值:
[sshd-invaliduser] enabled = true port = ssh logpath = /var/log/auth.log backend = systemd filter = sshd-invaliduser maxretry = 1 findtime = 600 bantime = 86400 banaction = nftables-multiport
这个规则的意思是:只要有一个IP尝试使用不存在的用户名登录,立即封禁24小时。合法用户知道自己用什么用户名,几乎不可能触发这个规则。这种“零容忍”策略对无效用户名扫描的威慑力极强。
将SSH端口修改与非标准配置结合Fail2ban默认监控的是ssh服务的标准端口,也就是22端口。但很多管理员会选择将SSH端口修改为其他数值,比如2222或更高位的随机端口,以减少自动化扫描的噪音。如果你修改了SSH端口,Fail2ban的sshd jail需要同步更新port参数。在jail.local的sshd段落中,将port = ssh改为port = 2222或者你实际使用的端口号。这里的“ssh”实际上是/etc/services中定义的22端口别名,直接写数字更明确。
更深一层的防御是结合端口敲门技术,但那是另一个话题。在Fail2ban的语境下,即使你改了端口,只要日志中记录了认证失败,Fail2ban就能正常工作。关键是确保logpath和backend配置正确,让Fail2ban能读到日志。如果你使用systemd backend,通常不需要手动指定logpath,Fail2ban会通过journalctl获取日志。但显式指定logpath作为fallback是更稳妥的做法。
白名单和忽略规则的正确设置封禁策略再严格,也不能把自己锁在门外。Fail2ban提供了ignoreip参数,用于设置永远不会被封禁的IP地址或网段。这个配置通常放在[DEFAULT]段落中,对所有jail生效。典型设置包括本地回环地址、内网管理网段,以及你常用的固定公网IP。如果有多个IP需要白名单,用空格分隔。
[DEFAULT] ignoreip = 127.0.0.1/8 ::1 192.168.1.0/24 你的固定公网IP
对于没有固定IP的管理员,可以考虑使用ignoreip的DNS解析功能,但要注意这会在每次检查时产生DNS查询,可能影响性能。更可靠的做法是设置一个较短的bantime,或者在手机等移动设备上配置密钥认证,确保即使被误封也能很快自动解封。另外,如果服务器前面有反向代理或负载均衡,需要确保Fail2ban看到的是真实客户端IP,而不是代理的IP。这通常需要配置X-Forwarded-For头解析,但在SSH场景下一般不涉及,因为SSH是直接TCP连接。
验证和监控Fail2ban运行状态配置完成后,重启Fail2ban服务使设置生效:
sudo systemctl restart fail2ban
验证各jail是否正常启动:
sudo fail2ban-client status
查看特定jail的详细状态,包括当前封禁的IP列表:
sudo fail2ban-client status sshd
输出会显示当前sshd jail的过滤器、动作、以及被封禁的IP数量和列表。这是日常巡检最常用的命令。如果想查看更详细的日志,Fail2ban的主日志位于/var/log/fail2ban.log,里面记录了每一次封禁和解封操作的时间、IP和原因。定期检查这个日志可以帮助你了解攻击态势,也可以作为调整阈值的依据。
另一个实用的监控手段是查看auth.log中最近被拒绝的尝试,配合grep和awk进行简单统计:
sudo grep "Failed password" /var/log/auth.log | awk '{print $(NF-3)}' | sort | uniq -c | sort -nr | head -20
这条命令会列出最近尝试失败密码最多的20个IP,让你直观地看到哪些IP在疯狂扫描。如果这些IP已经在Fail2ban的封禁列表中,说明防御生效了。如果发现大量失败尝试来自同一个IP且未被封禁,就需要检查Fail2ban配置是否正确,或者阈值是否设得太高。
Fail2ban与nftables的深度整合Debian 12默认使用nftables替代iptables,Fail2ban对此有良好支持。当banaction设置为nftables-multiport时,Fail2ban会在nftables中创建一个名为f2b-sshd的表(具体名称取决于jail名),并在其中添加封禁规则。你可以通过以下命令查看nftables中Fail2ban创建的规则:
sudo nft list ruleset | grep -A 10 f2b
与iptables不同,nftables的规则管理更加结构化,Fail2ban创建的规则不会污染主规则集,而是在独立的表中管理,删除和添加都更加干净。如果你之前使用iptables,迁移到nftables后,需要确保jail.local中的banaction全部更新为nftables相关动作,否则Fail2ban会尝试调用iptables命令而失败。
进阶:结合系统日志的持久化分析Fail2ban的封禁是临时的,服务重启后所有封禁记录都会丢失。对于需要长期跟踪恶意IP的场景,可以考虑将Fail2ban的封禁日志导入到独立的分析系统,或者使用fail2ban的持久化方案。一个轻量级的做法是配置Fail2ban在封禁和解封时执行自定义脚本,将IP记录到文件中。在jail.local的action段落中可以定义actionban和actionunban触发的脚本路径。不过对于大多数中小型部署,这种持久化并非必需,因为僵尸网络的IP池变化很快,封禁历史对未来的参考价值有限。
常见故障排查思路如果Fail2ban安装后没有按预期工作,排查步骤应该从日志开始。首先确认Fail2ban服务是否正常运行:sudo systemctl status fail2ban。如果服务启动失败,journalctl -u fail2ban会给出具体错误信息,通常是配置文件语法错误。其次确认jail是否enabled:在jail.local中,每个jail段落必须有enabled = true,仅靠默认配置中的[sshd]是不够的,因为jail.local会覆盖jail.conf,如果你在jail.local中重新定义了[sshd]但没有写enabled = true,这个jail就会被禁用。再次检查日志路径和backend设置是否匹配:如果backend设为systemd但系统没有使用systemd journal,或者logpath指向的文件不存在,Fail2ban无法读取日志,自然无法触发封禁。最后,使用fail2ban-client set sshd banip命令手动封禁一个测试IP,然后检查nftables或iptables中是否真的出现了对应规则,以此验证动作链是否正常。
SSH暴力破解防御是一个多层体系,Fail2ban是其中性价比最高的一环。它不需要复杂的硬件,不依赖外部服务,部署后几乎不消耗系统资源,却能过滤掉90%以上的自动化攻击噪音。剩下的10%需要靠密钥认证、禁用root密码登录、修改默认端口等措施来应对。将Fail2ban配置到位后,你的auth.log会清净很多,那些曾经刷屏的失败登录记录会大幅减少,剩下的零星尝试也会在几次失败后被自动阻断。这就是用自动化的防御对抗自动化的攻击,让服务器在公网上多一层坚实的护盾。
