firewalld的动态黑名单功能看似简单,但在实际配置中容易踩坑,比如临时规则无法持久化、批量封禁效率低下、规则写入后未生效等问题。这些问题在线上环境中都会带来困扰。以下是几个关键要点:使用rich rule配置黑名单时必须添加--permanent参数,并执行--reload,否则重载后规则会丢失;reject和drop各有适用场景,调试时建议用reject,生产环境静默丢弃则用drop;批量封禁大量IP时,应避免循环调用firewall-cmd,改用--direct直接写入iptables规则,性能更稳定。

firewalld使用rich rule配置动态IP黑名单时必须加--permanent
许多人在测试时习惯使用--add-rich-rule而不加--permanent,虽然当时规则生效,但一旦执行firewall-cmd --reload,所有规则都会消失,往往会误以为是脚本配置错误。实际上,临时规则无法承受重载操作,将其用于黑名单管理等于徒劳。
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="203.0.113.44" reject'
sudo firewall-cmd --reload
reject会向客户端返回明确的拒绝响应(如Connection refused),在调试阶段非常有用;生产环境中若希望静默丢弃,则应使用drop- 对同一IP重复添加规则不会报错,但也不会产生叠加效果——firewalld不允许重复规则,第二次添加时会提示
ERROR: timeout或直接静默忽略 - 删除规则时,必须严格匹配字符串,包括引号和空格,使用
--remove-rich-rule时source address与reject的顺序不能颠倒
黑名单批量更新时避免逐条调用firewall-cmd
在shell循环中逐条调用firewall-cmd,每条规则都会触发一次D-Bus通信和规则链重编译——当处理100个IP时,卡顿或超时几乎不可避免。线上环境必须采用合并操作的方式。
合理的做法是:先收集所有需要封禁的IP,生成临时规则文件,再通过--direct参数批量注入内核链:
sudo firewall-cmd --direct --add-rule ipv4 filter INPUT 0 -s 203.0.113.44 -j REJECT
sudo firewall-cmd --direct --add-rule ipv4 filter INPUT 0 -s 192.168.5.101 -j REJECT
--direct参数可以绕过firewalld的抽象层直接写入iptables,性能高且无额外延迟,非常适合高频封禁场景- 规则插入位置的数字(例如
0)用于控制优先级,数字越小优先级越高,确保黑名单规则先于白名单生效 - 上述规则默认不持久化,重启firewalld或系统后会丢失,需要配合
firewall-cmd --runtime-to-permanent或手动保存到/etc/firewalld/direct.xml文件
同步任务中必须校验规则是否真实生效
许多自动化脚本仅以"命令返回0"作为成功标志,但实际规则可能并未进入防火墙链。常见错误原因包括:zone绑定到错误的网卡、rich rule被其他规则覆盖、firewalld服务未启动。
验证时不能仅依赖firewall-cmd --list-all,还需检查底层链:
- 首先确认当前活跃的zone:执行
firewall-cmd --get-active-zones,确保目标IP位于你操作的zone(例如public)中 - 检查rich rule是否已写入:
firewall-cmd --zone=public --list-rich-rules - 查看底层iptables是否命中:
sudo iptables -L INPUT -n -v | grep 203.0.113.44 - 最简单的模拟测试是从被封IP的机器上运行
curl -v https://your-server:80,预期会看到Connection refused或直接超时
CentOS 7上持久化黑名单需特别注意服务状态
CentOS 7.9已于2024年6月停止维护,但仍有许多旧系统在使用。该系统默认启用firewalld,但某些镜像预装了iptables-services并设置为开机自启,导致firewalld被屏蔽,使得向firewalld添加的规则无法生效。
检查与修复步骤如下:
- 确认当前实际运行的服务:执行
systemctl is-active firewalld和systemctl is-active iptables,仅允许一个为active (running) - 如果
iptables正在运行,停止并禁用:sudo systemctl stop iptables && sudo systemctl disable iptables - 启用firewalld:
sudo systemctl enable --now firewalld - 验证:
firewall-cmd --state应输出running,且iptables -L的输出中不应出现手动添加的DROP规则
规则持久化本身并不复杂,但旧系统中服务冲突的现象比预想更为普遍。如果不先仔细检查就编写脚本,相当于往空配置里填充数据——徒劳无功。
