nftables
nftables
历史
nftables集成并替代下列工具:
iptables/ip6tables -- 这两个工具分别处理IPv4和IPv6报文。
arptables -- 这个工具处理ARP报文。
ebtables -- 这个工具处理以太网桥接(Ethernet bridging)帧。
ipset -- 这是对iptables的扩展,它是一种内核级数据结构,与iptables一起使用,用于创建IP地址、端口号和其他与网络相关的元素的集合,这些集合可以与报文进行匹配。这些集合显著简化了编写和管理防火墙规则的过程,并对其进行了优化和加速。
基础
表(table)
nftables中的表是一个名称空间,包含一组链(chain)、规则(rule)、集合(set)和其它对象。
每个表都必须被分配一个地址族。这个地址族定义了该表处理的报文类型。
在创建表时,可以为它设置下列地址族之一:
- ip:仅匹配IPv4报文。在不指定地址族时,这是默认值。
- ip6:仅匹配IPv6报文。
- inet:同时匹配IPv4和IPv6报文。
- arp:匹配IPv4 ARP报文。
- bridge:匹配穿过桥接设备的报文。
- netdev:匹配入口(ingress)报文。
在添加表时,要使用的格式取决于防火墙脚本(firewall script):
-
脚本原生语法:
table <table_address_family> <table_name> { }
-
shell脚本:
nft add table <table_address_family> <table_name>
链(chain)
表由链组成,链中包含规则。有下列两种规则类型:
- Base chain:可以使用基本链作为网络栈中报文的入口点。-- 需要指定hook类型和优先级;
- Regular chain:可以使用常规链作为jump的目标,以更好的组织规则。-- 不需要指定hook类型和优先级。
向表中添加链的格式取决于防火墙脚本(firewall script):
-
脚本原生语法:
table <table_address_family> <table_name> { chain <chain_name> { type <type> hook <hook> priority <priority> policy <policy> ; } }
-
shell脚本:
nft add chain <table_address_family> <table_name> <chain_name> { type <type> hook <hook> priority <priority> \; policy <policy> \; }
为了避免shell将分号(;)解释为命令行的结尾,在分号前使用\进行转义。
上述两个例子都是创建基本链。
要想创建常规链,则不要在{}中设置任何参数。
链类型(type):
Type | 地址族 | Hooks | 描述 |
---|---|---|---|
filter | all | all | 标准链类型 |
nat | ip, ip6, inet | prerouting, input, output, postrouting | 这种类型的链基于连接跟踪条目进行地址转换。仅第一个报文会穿过此链类型。 |
route | ip, ip6 | output | 如果IP报头的相关部分发生了变化,则通过此链类型的已接受报文会触发新的路由查找。 |
链优先级(priority):
优先级参数指定了报文在具有相同hook值的链中遍历的顺序。您可以将此参数设置为一个整数值,或使用标准的优先级名称。
文本值 | 数字值 | 地址族 | Hooks |
---|---|---|---|
raw | -300 | ip, ip6, inet | all |
mangle | -150 | ip, ip6, inet | all |
dstnat | -100 | ip, ip6, inet | prerouting |
-300 | bridge | prerouting | |
filter | 0 | ip, ip6, inet, arp, netdev | all |
-200 | bridge | all | |
security | 50 | ip, ip6, inet | all |
srcnat | 100 | ip, ip6, inet | postrouting |
300 | bridge | postrouting | |
out | 100 | bridge | output |
链策略(policy):
链策略定义了当此链中的规则未指定任何动作时,nftables是否应该接受或丢弃报文。
可以在链中设置下列策略之一:
- accept(默认值)
- drop
规则(rule)
规则定义了对通过包含此规则的链的报文应执行的操作。如果规则中还包含匹配表达式,则只有在所有先前的表达式都适用时,nftables才会执行这些操作。
向链中添加规则的格式取决于防火墙脚本(firewall script):
-
脚本原生语法:
table <table_address_family> <table_name> { chain <chain_name> { type <type> hook <hook> priority <priority> ; policy <policy> ; <rule> } }
-
shell脚本:
nft add rule <table_address_family> <table_name> <chain_name> <rule>
这条shell命令在链的末尾追加新规则。如果要在链的开头插入规则,使用nft insert命令。
以在创建规则时就获取到规则的句柄值,只需要在创建规则时同时加上参数 --echo
和 --handle
。
nft --echo --handle add rule inet my_table my_filter_chain udp dport 3333 accept
常见用法示例
新增表:
nft add table inet nftables_svc
向表中添加链:
nft add chain inet nftables_svc INPUT { type filter hook input priority filter ; policy accept ; }
这里添加的是基本链(INPUT),类型是filter,hook是input,优先级是filter,策略是accept。
向链中添加规则:
nft add rule inet nftables_svc INPUT tcp dport 22 accept
nft add rule inet nftables_svc INPUT tcp dport 443 accept
nft add rule inet nftables_svc INPUT reject with icmpx type port-unreachable
上述命令向表nftables_svc的链INPUT中添加了3条规则。
查看当前规则集合:
# nft -a list table inet nftables_svc
table inet nftables_svc { # handle 13
chain INPUT { # handle 1
type filter hook input priority filter; policy accept;
tcp dport 22 accept # handle 2
tcp dport 443 accept # handle 3
reject # handle 4
}
}
在handle3前面插入规则:
nft insert rule inet nftables_svc INPUT position 3 tcp dport 636 accept
在handle3后面追加规则:
nft add rule inet nftables_svc INPUT position 3 tcp dport 80 accept
再次查看当前规则集合:
# nft -a list table inet nftables_svc
table inet nftables_svc { # handle 13
chain INPUT { # handle 1
type filter hook input priority filter; policy accept;
tcp dport 22 accept # handle 2
tcp dport 636 accept # handle 5
tcp dport 443 accept # handle 3
tcp dport 80 accept # handle 6
reject # handle 4
}
}
删除单条规则:
nft delete rule inet nftables_svc INPUT handle 6
删除规则时,必须指定其handle
删除链中所有规则:
nft flush chain inet nftables_svc INPUT
删除链:
nft delete chain inet nftables_svc INPUT
删除表:
nft delete table inet nftables_svc
注意:
可以使用命令“nft flush ruleset”来删除整个规则集合。
从iptables迁移到nftables
在RHEL8中,有3种报文过滤工具可用:
- firewalld,此工具针对常见使用场景简化了防火墙配置。
- nftables,使用此工具来设置复杂的和性能关键的防火墙。
- iptables,此工具使用nf_tables内核API,提供向后兼容性。
建议使用nftables。为了防止上述3种服务互相干扰,建议仅运行其中一个,将其它禁用。
与iptables框架相比,nftables提供了一个更现代、高效且灵活的替代方案。nftables框架提供了比iptables更高级的功能和改进,简化了规则管理并提升了性能。这使得nftables成为复杂且高性能网络环境的现代替代方案。
转换所有规则
使用工具iptables-restore-translate和ip6tables-restore-translate将iptables或ip6tables规则集合转换成nftables。
-
将iptables或ip6tables规则写入到文件:
# iptables-save >/root/iptables.dump # ip6tables-save >/root/ip6tables.dump
-
将文件内容转换为nftables指令:
iptables-restore-translate -f /root/iptables.dump > /etc/nftables/ruleset-migrated-from-iptables.nft ip6tables-restore-translate -f /root/ip6tables.dump > /etc/nftables/ruleset-migrated-from-ip6tables.nft
-
将下列内容添加到文件/etc/sysconfig/nftables.conf中:
include "/etc/nftables/ruleset-migrated-from-iptables.nft" include "/etc/nftables/ruleset-migrated-from-ip6tables.nft"
-
停止并禁用iptables服务:
systemctl disable --now iptables
-
使能并启动nftables服务:
systemctl enable --now nftables
转换单条规则
使用工具iptables-translate和ip6tables-translate将一条iptables或ip6tables规则转换成等价的nftables规则。
# iptables-translate -A INPUT -s 192.0.2.0/24 -j ACCEPT
nft add rule ip filter INPUT ip saddr 192.0.2.0/24 counter accept
注意:对一些扩展缺少转换支持,这种情况下,工具会将打印未转换的规则,并添加前缀“#”:
# iptables-translate -A INPUT -j CHECKSUM --checksum-fill
nft # -A INPUT -j CHECKSUM --checksum-fill
使用nftables配置NAT
通过nftables,可以配置下列网络地址转换(NAT)类型:
- Masquerading
- SNAT
- DNAT
- Redirect
NAT类型
Masquerading和SNAT
使用这些类型之中的一个来改变报文的源地址。
Masquerading和SNAT之间非常类似。它们的区别是:
- Masquerading自动使用出接口的IP地址。因此,如果出接口使用动态IP地址,则使用Masquerading。
- SNAT将报文的源地址设置为指定的IP地址,不会动态查找出接口的IP地址。因此,SNAT要比Masquerading快。如果出接口使用固定IP地址,则使用SNAT。
DNAT
使用此NAT类型来重写传入报文的目的IP地址和端口。
例如,如果web服务器使用私有IP范围内的IP地址,因而无法从公网直接访问,则可以在路由器上设置一个DNAT规则,将传入流量重定向到此服务器。
Redirect
这种类型是DNAT的特例,根据链钩子(chain hook)重定向报文。
例如,如果服务运行在非标准端口上,则可以将传入流量重标准端口重定向到此特定端口。
使用nftables配置Masquerading
-
创建表
nft add table nat
注意:nat表一般在系统上已经存在。
-
在表中添加链prerouting和postrouting
nft add chain nat postrouting { type nat hook postrouting priority 100 \; } #当优先级值为负数时,使用下列形式的命令行,以防止shell将负号解释为选项: nft -- add chain nat prerouting { type nat hook prerouting priority -100 \; }
重要:即使不在链prerouting中添加规则,nftables框架需要此链来匹配传入报文。
-
在链postrouting中添加规则
nft add rule nat postrouting oifname "ens3" masquerade
写作和执行nftables脚本
使用nftables框架的主要好处是脚本执行是自动的。这意味着系统要么应用整个脚本,要么在发生错误时阻止执行。这保证了防火墙总是处于一致的状态。
此外,使用nftables脚本环境,可以:
- 添加注释
- 定义变量
- 包含其它规则集合文件
当安装了nftables包后,系统自动在目录/etc/nftables/下创建*.nft脚本。这些脚本包含创建表的命令行以及用于不同目的的空链。
nftables脚本格式
在nftables脚本环境中,可以使用下列格式来写作脚本:
-
与命令行“nft list ruleset”展示的规则集合相同的格式:
#!/usr/sbin/nft -f # Flush the rule set flush ruleset table inet example_table { chain example_chain { # Chain for incoming packets that drops all packets that # are not explicitly allowed by any rule in this chain type filter hook input priority 0; policy drop; # Accept connections to port 22 (ssh) tcp dport ssh accept } }
-
与nft命令相同的语法:
#!/usr/sbin/nft -f # Flush the rule set flush ruleset # Create a table add table inet example_table # Create a chain for incoming packets that drops all packets # that are not explicitly allowed by any rule in this chain add chain inet example_table example_chain { type filter hook input priority 0 ; policy drop ; } # Add a rule that accepts connections to port 22 (ssh) add rule inet example_table example_chain tcp dport ssh accept
运行nftables脚本
可以将nftables脚本传递给nft工具运行。也可以直接运行脚本。
-
将脚本传递给nftables工具运行:
# nft -f /etc/nftables/<example_firewall_script>.nft
-
直接运行
确保脚本以下列行开头:
#!/usr/sbin/nft -f
重要:如果省略了-f参数,nft工具将不会读取脚本,并展示下列错误:
Error: syntax error, unexpected newline, expecting string
设置脚本的所有者为root
# chown root /etc/nftables/<example_firewall_script>.nft
为脚本赋予执行权限
# chmod u+x /etc/nftables/<example_firewall_script>.nft
执行脚本:
# /etc/nftables/<example_firewall_script>.nft
如果没有打印输出,则系统执行脚本成功。
在nftables脚本中使用注释
注释行以#开头
注释可以放在单独的行上,也可以跟在命令后面。
在nftables脚本中使用变量
使用关键字define在nftables脚本中定义变量。
保存单个值的变量
下列示例定义了一个值为“enp1s0”的变量“INET_DEV”:
define INET_DEV = enp1s0
可以在脚本中通过$符号引用变量。
包含匿名集合(anonymous set)的变量
下列示例定义了一个变量“DNS_SERVERS”,其值是一个匿名集合:
define DNS_SERVERS = { 192.0.2.1, 192.0.2.2 }
在nftables脚本中包含文件
可以使用include语句来包含其它脚本。
如果仅指定了文件名,没有绝对或相对路径,nftables从默认搜索目录中包含文件。默认搜索目录一般是/etc。
#从默认搜索目录中包含文件
include "example.nft"
#包含目录/etc/nftables/rulesets/下所有以.nft结尾的文件
include "/etc/nftables/rulesets/*.nft"
注意:include语句不会匹配以.开头的文件。
在系统启动时自动加载nftables规则
系统服务nftables自动加载包含在文件/etc/sysconfig/nftables.conf中的防火墙脚本。
将脚本保存在目录/etc/nftables/下。
编辑文件/etc/sysconfig/nftables.conf,使用include语句包含新写的脚本。
使能并启动nftables服务。
在nftables命令中使用集合(set)
nftables框架原生支持集合。如果一条规则要匹配多个IP地址、端口号、接口或任何其它匹配条件,则可以使用集合。
在nftables中使用匿名集合
匿名集合使用大括号{}包围逗号分隔的值,在规则中直接使用。例如{ 22, 80, 443 }。
匿名集合的缺点是,如果想修改集合,则必须替换规则。
nft add rule inet example_table example_chain tcp dport { 22, 80, 443 } accept
在nftables中使用命名集合
nftables框架支持可变的命名集合。
可以在表中的多条规则中使用命名集合。
与匿名集合相比,命名集合的另一个好处是可以更新它,而不需要替换使用它的规则。
在创建命名集合时,必须指定此集合包含的元素类型。可以设置下列类型:
- ipv4_addr:集合包含IPv4地址或范围,例如192.0.2.1或192.0.2.0/24。
- ipv6_addr:集合包含IPv6地址或范围,例如2001:db8:1::1或2001:db8:1::1/64。
- ether_addr:集合包含MAC地址,例如52:54:00:6b:66:42。
- inet_proto:集合包含互联网协议类型的列表,例如tcp。
- inet_service:集合包含互联网服务的列表,例如ssh。
- mark:集合包含报文标记的列表。报文标记是任何正的32位整数值(0到2147483647)。
创建一个保存多个单独IPv4地址的集合:
#创建命名集合
# nft add set inet example_table example_set { type ipv4_addr \; }
#向集合添加元素
# nft add element inet example_table example_set { 192.0.2.1, 192.0.2.2 }
创建一个保存IPv4地址范围的集合:
#创建命名集合
# nft add set inet example_table example_set { type ipv4_addr \; flags interval \; }
#向集合添加元素,也可以使用192.0.2.0/24替代
# nft add element inet example_table example_set { 192.0.2.0-192.0.2.255 }
在规则中使用命名集合:
# nft add rule inet example_table example_chain ip saddr @example_set drop
动态集合(dynamic set)
nftables框架中的动态集合允许自动添加来自报文数据的元素。例如,IP地址、目标端口、MAC地址等。此功能使您能够实时收集这些元素,并使用它们来创建拒绝列表、禁止列表等,以便您能够即时应对安全威胁。
#创建表
nft add table inet example_table
#创建链
nft add chain inet example_table example_chain { type filter hook input priority 0\; }
#创建集合
nft add set inet example_table example_set { type ipv4_addr \; }
#创建动态向集合中添加条目的规则
nft add rule inet example_table example_chain set add ip saddr @example_set
#查看结果
[root@localhost ~]# nft list table inet example_table
table inet example_table {
set example_set {
type ipv4_addr
size 65535
elements = { 192.168.224.1, 192.168.234.1,
192.168.234.254 }
}
chain example_chain {
type filter hook input priority filter; policy accept;
add @example_set { ip saddr }
}
}
在nftables命令中使用裁决映射(Verdict maps)
裁决映射也被称为字典,通过将匹配条件映射到动作,可以让nft基于报文信息执行动作。
在nftables中使用匿名映射(anonymous map)
匿名映射是一条{ match_criteria : action }语句,可以在规则中直接使用。该语句可以包含多个逗号分割的映射。
匿名映射的缺点是,如果想修改映射,则必须替换规则。
可以使用匿名映射将IPv4和IPv6的TCP和UDP报文路由到不同的链,以分别对传入的TCP和UDP报文进行计数。
#创建表
nft add table inet example_table
#创建TCP链
nft add chain inet example_table tcp_packets
#向链中添加规则,对链中的流量进行计数
nft add rule inet example_table tcp_packets counter
#创建UDP链
nft add chain inet example_table udp_packets
#向链中添加规则,对链中的流量进行计数
nft add rule inet example_table udp_packets counter
#为传入流量创建链。
nft add chain inet example_table incoming_traffic { type filter hook input priority 0 \; }
#向链中添加规则,使用匿名映射。此匿名映射对报文进行区分,并将它们发往不同的计数链
nft add rule inet example_table incoming_traffic ip protocol vmap { tcp : jump tcp_packets, udp : jump udp_packets }
#查看结果
[root@localhost ~]# nft list table inet example_table
table inet example_table {
set example_set {
type ipv4_addr
size 65535
elements = { 45.76.221.157, 192.168.224.1,
192.168.234.1, 192.168.234.254,
193.182.111.12, 193.182.111.142,
193.182.111.143 }
}
chain example_chain {
type filter hook input priority filter; policy accept;
add @example_set { ip saddr }
}
chain tcp_packets {
counter packets 72 bytes 5568
}
chain udp_packets {
counter packets 0 bytes 0
}
chain incoming_traffic {
type filter hook input priority filter; policy accept;
ip protocol vmap { tcp : jump tcp_packets, udp : jump udp_packets }
}
}
在nftables中使用命名映射(named map)
nftables框架支持命名映射。
可以在表中的多条规则中使用命名映射。
与匿名映射相比,命名映射的另一个好处是可以更新它,而不需要替换使用它的规则。
在创建命名映射时,必须指定其包含的元素类型。可以设置下列类型:
- ipv4_addr:映射的匹配部分包含IPv4地址,例如192.0.2.1。
- ipv6_addr:映射的匹配部分包含IPv6地址,例如2001:db8:1::1。
- ether_addr:映射的匹配部分包含MAC地址,例如52:54:00:6b:66:42。
- inet_proto:映射的匹配部分包含互联网协议类型的列表,例如tcp。
- inet_service:映射的匹配部分包含互联网服务的列表,例如ssh或22。
- mark:映射的匹配部分包含报文标记的列表。报文标记是任何正的32位整数值(0到2147483647)。
- counter:映射的匹配部分包含计数值。计数值是任何正的64位整数值。
- quota:映射的匹配部分包含配额值。配额值是任何正的64位整数值。
#创建表
nft add table ip example_table
#为传入流量创建链。
nft add chain ip example_table example_chain { type filter hook input priority 0 \; }
#创建空的命名映射
nft add map ip example_table example_map { type ipv4_addr : verdict \; }
#创建规则,引用上一步创建的映射
nft add rule example_table example_chain ip saddr vmap @example_map
#向命名映射中添加IPv4地址和对应的动作
nft add element ip example_table example_map { 192.0.2.1 : accept, 192.0.2.2 : drop }
#查看结果
[root@localhost ~]# nft list table ip example_table
table ip example_table {
map example_map {
type ipv4_addr : verdict
elements = { 192.0.2.1 : accept, 192.0.2.2 : drop }
}
chain example_chain {
type filter hook input priority filter; policy accept;
ip saddr vmap @example_map
}
}
#再次向命名映射中添加条目
nft add element ip example_table example_map { 192.0.2.3 : accept }
#从命名映射中删除条目
nft delete element ip example_table example_map { 192.0.2.1 }
使用nftables限制连接数量
可以使用nftables来限制连接数量,或者阻止那些尝试建立给定数量连接的IP地址,以防止它们使用太多系统资源。
使用nftables限制连接数量
限制所有IP地址同时创建的SSH连接不超过2个:
#创建表
nft add table inet filter
#创建链
nft add chain inet filter input { type filter hook input priority 0 \; }
#创建集合
nft add set inet filter limit-ssh { type ipv4_addr\; flags dynamic \;}
#创建动态向集合中添加条目的规则,下面的规则在CentOS8系统下创建失败,提示“操作不支持”
nft add rule inet filter input tcp dport ssh ct state new add @limit-ssh { ip saddr ct count over 2 } counter reject
阻止同一IP地址在1分钟内创建超过10个TCP连接:
nft add rule ip filter input ip protocol tcp ct state new, untracked meter ratemeter { ip saddr timeout 5m limit rate over 10/minute } drop
调试nftables规则
nftables框架为管理员提供不同的选项来调试规则,确定报文是否与规则匹配。
为了确定一条规则是否被匹配,可以创建一个计数器(counter)。
下面的命令创建一个带计数器的规则:
nft add rule inet example_table example_chain tcp dport 22 counter accept
下面的命令为已有的规则添加计数器:
#首先使用nft -a获取规则的handle编号
nft replace rule inet example_table example_chain handle 4 tcp dport 22 counter accept
监控与规则匹配的报文:
#替换规则
nft replace rule inet example_table example_chain handle 4 tcp dport 22 meta nftrace set 1 accept
#使用nft monitor命令查看结果
[root@localhost ~]# nft monitor | grep "inet example_table example_chain"
trace id 8387dc4c inet example_table example_chain packet: iif "ens37" ether saddr 00:50:56:c0:00:08 ether daddr 00:0c:29:44:b7:e4 ip saddr 192.168.224.1 ip daddr 192.168.224.148 ip dscp cs0 ip ecn not-ect ip ttl 128 ip id 28894 ip protocol tcp ip length 40 tcp sport 53214 tcp dport 22 tcp flags == ack tcp window 508
trace id 8387dc4c inet example_table example_chain rule tcp dport 22 meta nftrace set 1 accept (verdict accept)
trace id 8387dc4c inet example_table example_chain packet: iif "ens37" ether saddr 00:50:56:c0:00:08 ether daddr 00:0c:29:44:b7:e4 ip saddr 192.168.224.1 ip daddr 192.168.224.148 ip dscp cs0 ip ecn not-ect ip ttl 128 ip id 28895 ip protocol tcp ip length 40 tcp sport 53214 tcp dport 22 tcp flags == ack tcp window 511
trace id 8387dc4c inet example_table example_chain rule tcp dport 22 meta nftrace set 1 accept (verdict accept)
备份和恢复nftables规则集合
可以将nftables规则备份到文件并在以后恢复它们。
管理员也可以将这些规则文件传输到其它服务器。
备份:
#使用默认输出格式
nft list ruleset > file.nft
#使用json格式:
nft -j list ruleset > file.json
恢复:
#使用默认格式
nft -f file.nft
#使用json格式
nft -j -f file.json