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。

  1. 将iptables或ip6tables规则写入到文件:

    # iptables-save >/root/iptables.dump
    # ip6tables-save >/root/ip6tables.dump
    
  2. 将文件内容转换为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
    
  3. 将下列内容添加到文件/etc/sysconfig/nftables.conf中:

    include "/etc/nftables/ruleset-migrated-from-iptables.nft"
    include "/etc/nftables/ruleset-migrated-from-ip6tables.nft"
    
  4. 停止并禁用iptables服务:

    systemctl disable --now iptables
    
  5. 使能并启动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

  1. 创建表

    nft add table nat
    

    注意:nat表一般在系统上已经存在。

  2. 在表中添加链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框架需要此链来匹配传入报文。

  3. 在链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
posted @ 2025-08-31 10:31  苍然满关中  阅读(3)  评论(0)    收藏  举报