| nftables深入浅出 |
| -入门介绍- |

故事从RHEL8开始...
CentOS 8 主要改动和 RedHat Enterprise Linux 8 是一致的,基于 Fedora 28 和内核版本 4.18,其中网络方面的主要改动是**用 nftables 框架替代 iptables 框架作为默认的网络包过滤工具。
以前iptables和firewalld兼容,现在是nftables和firewalld兼容了。
nftables 是一个 netfilter 项目,旨在替换现有的 {ip,ip6,arp,eb}tables 框架,为 {ip,ip6}tables 提供一个新的包过滤框架、一个新的用户空间实用程序(nft)和一个兼容层。它使用现有的钩子、链接跟踪系统、用户空间排队组件和 netfilter 日志子系统。
nftables 主要由三个组件组成:内核实现、libnl netlink 通信和 nftables 用户空间。 其中内核提供了一个 netlink 配置接口以及运行时规则集评估,libnl 包含了与内核通信的基本函数,用户空间可以通过 nft 和用户进行交互。
本文主要介绍用户空间命令行工具 nft 的用法。
nftables 和 iptables 一样,由表(table)、链(chain)和规则(rule)组成,其中表包含链,链包含规则,规则是真正的 action。与 iptables 相比,nftables 主要有以下几个变化:
iptables 规则的布局是基于连续的大块内存的,即数组式布局;而 nftables 的规则采用链式布局。其实就是数组和链表的区别,好像 Kubernetes 用户对此应该很兴奋?iptables 大部分工作在内核态完成,如果要添加新功能,只能重新编译内核;而 nftables 的大部分工作是在用户态完成的,添加新功能很 easy,不需要改内核。iptables 有内置的链,即使你只需要一条链,其他的链也会跟着注册;而 nftables 不存在内置的链,你可以按需注册。由于 iptables 内置了一个数据包计数器,所以即使这些内置的链是空的,也会带来性能损耗。IPv4/IPv6 双栈管理
nftables 的每个表只有一个地址簇,并且只适用于该簇的数据包。表可以指定五个簇中的一个:
| nftables簇 | iptables命令行工具 |
|---|---|
| ip | iptables |
| ip6 | ip6tables |
| inet | iptables和ip6tables |
| arp | arptables |
| bridge | ebtables |
inet 同时适用于 IPv4 和 IPv6 的数据包,即统一了 ip 和 ip6 簇,可以更容易地定义规则,下文的示例都将采用 inet 簇。
以上理论取自互联网并整理并加以个人理解,以下是实践部分。
怎么实际使用,怎么从iptables那边做对比理解,怎么配置生效,将是以下部分的重点。
##
table 表:
对应的是四表 raw 表、mangle 表、nat 表、filter 表,各表各从网络流量的类型上进行一次区分,我们常用的端口放行位于filter表中的入站链(INPUT)上
chain 链:对应的是五链,PREROUTING,INPUT,FORWARD,OUTPUT,POSTROUTING链。不是每种表都有链。
rule 规则:
在iptables或firewalld中定义的一条条策略,常规网络中用到的是配置在INPUT链中。如果是软路由、WAF等网络设备,则要完整的涉及所有的链、表。
更多见附录部分。
规则是存在于某表下某链下的
nftables也是一个服务,它就像iptables一样,可以与firewalld共存。表现是启动firewalld时,系统中还可以使用iptables或nftables命令(命令为nft)进行动态的防火墙配置。
可以认为firewalld只是它们的一个统一的配置入口。firewalld不是必须,而nftables/iptables则是不同时期的网络流量管制之必须。
centos7及之前则是iptables,而rhel8及之后暂时是新秀nftables。
所以说了这么多,引出结论是nftables的启动不是必须的 。
先不启动nftables。因为熟悉,更是因为一路走来老鸟已经熟悉了firewalld,好吧,你是00后...
systemctl start firewalld启动firewalld服务后,会默认创建firewalld表,filter_IN_public_allow链,显示的规则是通过firewall-cmd 添加上来的策略。验证一下:
x1[root@CentOS9 ~]# nft list tables2table inet firewalld3# 这个是默认由firewalld服务创建表、链4
5# 列出表下的规则6[root@CentOS9 ~]# nft list table inet firewalld7...8table inet firewalld {9...10 chain filter_IN_public_allow {11 tcp dport 22 ct state { new, untracked } accept12 ip daddr 224.0.0.251 udp dport 5353 ct state { new, untracked } accept13 ip6 daddr ff02::fb udp dport 5353 ct state { new, untracked } accept14 ip6 daddr fe80::/64 udp dport 546 ct state { new, untracked } accept15 tcp dport 54714 ct state { new, untracked } accept16 tcp dport 54752 ct state { new, untracked } accept17 }18...19
20}21
22# 注释:23# firewalld 就是inet类型的表名,自定义表名后面有说明24# filter_IN_public_allow 是由firewalld添加入站链,{} 中的是具体的规则25# 输出部分有省略启动firewalld之后什么都不做即有如上表链被创建,停止firewalld服务表链规则消失,什么都没有留下。
xxxxxxxxxx131[root@CentOS9 ~]# nft list chain inet firewalld filter_IN_public_allow2table inet firewalld {3 chain filter_IN_public_allow {4 tcp dport 22 ct state { new, untracked } accept5 ip daddr 224.0.0.251 udp dport 5353 ct state { new, untracked } accept6 ip6 daddr ff02::fb udp dport 5353 ct state { new, untracked } accept7 ip6 daddr fe80::/64 udp dport 546 ct state { new, untracked } accept8 tcp dport 54714 ct state { new, untracked } accept9 tcp dport 54752 ct state { new, untracked } accept10 }11}12
13# 指定链名时则只输出指定链的规则,是不是有点熟悉了,下面会越来越熟悉
xxxxxxxxxx151[root@CentOS9 ~]# nft list ruleset2...3table inet firewalld {4 chain filter_IN_public_allow {5 tcp dport 22 ct state { new, untracked } accept6 ip daddr 224.0.0.251 udp dport 5353 ct state { new, untracked } accept7 ip6 daddr ff02::fb udp dport 5353 ct state { new, untracked } accept8 ip6 daddr fe80::/64 udp dport 546 ct state { new, untracked } accept9 tcp dport 54714 ct state { new, untracked } accept10 tcp dport 54752 ct state { new, untracked } accept11 }12...13}14# 此命令也会列出所有规则15
向已有的表链中添加。
xxxxxxxxxx371
2[root@CentOS9 ~]# nft add rule inet firewalld filter_IN_public_allow tcp dport { 111,2049,41001-41004 } accept3[root@CentOS9 ~]# nft add rule inet firewalld filter_IN_public_allow udp dport { 111,2049,41001-41004 } accept4[root@CentOS9 ~]# nft list chain inet firewalld filter_IN_public_allow5table inet firewalld {6 chain filter_IN_public_allow {7 tcp dport 22 ct state { new, untracked } accept8 ip daddr 224.0.0.251 udp dport 5353 ct state { new, untracked } accept9 ip6 daddr ff02::fb udp dport 5353 ct state { new, untracked } accept10 ip6 daddr fe80::/64 udp dport 546 ct state { new, untracked } accept11 tcp dport 54714 ct state { new, untracked } accept12 tcp dport 54752 ct state { new, untracked } accept13 tcp dport { 111, 2049, 41001-41004 } accept14 udp dport { 111, 2049, 41001-41004 } accept15 }16}17
18[root@CentOS9 ~]# firewall-cmd --list-all19public (active)20 target: default21 icmp-block-inversion: no22 interfaces: eth023 sources:24 services: dhcpv6-client mdns ssh25 ports: 22/tcp 54714/tcp 54752/tcp26 protocols:27 forward: yes28 masquerade: no29 forward-ports:30 source-ports:31 icmp-blocks:32 rich rules:33 34# 通过firewall-cmd查看不到表明规则不是由firewalld管理的。35
36
37[root@CentOS9 ~]# firewalld服务停掉之后,此表链自动停掉。此表、链、规则也就失败了,相当于停掉防火墙了。此时可以用前面的查看表、链、规则的命令验证。
此时firewalld服务在运行状态。
xxxxxxxxxx621root@CentOS9 ~]# nft add table inet direct2[root@CentOS9 ~]# nft list tables3table inet firewalld4table inet direct5
6# 此时有2个inet表7
8# 在新创建的表中创建链9[root@CentOS9 ~]# nft add chain inet direct iptables10[root@CentOS9 ~]# nft list tables11table ip filter12table ip6 filter13table bridge filter14table inet firewalld15table inet direct16[root@CentOS9 ~]# nft list table inet direct17table inet direct {18 chain iptables {19 }20}21[root@CentOS9 ~]#22
23[root@Server-83af1803-a240-4112-9d44-05d0b89ae66a ~]# nft list chains24table inet firewalld { # 这里是由firewalld管理的表链25 chain mangle_PREROUTING {26 type filter hook prerouting priority mangle + 10; policy accept;27 }28 ...29 chain filter_INPUT_ZONES {30 }31 ...32 chain filter_IN_public_allow {33 }34 chain filter_IN_public_post {35 }36 chain nat_POST_public {37 }38 chain nat_POST_public_pre {39 }40 ...41 }42 chain mangle_PRE_public_allow {43 }44 chain mangle_PRE_public_post {45 }46 chain filter_IN_policy_allow-host-ipv6 {47 }48 ...49 }50 chain nat_PRE_policy_allow-host-ipv6 {51 }52 chain nat_PRE_policy_allow-host-ipv6_pre {53 }54 ...55 chain mangle_PRE_policy_allow-host-ipv6_post {56 }57}58table inet direct { # 这里是自定表链59 chain iptables {60 }61}62
xxxxxxxxxx271[root@CentOS9 ~]# systemctl status nftables -l 2● nftables.service - Netfilter Tables3 Loaded: loaded (/usr/lib/systemd/system/nftables.service; disabled; vendor preset: disabled)4 Active: active (exited) since Sat 2022-07-23 19:02:29 CST; 6s ago5 Docs: man:nft(8)6 Process: 3153799 ExecStart=/sbin/nft -f /etc/sysconfig/nftables.conf (code=exited, status=0/SUCCESS)7 Main PID: 3153799 (code=exited, status=0/SUCCESS)8
9[root@CentOS9 ~]# cat /usr/lib/systemd/system/nftables.service10
11[Unit]12Description=Netfilter Tables13Documentation=man:nft(8)14Wants=network-pre.target15Before=network-pre.target16
17[Service]18Type=oneshot19ProtectSystem=full20ProtectHome=true21ExecStart=/sbin/nft -f /etc/sysconfig/nftables.conf22ExecReload=/sbin/nft 'flush ruleset; include "/etc/sysconfig/nftables.conf";'23ExecStop=/sbin/nft flush ruleset24RemainAfterExit=yes25
26[Install]27WantedBy=multi-user.target从服务的启动过程中可以看到启动nftables的过程是一个重置、重载nfstables的过程,同时也提供的持久化的过程。
xxxxxxxxxx31ExecStart=/sbin/nft -f /etc/sysconfig/nftables.conf # 此时是从nftables.conf文件中加载2ExecReload=/sbin/nft 'flush ruleset; include "/etc/sysconfig/nftables.conf";' # 重启是是清空,再从nftables.conf中加载3ExecStop=/sbin/nft flush ruleset # 停止是重置查看/etc/sysconfig/nftables.conf
配置文件是个入口,会进一步加载更多的配置文件。同时从这里可以看出运行符号 # 作为注释.
xxxxxxxxxx161#2# This this will contain your nftables rules and3# is read by the systemd service when restarting4#5# These provide an iptables like set of filters6# (uncomment to include)7# include "/etc/nftables/bridge-filter"8# include "/etc/nftables/inet-filter"9# include "/etc/nftables/ipv4-filter"10# include "/etc/nftables/ipv4-mangle"11# include "/etc/nftables/ipv4-nat"12# include "/etc/nftables/ipv6-filter"13# include "/etc/nftables/ipv6-mangle"14# include "/etc/nftables/ipv6-nat"15include "/etc/nftables/inet-direct.nft" # 此行是自定义添加的16
前面手动添加策略到表链中的过程,这里向自定义表链中添加策略,然后将它输出到指定文件
xxxxxxxxxx221# 向自定义表链中添加规则2[root@CentOS9 ~]# nft add rule inet direct iptables tcp dport { 111,2049,41001-41004 } accept3[root@CentOS9 ~]# nft add rule inet direct iptables udp dport { 111,2049,41001-41004 } accept4[root@CentOS9 ~]# nft list chain inet direct iptables5table inet direct {6 chain iptables {7 tcp dport { 111, 2049, 41001-41004 } accept8 udp dport { 111, 2049, 41001-41004 } accept9 }10}11
12
13[root@CentOS9 ~]# nft list chain inet direct iptables > /etc/nftables/inet-direct.nft 14[root@CentOS9 ~]# cat /etc/nftables/inet-direct.nft 15table inet direct {16 chain iptables {17 tcp dport { 111, 2049, 41001-41004 } accept18 udp dport { 111, 2049, 41001-41004 } accept19 }20}21
22# 策略配置文件扩展名不重要,只需要与include引入时一致即可。
xxxxxxxxxx31[root@CentOS9 ~]# nft list ruleset > /tmp/ruleset_bak.nft2
3会加载配置文件就如同nginx加载主配置文件之外的配置文件
xxxxxxxxxx101[root@CentOS9 ~]# nft list table inet direct > /etc/nftables/inet-direct.nft2[root@CentOS9 ~]# cat /etc/nftables/inet-direct.nft3cat /etc/nftables/inet-direct.nft4table inet direct {5 chain iptables {6 tcp dport { 111, 2049, 41001-41004 } accept7 udp dport { 111, 2049, 41001-41004 } accept8 }9}10[root@CentOS9 ~]#
xxxxxxxxxx201[root@CentOS9 ~]# nft list ruleset2table inet firewalld {3 chain filter_IN_public_allow {4 tcp dport 22 ct state { new, untracked } accept5 ip daddr 224.0.0.251 udp dport 5353 ct state { new, untracked } accept6 ip6 daddr ff02::fb udp dport 5353 ct state { new, untracked } accept7 ip6 daddr fe80::/64 udp dport 546 ct state { new, untracked } accept8 tcp dport 54714 ct state { new, untracked } accept9 tcp dport 54752 ct state { new, untracked } accept10 tcp dport 50001-50005 ct state { new, untracked } accept11 }12
13... 省略大量firewalld规则14}15table inet direct { # 自定义规则16 chain iptables {17 tcp dport { 111, 2049, 41001-41004 } accept18 udp dport { 111, 2049, 41001-41004 } accept19 }20}
还记得防火墙清空命令iptables -F 和 查看命令 iptables -nL吗?nftables也有同样的操作。
xxxxxxxxxx141[root@CentOS9 ~]# nft flush ruleset # 清空防火墙规则2[root@CentOS9 ~]# nft list ruleset # 显示为空,表示已清空。即使firewall在运行中,规则也是同样被清空3[root@CentOS9 ~]# nft -f /etc/nftables/inet-direct.nft # 从文件中加载规则4[root@CentOS9 ~]# nft list ruleset # 查看规则,只有文件源中的策略。5table inet direct {6 chain iptables {7 tcp dport 111 ct state { new, untracked } accept8 tcp dport 2049 ct state { new, untracked } accept9 tcp dport 41001-41004 ct state { new, untracked } accept10 udp dport { 111, 2049, 41001-41004 } ct state { new, untracked } accept11 }12}13
14
重新启动或加载的firewall表链追加在了后面,原来加载的自定义表链规则还在。
表明:只要不删除或清空规则,从静态文件或firewalld中加入的规则并不会覆盖原有的规则,是纯追加。删除规则的命令见下文。
xxxxxxxxxx471#--> 使用firewall-cmd --reload或systemctl restart firewalld重新加载firewall规则后2[root@CentOS9 ~]# firewall-cmd --reload3[root@CentOS9 ~]# nft list ruleset4table inet direct { # 自定义规则5 chain iptables {6 tcp dport { 111, 2049, 41001-41004 } accept7 udp dport { 111, 2049, 41001-41004 } accept8 }9}10table inet firewalld {11 chain filter_IN_public_allow {12 tcp dport 22 ct state { new, untracked } accept13 ip daddr 224.0.0.251 udp dport 5353 ct state { new, untracked } accept14 ip6 daddr ff02::fb udp dport 5353 ct state { new, untracked } accept15 ip6 daddr fe80::/64 udp dport 546 ct state { new, untracked } accept16 tcp dport 54714 ct state { new, untracked } accept17 tcp dport 54752 ct state { new, untracked } accept18 tcp dport 50001-50005 ct state { new, untracked } accept19 }20
21... 省略大量firewalld规则22}23
24[root@CentOS9 ~]# nft -f /etc/nftables/inet-direct.nft # 从文件中加载规则25[root@CentOS9 ~]# nft list ruleset26table inet direct {27 chain iptables {28 tcp dport { 111, 2049, 41001-41004 } accept # 原有的规则29 udp dport { 111, 2049, 41001-41004 } accept30 tcp dport { 111, 2049, 41001-41004 } accept # 追加的规则31 udp dport { 111, 2049, 41001-41004 } accept32 }33}34table inet firewalld { 35 chain filter_IN_public_allow { # 因重启firewalld追加上的规则36 tcp dport 22 ct state { new, untracked } accept37 ip daddr 224.0.0.251 udp dport 5353 ct state { new, untracked } accept38 ip6 daddr ff02::fb udp dport 5353 ct state { new, untracked } accept39 ip6 daddr fe80::/64 udp dport 546 ct state { new, untracked } accept40 tcp dport 54714 ct state { new, untracked } accept41 tcp dport 54752 ct state { new, untracked } accept42 tcp dport 50001-50005 ct state { new, untracked } accept43 }44
45... 省略大量firewalld规则46}47
nftables服务与firewalld的关系,就像iptables与firewalld服务的关系一样,同时只能启动其中一个,另一个会自动停掉。
回到前面回顾nftables的启动过程,
将自定规则文件在/etc/sysconfig/nftables.conf中通过include引入,如下
xxxxxxxxxx161#2# This this will contain your nftables rules and3# is read by the systemd service when restarting4#5# These provide an iptables like set of filters6# (uncomment to include)7# include "/etc/nftables/bridge-filter"8# include "/etc/nftables/inet-filter"9# include "/etc/nftables/ipv4-filter"10# include "/etc/nftables/ipv4-mangle"11# include "/etc/nftables/ipv4-nat"12# include "/etc/nftables/ipv6-filter"13# include "/etc/nftables/ipv6-mangle"14# include "/etc/nftables/ipv6-nat"15include "/etc/nftables/inet-direct.nft" # 此行是自定义添加的16
启动服务命令:systemctl start nftables
以下过程验证服务只能同时运行一种。
xxxxxxxxxx391[root@Server-83af1803-a240-4112-9d44-05d0b89ae66a ~]# systemctl status nftables2○ nftables.service - Netfilter Tables3 Loaded: loaded (/usr/lib/systemd/system/nftables.service; disabled; vendor preset: disabled)4 Active: inactive (dead) since Sat 2022-07-23 19:18:29 CST; 20min ago5 Docs: man:nft(8)6 Process: 3153799 ExecStart=/sbin/nft -f /etc/sysconfig/nftables.conf (code=exited, status=0/SUCCESS)7 Process: 3155087 ExecStop=/sbin/nft flush ruleset (code=exited, status=0/SUCCESS)8 Main PID: 3153799 (code=exited, status=0/SUCCESS)9
10Jul 23 19:02:29 systemd[1]: Starting Netfilter Tables...11Jul 23 19:02:29 systemd[1]: Finished Netfilter Tables.12Jul 23 19:18:29 systemd[1]: Stopping Netfilter Tables...13Jul 23 19:18:29 systemd[1]: nftables.service: Deactivated successfully.14Jul 23 19:18:29 systemd[1]: Stopped Netfilter Tables.15[root@Server-83af1803-a240-4112-9d44-05d0b89ae66a ~]# systemctl start nftables16[root@Server-83af1803-a240-4112-9d44-05d0b89ae66a ~]# systemctl status nftables17● nftables.service - Netfilter Tables18 Loaded: loaded (/usr/lib/systemd/system/nftables.service; disabled; vendor preset: disabled)19 Active: active (exited) since Sat 2022-07-23 19:39:05 CST; 3s ago20 Docs: man:nft(8)21 Process: 3157625 ExecStart=/sbin/nft -f /etc/sysconfig/nftables.conf (code=exited, status=0/SUCCESS)22 Main PID: 3157625 (code=exited, status=0/SUCCESS)23
24Jul 23 19:39:05 systemd[1]: Starting Netfilter Tables...25Jul 23 19:39:05 systemd[1]: Finished Netfilter Tables.26[root@Server-83af1803-a240-4112-9d44-05d0b89ae66a ~]# systemctl status firewalld27○ firewalld.service - firewalld - dynamic firewall daemon28 Loaded: loaded (/usr/lib/systemd/system/firewalld.service; enabled; vendor preset: enabled)29 Active: inactive (dead) since Sat 2022-07-23 19:39:05 CST; 9s ago30 Docs: man:firewalld(1)31 Process: 3155086 ExecStart=/usr/sbin/firewalld --nofork --nopid $FIREWALLD_ARGS (code=exited, status=0/SUCCESS)32 Main PID: 3155086 (code=exited, status=0/SUCCESS)33
34Jul 23 19:18:29 systemd[1]: Starting firewalld - dynamic firewall daemon...35Jul 23 19:18:29 systemd[1]: Started firewalld - dynamic firewall daemon.36Jul 23 19:39:05 systemd[1]: Stopping firewalld - dynamic firewall daemon...37Jul 23 19:39:05 systemd[1]: firewalld.service: Deactivated successfully.38Jul 23 19:39:05 systemd[1]: Stopped firewalld - dynamic firewall daemon.39[root@Server-83af1803-a240-4112-9d44-05d0b89ae66a ~]#
xxxxxxxxxx241[root@CentOS9 ~]# nft list ruleset 2table inet direct {3 chain iptables {4 tcp dport { 111, 2049, 41001-41004 } accept5 udp dport { 111, 2049, 41001-41004 } accept6 tcp dport { 111, 2049, 41001-41004 } accept7 udp dport { 111, 2049, 41001-41004 } accept8 tcp dport { 111, 2049, 41001-41004 } accept9 udp dport { 111, 2049, 41001-41004 } accept10 }11}12# 由于firewalld停掉规则自动删除,而nftables服务启动时又执行了一次从配置文件中加载规则的过程,现在变成了6条规则13
14# 清理为只包含配置文件中的规则方法15[root@CentOS9 ~]# nsystemctl restart nftables16[root@CentOS9 ~]# nnft list ruleset17table inet direct {18 chain iptables {19 tcp dport { 111, 2049, 41001-41004 } accept20 udp dport { 111, 2049, 41001-41004 } accept21 }22}23[root@CentOS9 ~]# n24
从firewalld v0.6.0发布开始支持nftables作为后端(https://firewalld.org/2018/07/nftables-backend)
firewalld直接通过nft命令行工具也nftables联动,就像firewalld与iptables之间的联动是相似的。后期引入新的libnftables库。
从如下的firewalld设计构架图中可以看到,nftables扮演的是后端角色。

firewalld的常规设置项目(如服务、端口、端口转发等)默认使用nftables.其它的可有要经过一点转变,转化成原生的nftables sets.
修改配置文件/etc/firewalld/firewalld.conf
x
1FirewallBackend=iptables 2# 详见文件注释,将后端切换为,iptables一般用于支持docker、3# 或者处理报错:Error: COMMAND_FAILED: 'python-nftables' failed: internal:0:0-0: Error: Could not process rule: Numerical result out of range
nft语法规范
nft命令和iptables类似都有语法检测功能,直接通过命令添加规则本身就是一个验证语法的过程。如果你比较熟悉命令操作,可以直接在配置文件中修改。
实用部分章节主要以验证和对比为主,通过一个添加多端口的实例来说明和对比iptables与nftables之间的区别与联系。
linux新内核从4.18后使用了nftables,可能有的服务器上依然有iptables命令,但iptables显然执行不报错,但也已经失效了。如何iptables -F已经无法清空nftables规则了,请使用nft flush ruleset。即是说,在同一个内核上它们只会有一个正常。
通过firewall-cmd命令过渡的方式来直接添加熟悉规则,然后通过nft查看规则结果,从而对比和学习如何直接通过nft命令添加规则。
理解nftables的新功能特点
对比iptables与nftables的对比与联系,从而快速切换,新事物都是为解决复杂问题而被提出来的
nftables的语法更加倾向于行业规范化,与wireshark、iproute2util等的语法接近。
引用:
https://www.netfilter.org/projects/nftables/manpage.html
更多实例,未完待续...如果你还能看到这名话,表明是真的