CentOS 8及以后的版本(包括RHEL 8、Rocky Linux、AlmaLinux)默认使用nftables作为防火墙后端,iptables命令虽然还能用,但本质上是通过iptables-nft兼容层翻译成nftables规则。如果你想彻底告别iptables的语法,直接用nftables写规则,那核心就一件事:搞懂nftables的"表-链-规则"三层结构,以及它和iptables在语法上的本质差异。iptables是按协议分表(filter、nat、mangle、raw),nftables是按功能分表(ip、ip6、inet、bridge、arp),链也不再叫INPUT/FORWARD/OUTPUT,而是自定义名称。下面我把迁移过程中最常遇到的场景全部拆解一遍。
一、nftables和iptables的核心架构差异
iptables的规则逻辑是"表→链→规则",比如filter表的INPUT链。nftables同样是"表→链→规则",但它把IPv4和IPv6合并到inet表里统一处理,这是最大的变化。你不需要再分别维护iptables和ip6tables两套规则了。nftables的配置文件默认在/etc/nftables.conf,命令行工具是nft,而不是iptables。查看当前所有规则只需要一条命令:
nft list ruleset
输出结果会显示完整的表、链、规则层级,非常直观。如果你之前用iptables-save导出过规则,现在要做的就是把那些规则"翻译"成nftables语法,或者直接重新写一遍。
二、基础规则迁移:从iptables到nftables的语法对照
先看最简单的场景。iptables里允许SSH(22端口)的规则是:
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
换成nftables,你需要先创建一个表和链(如果还没有的话),然后写规则:
nft add table inet filter
nft add chain inet filter input { type filter hook input priority 0 \; }
nft add rule inet filter input tcp dport 22 accept
注意几个关键点:nftables里链要指定type(filter)、hook(input/output/forward/prerouting/postrouting)和priority。hook决定这条链挂在哪个网络钩子上,priority决定同一个hook上多条链的执行顺序。iptables里的INPUT链对应hook input,FORWARD对应hook forward,OUTPUT对应hook output。而nat表的PREROUTING和POSTROUTING在nftables里也是通过hook prerouting和postrouting实现的,但要放在nat表而不是filter表。
三、完整的filter表迁移示例
假设你原来的iptables规则是这样一套:
iptables -P INPUT DROP iptables -P FORWARD DROP iptables -P OUTPUT ACCEPT iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT iptables -A INPUT -i lo -j ACCEPT iptables -A INPUT -p tcp --dport 22 -j ACCEPT iptables -A INPUT -p tcp --dport 80 -j ACCEPT iptables -A INPUT -p tcp --dport 443 -j ACCEPT iptables -A INPUT -p icmp -j ACCEPT
对应的nftables完整配置文件/etc/nftables.conf应该这么写:
#!/usr/sbin/nft -f
flush ruleset
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
ct state established,related accept
iif "lo" accept
tcp dport 22 accept
tcp dport 80 accept
tcp dport 443 accept
ip protocol icmp accept
}
chain forward {
type filter hook forward priority 0; policy drop;
}
chain output {
type filter hook output priority 0; policy accept;
}
}
这里有几个重要变化要理解:第一,iptables的-m state --state在nftables里变成了ct state,ct是conntrack的缩写,nftables原生支持连接跟踪。第二,iptables的-i lo(入接口为lo)在nftables里写成iif "lo",iif是input interface的缩写,对应的出接口是oif。第三,策略默认动作用policy drop/accept直接写在链定义里,不需要单独的-P命令。
四、NAT规则迁移:PREROUTING和POSTROUTING
NAT是运维中最容易出错的部分。iptables的DNAT和SNAT规则在nftables里要放在nat表中,而且语法完全不同。先看SNAT(源地址转换,比如内网机器上网):
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
nftables版本:
table ip nat {
chain postrouting {
type nat hook postrouting priority 100;
oif "eth0" masquerade
}
}
再看DNAT(端口转发,比如把外部8080端口映射到内网机器的80端口):
iptables -t nat -A PREROUTING -p tcp --dport 8080 -j DNAT --to-destination 192.168.1.100:80
nftables版本:
table ip nat {
chain prerouting {
type nat hook prerouting priority 0;
tcp dport 8080 dnat to 192.168.1.100:80
}
}
注意nftables里dnat to后面直接跟IP和端口,不需要--to-destination这种长参数。另外,nftables的nat表只支持ip和ip6(即只处理IPv4或IPv6),如果你想同时处理双栈,就用inet表但要注意inet表的nat链支持有限,通常建议ip和ip6分开建表。
五、使用集合和映射提升规则可维护性
nftables相比iptables有一个巨大优势:支持集合(set)和映射(map)。当你有大量IP需要封禁或放行时,不用写几百条规则,直接用一个集合搞定。比如封禁一批恶意IP:
nft add set inet filter blackhole { type ipv4_addr \; }
nft add element inet filter blackhole { 1.2.3.4, 5.6.7.8, 9.10.11.12 }
nft add rule inet filter input ip saddr @blackhole drop
集合还支持动态更新,不需要重启服务,运行时直接add element或delete element就行。这在iptables里要实现同样的效果,要么用ipset(也是兼容层),要么手动维护大量规则,效率差很多。映射(map)则可以做更复杂的键值对应,比如根据源IP映射到不同的目标地址。
六、从iptables-save批量迁移的实用技巧
如果你的服务器上已经跑着一套iptables规则,不想一条一条手写,可以用iptables-save导出再用工具转换。虽然没有官方的完美转换器,但社区有一些脚本可以做基本转换。更稳妥的方式是:先用iptables-save > /tmp/iptables-rules.txt导出,然后对照着逐条翻译。重点关注这几类规则的转换:
1. 状态匹配:-m state --state → ct state;
2. 接口匹配:-i/--in-interface → iif,-o/--out-interface → oif;
3. 协议匹配:-p tcp/udp/icmp → tcp/udp/icmp(直接写协议名);
4. 端口匹配:--dport/--sport → dport/sport;
5. 动作:-j ACCEPT/DROP/LOG → accept/drop/log(小写);
6. NAT动作:-j MASQUERADE/SNAT/DNAT → masquerade/snat/dnat。
另外特别提醒,iptables里的-j REJECT在nftables里是reject,但reject后面可以指定类型,比如reject with tcp reset。如果你原来用的是REJECT --reject-with icmp-port-unreachable,对应的写法是reject with icmp type port-unreachable。
七、迁移后的验证和持久化
写好规则后,先用nft -f /etc/nftables.conf加载测试,然后用nft list ruleset确认规则生效。CentOS 8/9上nftables服务默认是开机启动的,但要确认:
systemctl enable nftables systemctl start nftables
如果你发现规则加载后网络断了,别慌,本地控制台或者带外管理进来执行nft flush ruleset清空所有规则就能恢复。这也是为什么我建议迁移时一定要有带外管理手段,或者至少留一条允许SSH的规则最后写、最先验证。
还有一个细节:CentOS 8上如果同时装了firewalld,firewalld底层也是用nftables,但它有自己的规则管理方式。如果你决定直接用nftables管理,建议先systemctl stop firewalld && systemctl disable firewalld,避免两套系统互相冲突导致规则混乱。
八、常见踩坑点和实战建议
第一,nftables的链名是自定义的,不像iptables有固定的INPUT/FORWARD/OUTPUT。你可以叫input、inbound、my_custom_chain都行,但建议保持和iptables时代类似的命名习惯,降低维护成本。第二,nftables规则是原子性的,一条nft add rule就是一条完整规则,不存在iptables里-A和-I的区别那么复杂,但你可以用handle关键字精确控制规则插入位置。第三,nftables支持规则注释,用#或者meta comment "xxx"都行,方便日后维护。第四,如果你的环境有IPv6需求,强烈建议直接用inet表统一管理,而不是分别建ip和ip6表,除非你有特殊的双栈NAT需求。
总结一下,nftables替代iptables不是简单的命令替换,而是一次规则思维的升级。它更模块化、更灵活、性能更好,尤其是集合和映射机制在大规模规则场景下优势明显。迁移的核心就是理解"表-链-规则"的对应关系,然后逐条翻译。花一个下午把规则理清楚,后面的运维会轻松很多。
