在CentOS服务器上,fail2ban配合firewalld实现联动封禁,核心原理就是fail2ban检测到暴力破解、异常登录等行为后,自动调用firewall-cmd命令将攻击IP加入富规则(rich rule)进行临时或永久封禁。这套组合比单独使用iptables更灵活、更易管理,因为firewalld是CentOS 7/8/9默认的防火墙管理工具,支持动态规则而不需要重启服务。具体操作分三步走:安装配置fail2ban、编写对应的jail规则、设置action脚本让fail2ban调用firewalld接口。下面我把每一步的细节、踩坑点和优化方案全部讲透。
一、为什么要用fail2ban+firewalld而不是其他方案
很多人第一反应是用iptables直接写规则,或者用denyhosts之类的工具。但fail2ban的优势在于它能实时分析日志文件,自动识别异常模式,不需要你手动写复杂的正则。而firewalld作为CentOS原生防火墙,支持zone概念、rich rule动态添加、永久规则和临时规则分离,管理起来比直接操作iptables链清晰得多。两者联动的好处是:封禁是动态的,到时间自动解除;规则持久化在firewalld里,重启不丢失;而且你可以随时用firewall-cmd --list-all查看当前被封的IP,运维非常直观。
需要注意的是,CentOS 7默认用firewalld,CentOS 8/9也是。如果你的系统还在用CentOS 6,那是iptables+ipchains的时代,这套方案不适用。另外,如果你同时装了ufw或者直接关了firewalld用iptables,那联动就没意义了,先统一用firewalld。
二、安装和基础配置fail2ban
先装fail2ban,CentOS 7用yum,CentOS 8/9用dnf:
yum install -y fail2ban
或者:
dnf install -y fail2ban
装完之后启动并设为开机自启:
systemctl enable fail2ban systemctl start fail2ban
fail2ban的主配置文件在/etc/fail2ban/jail.conf,但官方强烈建议不要直接改这个文件,而是复制一份到jail.local,所有自定义都写在jail.local里。这样以后升级fail2ban不会覆盖你的配置。
cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
打开jail.local,先看默认的[DEFAULT]段,几个关键参数要理解:
[DEFAULT] bantime = 10m # 封禁时长,默认10分钟 findtime = 10m # 检测时间窗口,默认10分钟内 maxretry = 5 # 最多尝试次数,超过就封 ignoreip = 127.0.0.1/8 # 忽略的IP,别把自己封了
bantime建议根据业务调整,SSH暴力破解一般设1小时到24小时都行。findtime和maxretry配合使用,意思是"在10分钟内失败5次就封"。如果你的服务器访问量大、正常用户偶尔输错密码,可以把maxretry调高到8或10,避免误封。
三、编写firewalld联动的action脚本
fail2ban自带了一些action,但默认没有firewalld的。你需要自己写一个。在/etc/fail2ban/action.d/目录下创建一个文件,比如firewallcmd-ipset.conf:
[Definition]
actionstart =
actionstop =
actioncheck =
actionban = firewall-cmd --add-rich-rule='rule family="ipv4" source address="" drop' --permanent
firewall-cmd --reload
actionunban = firewall-cmd --remove-rich-rule='rule family="ipv4" source address="" drop' --permanent
firewall-cmd --reload
[Init]
name = firewallcmd-ipset这里有几个关键点要说清楚。第一,--permanent表示规则写入永久配置,重启firewalld不会丢。第二,--reload是让规则立即生效,不需要重启整个防火墙服务。第三,actionban里的<ip>会被fail2ban自动替换成实际的攻击IP。第四,如果你想用ipset而不是rich rule(ipset性能更好,适合封禁大量IP),可以改用:
actionban = ipset add fail2ban-ssh-exist
firewall-cmd --add-rich-rule='rule family="ipv4" source ipset="fail2ban-ssh drop" --permanent
firewall-cmd --reload用ipset的好处是批量管理,一条rich rule引用一个ipset,里面可以塞几百上千个IP,而不是每个IP一条规则。对于高频率攻击场景,强烈推荐ipset方案。
创建完action文件后,还要创建对应的ipset(如果用ipset方案):
ipset create fail2ban-ssh hash:ip timeout 86400 ipset save fail2ban-ssh > /etc/sysconfig/ipset/fail2ban-ssh systemctl enable ipset systemctl start ipset
timeout 86400表示ipset里的条目24小时自动过期,相当于自动解封。这样即使fail2ban出问题没unban,ipset也会自己清理。
四、配置jail规则启用联动
回到jail.local,添加SSH的jail配置:
[sshd] enabled = true port = ssh filter = sshd logpath = /var/log/secure maxretry = 5 bantime = 3600 action = firewallcmd-ipset
如果你用的是ipset方案,action就写上面创建的action名称。如果你想同时封禁多个服务,比如SSH和HTTP,可以分别建jail:
[sshd] enabled = true port = ssh filter = sshd logpath = /var/log/secure maxretry = 5 bantime = 7200 action = firewallcmd-ipset [http-auth] enabled = true port = http,https filter = apache-auth logpath = /var/log/httpd/*error.log maxretry = 3 bantime = 1800 action = firewallcmd-ipset
注意logpath要根据你实际的日志路径来写。CentOS 8/9如果用的是nginx,日志路径是/var/log/nginx/error.log。filter用apache-auth或者nginx-auth都行,fail2ban自带了这些filter的正则定义。
五、测试验证和常见问题排查
配置完之后重启fail2ban:
systemctl restart fail2ban
然后用fail2ban-client查看状态:
fail2ban-client status sshd
你会看到当前被封的IP列表、封禁时间等信息。想手动测试,可以从另一台机器故意输错SSH密码5次,然后回来看fail2ban有没有记录:
fail2ban-client status sshd
同时用firewall-cmd验证规则是否真的加进去了:
firewall-cmd --list-all firewall-cmd --list-rich-rules
常见问题有这么几个。第一,fail2ban启动报错说找不到action,检查action文件路径是不是在/etc/fail2ban/action.d/下面,文件名后缀必须是.conf。第二,firewall-cmd报权限错误,确保fail2ban是以root运行的,它默认就是root。第三,封禁了但攻击还能进来,检查你的jail里port写的对不对,SSH默认是22,如果你改了端口要对应改。第四,规则加了但没生效,确认你用了--permanent加--reload,光--permanent不加--reload的话规则要重启firewalld才生效。
六、进阶优化和安全建议
第一,建议把fail2ban的日志级别调高,方便排查问题。在jail.local的[DEFAULT]段加:
loglevel = INFO
第二,配合/etc/hosts.deny做双重保险。虽然fail2ban已经在防火墙层封了,但hosts.deny是TCP Wrappers层的,多一层没坏处。在/etc/hosts.deny里加:
sshd: ALL
然后在/etc/hosts.allow里放你信任的IP。不过现在很多服务默认不编译TCP Wrappers支持了,这一步看情况。
第三,如果你的服务器有公网IP且暴露了SSH端口,强烈建议同时改SSH默认端口,把22改成一个不常用的高位端口,比如22222或者38923。这不是必须的,但能大幅减少自动化扫描的命中概率。改完之后记得在jail里的port字段同步修改,在firewalld里也开放新端口。
第四,定期检查fail2ban的统计报告。fail2ban自带一个fail2ban-client status命令可以看每个jail的统计,你也可以装fail2ban-server的邮件通知功能,在jail.local里配置destemail和sender,被封的时候发邮件提醒你。对于个人服务器,这个功能很实用。
第五,关于解封策略。如果你发现某个IP被误封了(比如你自己的动态IP),可以手动解封:
fail2ban-client set sshd unbanip 1.2.3.4
或者直接用firewall-cmd删规则:
firewall-cmd --remove-rich-rule='rule family="ipv4" source address="1.2.3.4" drop' --permanent firewall-cmd --reload
第六,对于高并发攻击场景(比如DDoS的SYN flood),fail2ban其实不太适合,因为它是基于应用层日志分析的,对网络层洪水反应太慢。这种情况需要在firewalld里直接配SYN proxy或者用更底层的防护手段。fail2ban最擅长的还是应对SSH暴力破解、Web登录暴力破解、邮件爆破这类应用层攻击。
七、完整配置示例汇总
最后给一个可以直接用的完整配置参考。jail.local关键部分:
[DEFAULT] bantime = 1h findtime = 10m maxretry = 5 ignoreip = 127.0.0.1/8 192.168.1.0/24 backend = systemd [sshd] enabled = true port = ssh filter = sshd logpath = /var/log/secure maxretry = 5 bantime = 4h action = firewallcmd-ipset [sshd-ddos] enabled = true port = ssh filter = sshd logpath = /var/log/secure maxretry = 10 bantime = 24h action = firewallcmd-ipset
这里我故意建了两个sshd jail,一个正常封禁5次,一个针对更激进的攻击设10次才封但封24小时。你可以根据实际需求调整。action.d里的firewallcmd-ipset.conf前面已经给了,直接用就行。
总结一下,fail2ban+firewalld联动封禁这套方案,部署简单、维护方便、效果立竿见影。核心就是写好action脚本让fail2ban能调用firewall-cmd,然后在jail里指定这个action。CentOS 7/8/9都能用,比iptables方案优雅得多。花半小时配好,你的服务器安全等级直接上一个台阶。
