CentOS 服务器面临的不是“会不会被扫描”的问题,而是“每分钟被扫描多少次”的问题。公网 IP 暴露的第一分钟起,SSH 的 22 端口就会持续收到来自全球自动化僵尸网络的暴力破解尝试。单纯依靠 SSH 密钥认证或改端口号,只能解决“是否被破解”的问题,却无法解决资源消耗和日志污染的问题。Fail2ban 的本质是一个日志解析引擎,它能实时监控日志文件,在匹配到恶意行为模式后,执行预设动作。但很多人止步于 Fail2ban 自身的 iptables 屏蔽,忽略了防火墙前置规则与 Fail2ban 的联动深度优化,导致封禁效率低下,甚至在 DDOS 级别的分布式暴力破解面前形同虚设。
Fail2ban 默认封禁机制的致命缺陷默认安装的 Fail2ban 使用 iptables 在 INPUT 链的尾部动态插入拒绝规则。这种后置处理模式意味着一个恶意 IP 的数据包仍然需要完整穿过内核协议栈,经过路由决策、连接跟踪、以及一系列 RAW、MANGLE、NAT 表的规则检查后,才在 FILTER 表的 INPUT 链末端被丢弃。每一次被拦截的恶意请求,依然消耗了宝贵的 CPU 周期和连接跟踪表条目。当攻击者使用数千个肉鸡 IP 发起低频慢速爆破时,Fail2ban 虽然能逐一封禁,但连接跟踪表(conntrack)早已被这些无效连接占满,导致正常业务无法建立新连接。
构建防火墙前置黑名单的联动架构正确的做法是将 Fail2ban 的封禁动作从“丢弃数据包”升级为“拒绝连接跟踪”。这需要利用 iptables 的 raw 表或 nftables 的 ingress 钩子,在连接跟踪建立之前就将恶意 IP 的数据包丢弃。具体实现上,我们需要创建一个专用的黑名单链,并将其挂在 PREROUTING 或 raw 表的 PREROUTING 链上。这样恶意流量在内核收到数据包的第一时间就被剥离,根本不进入连接跟踪模块。在 CentOS 7 环境下,如果使用 firewalld 作为前端管理工具,则需要利用 firewalld 的 direct 规则或 rich rules 来配合 Fail2ban 的动作配置,避免两者互相覆盖规则。
CentOS 7 下 Fail2ban 与 firewalld 的深度整合在 CentOS 7 中,firewalld 是默认防火墙管理工具,而 Fail2ban 默认也支持 firewalld 动作。但直接使用默认的 firewallcmd-new 动作会在 firewalld 的区域中添加富规则,这些规则依然在较高层级处理。为了达到前置阻断效果,我们需要修改 Fail2ban 的动作文件。首先在 /etc/fail2ban/action.d/ 目录下创建自定义动作文件 firewallcmd-raw.conf,内容核心是利用 firewall-cmd 的 direct 模式添加规则到 raw 表。具体配置如下:
[Definition]
actionstart = firewall-cmd --direct --add-chain ipv4 raw fail2ban-raw
firewall-cmd --direct --add-rule ipv4 raw PREROUTING 0 -p tcp --dport 22 -j fail2ban-raw
firewall-cmd --direct --add-rule ipv4 raw fail2ban-raw 0 -j RETURN
actionstop = firewall-cmd --direct --remove-chain ipv4 raw fail2ban-raw
actioncheck = firewall-cmd --direct --query-chain ipv4 raw fail2ban-raw
actionban = firewall-cmd --direct --add-rule ipv4 raw fail2ban-raw 0 -s -j DROP
actionunban = firewall-cmd --direct --remove-rule ipv4 raw fail2ban-raw 0 -s -j DROP
这段配置创建了一个独立的 raw 表链 fail2ban-raw,并将其挂载到 PREROUTING 链的最前端。封禁动作直接在该链中针对源 IP 执行 DROP,数据包在进入连接跟踪前就被丢弃。需要注意的是,raw 表规则在重启 firewalld 后会丢失,因此需要在 Fail2ban 的 actionstart 中完成初始化。同时,默认的 RETURN 规则确保正常流量不被误伤。
针对分布式暴力破解的 ipset 联动策略当单个 IP 的封禁无法应对数千个分布式节点时,就需要引入 ipset 来提升匹配效率。iptables 的线性规则匹配在数百条规则时性能尚可,但 Fail2ban 积累的封禁 IP 可能达到数千甚至上万个,此时每次数据包到来都要逐条比对,CPU 开销巨大。ipset 使用哈希表存储 IP 地址,查找时间复杂度为 O(1),即使存储数万个 IP 也几乎不影响匹配速度。在 CentOS 7 中,需要安装 ipset 服务,并配置 Fail2ban 使用 ipset 类型的动作。创建动作文件 firewallcmd-ipset.conf:
[Definition]
actionstart = ipset create fail2ban-blacklist hash:ip timeout 86400
firewall-cmd --direct --add-rule ipv4 raw PREROUTING 0 -p tcp --dport 22 -m set --match-set fail2ban-blacklist src -j DROP
actionstop = firewall-cmd --direct --remove-rule ipv4 raw PREROUTING 0 -p tcp --dport 22 -m set --match-set fail2ban-blacklist src -j DROP
ipset destroy fail2ban-blacklist
actionban = ipset add fail2ban-blacklist timeout
actionunban = ipset del fail2ban-blacklist
这里的关键在于 ipset 的 timeout 参数,它允许为每个封禁 IP 设置独立的过期时间,与 Fail2ban 的 bantime 保持一致。当封禁时间到期后,IP 自动从 ipset 中移除,无需 Fail2ban 主动执行解封操作,大幅减少了 Fail2ban 的维护负担。raw 表中的单条规则配合 ipset 匹配,无论黑名单中有多少 IP,数据包匹配效率始终恒定。
多端口与服务联动的扩展设计SSH 只是入口之一,面向公网的 CentOS 服务器通常还运行着 Web 服务、邮件服务、FTP 服务等。每个服务都有对应的 Fail2ban 过滤器,如 nginx-http-auth、postfix、proftpd 等。如果为每个服务单独创建 raw 表规则和 ipset 集合,会导致规则碎片化。更优的方案是建立一个统一的 ipset 黑名单集合,所有服务的 Fail2ban 动作都向同一个 ipset 添加 IP,然后在 raw 表中用一条规则对所有端口的流量进行匹配拦截。具体实现时,需要修改各个 jail 的 action 配置,统一指向同一个 ipset 名称。同时,在 raw 表规则中去掉端口限制,对所有进入流量都进行黑名单匹配。这样做的好处是,一旦某个 IP 因为攻击 SSH 被封禁,它也无法访问 Web 服务,实现了全端口联动封禁。
日志分析与误封预防机制Fail2ban 的默认配置容易产生误封,尤其是在企业内网环境中,如果某个员工多次输错密码,可能导致整个办公网络出口 IP 被封禁。为了避免这种情况,需要在 jail.local 中精细调整 maxretry 和 findtime 参数。对于 SSH 服务,建议将 maxretry 设置为 5,findtime 设置为 600 秒,这样只有在 10 分钟内失败 5 次才会触发封禁。同时,必须配置 ignoreip 白名单,将企业办公出口 IP、VPN 接入 IP 段、以及内部管理网段加入白名单。在 CentOS 7 中,ignoreip 支持 CIDR 格式,可以写成 192.168.0.0/16 或 10.0.0.0/8。此外,建议在 Fail2ban 的 actionban 中增加日志记录功能,将每一次封禁操作的详细信息写入专用日志文件,便于事后审计和误封排查。
持久化与故障恢复的闭环设计服务器重启后,Fail2ban 默认会丢失所有封禁记录,恶意 IP 可以立即重新发起攻击。虽然 ipset 可以配合 ipset 的 save 和 restore 机制来持久化黑名单,但更稳妥的做法是在 Fail2ban 的 actionstart 中调用恢复脚本,在 actionstop 中调用保存脚本。在 /etc/fail2ban/action.d/firewallcmd-ipset.conf 中增加以下逻辑:actionstart 时检查是否存在 /etc/fail2ban/persistent/blacklist.save 文件,如果存在则用 ipset restore 命令恢复;actionstop 时用 ipset save 命令将当前黑名单保存到该文件。同时,需要配置 systemd 服务单元,确保 fail2ban 服务在 firewalld 和 ipset 服务之后启动,避免依赖关系错乱。在 /etc/systemd/system/fail2ban.service.d/override.conf 中添加 After=firewalld.service ipset.service 和 Wants=ipset.service,保证启动顺序正确。
性能监控与规则验证联动规则部署完成后,必须验证其实际效果。使用命令 firewall-cmd --direct --get-all-rules 可以查看 raw 表规则是否正确加载。使用 ipset list fail2ban-blacklist 可以查看当前黑名单中的 IP 数量和具体条目。通过 watch -n 1 'ipset list fail2ban-blacklist | grep "Number of entries"' 可以实时监控黑名单增长速度。更重要的是,需要监控 conntrack 表的使用情况,使用 cat /proc/sys/net/netfilter/nf_conntrack_count 查看当前连接跟踪条目数,与 /proc/sys/net/netfilter/nf_conntrack_max 对比,确保连接跟踪表不会被恶意连接占满。如果发现 conntrack 使用率仍然偏高,说明 raw 表规则可能没有生效,需要检查规则插入位置是否在连接跟踪之前。
从 Fail2ban 到自适应防护的演进路径Fail2ban 与防火墙的联动本质上是一种基于规则的静态响应机制,它在应对已知攻击模式时效果显著,但无法识别未知的慢速攻击或协议级别的漏洞利用。更高级的防护需要在 Fail2ban 的基础上叠加异常检测。例如,可以通过分析 netstat 的输出统计每个 IP 的并发连接数,当某个 IP 的 ESTABLISHED 状态连接数超过阈值时,即使没有触发 Fail2ban 的失败计数,也通过自定义脚本将其加入 ipset 黑名单。这种基于行为特征的动态封禁,可以弥补 Fail2ban 单纯依赖日志模式匹配的不足。在 CentOS 7 环境下,可以编写一个简单的 Python 脚本,周期性读取 /proc/net/nf_conntrack 文件,解析其中的源 IP 并进行计数,将超过阈值的 IP 直接调用 ipset add 命令加入黑名单,实现 Fail2ban 与自定义行为分析引擎的协同工作。
