在RHEL以及CentOS、Rocky Linux等衍生发行版中,防火墙管理基本都离不开firewalld。大部分人对它的印象停留在zone区域和service服务这两个层面:把网卡丢进某个zone,再往zone里加几个service,防火墙配置就算完成了。但这种粗粒度的控制在真实生产环境里经常捉襟见肘。比如你只想让运维网段的机器访问SSH,其他来源一律拒绝;或者想在拦截某个可疑IP的同时记录一条日志方便审计,这些需求靠service都做不到,必须使用rich rule富规则。富规则是firewalld提供的一种结构化策略语言,可以把匹配条件、日志动作和最终处理动作组合成一条完整的规则,相当于iptables时代手写规则的友好版本。

rich rule的语法结构拆解
一条富规则由若干个子句按固定顺序拼接而成,整体结构可以概括为:rule [family=ipv4|ipv6] [source ...] [destination ...] [service|port|protocol|icmp-block|forward-port ...] [log|audit ...] [accept|drop|reject|mark]。前面的部分都是匹配条件,最后一个是动作,firewalld按从左到右的顺序解析。理解这个骨架之后,剩下的就是记住每个子句能接哪些参数。
family子句指定规则适用的协议族,取值只能是ipv4或ipv6,规则里出现了IP地址就必须写family,否则firewalld无法判断该按v4还是v6匹配。source和destination分别定义源地址和目标地址,支持两种写法:address=192.168.10.0/24表示一个网段,mac=00:11:22:33:44:55表示MAC地址,还可以加invert="yes"做取反,也就是排除这个来源。
端口层面的匹配有几种方式:service name="ssh"直接引用预定义服务,port port=8080 protocol="tcp"指定单个端口,protocol value="icmp"匹配协议类型。log子句可选,加上之后命中规则的数据包会写入系统日志,还能用level指定日志级别,用prefix加前缀方便grep。最后的动作是四选一:accept允许通过,drop静默丢弃,reject拒绝并回送错误报文,mark打标记。drop和reject的区别在于前者不回应、对方会等到超时,后者立即收到拒绝响应,防扫描场景一般用drop。
常用配置命令与持久化操作
富规则通过firewall-cmd命令管理,最核心的参数是--add-rich-rule。完整规则要用双引号包起来,防止shell把空格拆散。比如允许192.168.10.0/24网段访问SSH,命令如下:
firewall-cmd --permanent --zone=public --add-rich-rule='rule family="ipv4" source address="192.168.10.0/24" service name="ssh" accept' firewall-cmd --reload
注意这里用了--permanent写入永久配置,改完必须reload才能生效。如果省略--permanent,规则只进入运行时内存,重启或reload后就消失,适合临时测试。反过来,如果先加了运行时规则觉得没问题,可以用--permanent配合同样的规则再写一次,或者直接执行迁移命令把运行时配置整体转为永久配置。
删除规则把add换成remove即可,规则内容必须和添加时完全一致,多一个空格都可能匹配失败,这是新手最常踩的坑。查看当前zone已有的富规则用查询命令:
firewall-cmd --zone=public --list-rich-rules firewall-cmd --permanent --zone=public --remove-rich-rule='rule family="ipv4" source address="192.168.10.0/24" service name="ssh" accept'
还有一种做法是把规则写进zone的XML配置文件,路径在/etc/firewalld/zones/目录下,在<zone>标签内添加<rule>节点,reload后同样生效。这种方式适合用Ansible等工具做批量下发,版本管理也更方便。写XML时注意source、port等属性都要放到对应子节点里,动作accept作为<rule>的子元素放在最后。
实战案例:精细访问控制
第一个案例是端口白名单加黑名单的组合。需求是内部网段可以访问3306端口的MySQL,但其中一台IP为192.168.10.66的机器曾被入侵,要明确拒绝它。可以分两条规则配置,黑名单写在前面利用优先级生效:
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.10.66" port port="3306" protocol="tcp" reject' firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.10.0/24" port port="3306" protocol="tcp" accept' firewall-cmd --reload
firewalld内部基于iptables或nftables实现,规则顺序很重要,更精确的匹配应该放在前面。上面的例子中如果两条规则顺序颠倒,.66那台机器的请求会先命中accept规则,黑名单形同虚设。虽然firewalld对rich rule的排序有自己的处理逻辑,但养成把拒绝规则放前面的习惯能避免很多莫名其妙的问题。
第二个案例是日志与限速。排查攻击时往往需要知道谁在撞防火墙,富规则的log子句配合limit可以做到记录日志同时控制日志量:
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="0.0.0.0/0" port port="22" protocol="tcp" log prefix="SSH-DENY " level="warning" limit value="3/m" drop' firewall-cmd --reload
这条规则对所有访问22端口的流量做处理:每分钟最多记录3条前缀为SSH-DENY的警告日志,超出部分不记录但仍然丢弃。limit的写法中m表示分钟,还可以用s表示秒、h表示小时,比如value="10/s"表示每秒10次。limit子句除了用在log上,也能直接和accept搭配实现简单的限速放行,比如每秒只允许某个IP建立50个新连接,一定程度上缓解SYN Flood。
排错技巧与注意事项
配置富规则后不生效是最常见的问题,排查思路分三步。第一步确认reload是否执行过,用firewall-cmd --list-all对比运行时和永久配置;第二步检查语法,rich rule对引号很敏感,source地址的子网掩码长度要合法,family漏写会直接报错;第三步看实际生成的iptables或nftables规则,执行firewall-cmd --zone=public --list-rich-rules确认规则已挂载,再配合journalctl -k查看内核日志里的丢包记录。
另外有几点容易被忽略。富规则与zone的默认动作有关,如果zone本身是ACCEPT策略,富规则的drop才有意义;如果是PUBLIC默认的针对性放行模式,就要确认对应的service没有单独开放。MAC地址匹配只对同一广播域的流量有效,跨路由的场景用不上。IPv6环境记得family写成ipv6并且地址格式要用冒号分隔。把这些细节掌握之后,rich rule基本能覆盖日常绝大多数精细化防火墙需求,复杂度再往上的场景才需要考虑直接操作nftables或交给安全设备处理。
firewalld富规则RHEL防火墙配置rich rule语法修改时间:2026-09-03 21:37:15