Red Hat Training

A Red Hat training course is available for RHEL 8

第 47 章 nftables 入门

nftables 框架提供数据包分类工具,它是 iptablesip6tablesarptablesebtablesipset 工具的指定后台。与之前的数据包过滤工具相比,它在方便、特性和性能方面提供了大量改进,最重要的是:

  • 内置查找表而不是线性处理
  • IPv4IPv6 协议都使用同一个框架
  • 规则会以一个整体被应用,而不是分为抓取、更新和存储完整的规则集的步骤
  • 支持在规则集(nftrace)中进行调试和追踪,并监控追踪事件(在 nft 工具中)
  • 更加一致和压缩的语法,没有特定协议的扩展
  • 用于第三方应用程序的 Netlink API

iptables类似, nftables 使用表来存储链。链包含执行动作的独立规则。nft 工具替换了之前数据包过滤框架中的所有工具。libnftnl 库可用于通过 libmnl 库与 nftables Netlink API 进行底层交互。

要显示规则集更改的效果,使用 nft list ruleset 命令。这些工具将表、链、规则、集合和其他对象添加到 nftables 规则集中,请注意,nftables 规则集操作(如 nft flush ruleset 命令)可能会影响使用以前独立的旧命令安装的规则集。

47.1. 从 iptables 迁移到 nftables

如果您将服务器升级到 RHEL 8,或者防火墙配置仍然使用 iptables 规则,您可以将 iptables 规则迁移到 nftables

47.1.1. 使用 firewalld、nftables 或者 iptables 时

以下是您应该使用以下工具之一的概述:

  • firewalld: 在简单防火墙用例中使用 firewalld 工具。该工具非常容易使用,并涵盖这些场景的典型用例。
  • nftables: 使用 nftables 实用程序设置复杂和性能的关键防火墙,比如为整个网络设置。
  • iptables:Red Hat Enterprise Linux 8 中的 iptables 工具程序使用 nf_tables 内核 API 而不是 legacy 后端。nf_tables API 提供向后兼容性,使用 iptables 命令的脚本仍然可用于 Red Hat Enterprise Linux 8。对于新的防火墙脚本,红帽建议使用 nftables
重要

要避免不同的防火墙服务相互影响,在 RHEL 主机中只有一个服务,并禁用其他服务。

47.1.2. 将 iptables 规则转换为 nftables 规则

Red Hat Enterprise Linux 8 提供 iptables-translateip6tables-translate 工具来将现有 iptablesip6tables 规则转换为对等的 nftables 规则。

请注意,一些扩展可能缺少响应的转换支持。如果存在这种扩展,该工具会输出以 # 符号为前缀的未转换的规则。例如:

# iptables-translate -A INPUT -j CHECKSUM --checksum-fill
nft # -A INPUT -j CHECKSUM --checksum-fill

另外,用户可以使用 iptables-restore-translateip6tables-restore-translate 工具来翻译规则转储。请注意,在此之前,用户可以使用 iptables-save 或者 ip6tables-save 命令打印当前规则的转储。例如:

# iptables-save >/tmp/iptables.dump
# iptables-restore-translate -f /tmp/iptables.dump

# Translated by iptables-restore-translate v1.8.0 on Wed Oct 17 17:00:13 2018
add table ip nat
...

要获得更多信息以及可能的选项和值列表,请使用 iptables-translate --help 命令。

47.1.3. 常用 iftables 和 nftables 命令的比较

以下是常见的 iptablesnftables 命令的比较:

  • 列出所有规则:

    iptablesnftables

    iptables-save

    nft list ruleset

  • 列出特定的表和链:

    iptablesnftables

    iptables -L

    nft list table ip filter

    iptables -L INPUT

    nft list chain ip filter INPUT

    iptables -t nat -L PREROUTING

    nft list chain ip nat PREROUTING

    nft 命令不预先创建表和链。只有在用户手动创建它们时才会出现它们。

    示例:列出 firewalld 生成的规则

    # nft list table inet firewalld
    # nft list table ip firewalld
    # nft list table ip6 firewalld

47.2. 编写和执行 nftables 脚本

nftables 框架提供了一个原生脚本环境,它比使用 shell 脚本维护防火墙规则得到主要优势:执行脚本是原子的(以一个整体运行)。这意味着,系统会应用整个脚本,或者在出现错误时防止执行。这样可保证防火墙始终处于一致状态。

另外,nftables 脚本环境使管理员能够:

  • 添加评论
  • 定义变量
  • 包含其他规则集文件

本节介绍如何使用这些功能,以及创建和执行 nftables 脚本。

当您安装 nftables 软件包时,Red Hat Enterprise Linux 会在 /etc/nftables/ 目录中自动生成 *.nft 脚本。这些脚本包含为不同目的创建表和空链的命令。

47.2.1. 支持的 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

47.2.2. 运行 nftables 脚本

您可以通过将其传递给 nft 工具或直接执行脚本来运行 nftables 脚本。

先决条件

  • 本节的步骤假设您在文件 /etc/nftables/example_firewall.nft 中保存了 nftables 脚本。

流程

  • 要通过传递给 nft 实用程序来运行 nftables 脚本,请输入:

    # nft -f /etc/nftables/example_firewall.nft
  • 直接运行 nftables 脚本:

    1. 只需要执行一次的步骤:

      1. 确保脚本以以下 rbang 序列开头:

        #!/usr/sbin/nft -f
        重要

        如果您省略 -f 参数, nft 实用程序不会读取脚本并显示: Error: syntax error, unexpected newline, expecting string

      2. 可选:将脚本的所有者设置为 root:

        # chown root /etc/nftables/example_firewall.nft
      3. 使脚本可以被其所有者执行:

        # chmod u+x /etc/nftables/example_firewall.nft
    2. 运行脚本:

      # /etc/nftables/example_firewall.nft

      如果没有输出结果,系统将成功执行该脚本。

重要

即使 nft 成功执行脚本,在脚本中错误地放置规则、缺失的参数或其他问题都有可能导致防火墙的行为不如预期。

其它资源

  • 有关设置文件所有者的详情,请参考 chown(1) man page。
  • 有关设置文件权限的详情,请查看 chmod(1) man page。
  • 有关使用系统引导载入 nftables 规则的详情,请参考系统引导 时自动载入 nftables 规则

47.2.3. 使用 nftables 脚本中的注释

nftables 脚本环境将 # 字符右边的所有内容都作为注释来解释。

例 47.1. nftables 脚本中的注释

注释可在一行的开始,也可以在命令后:

...
# Flush the rule set
flush ruleset

add table inet example_table  # Create a table
...

47.2.4. 使用 nftables 脚本中的变量

要在 nftables 脚本中定义变量,请使用 define 关键字。您可以在变量中存储单个值和匿名集合。对于更复杂的场景,请使用 set 或 verdict 映射。

只有一个值的变量

以下示例定义了名为 INET_DEV 的变量,其值为 enp1s0

define INET_DEV = enp1s0

您可以通过在脚本中写入 $ 符号后加上变量名称来使用脚本中的变量:

...
add rule inet example_table example_chain iifname $INET_DEV tcp dport ssh accept
...
包含匿名集合的变量

以下示例定义了一个包含匿名集合的变量:

define DNS_SERVERS = { 192.0.2.1, 192.0.2.2 }

您可以通过在脚本中写入 $ 符号后加上变量名称来使用脚本中的变量:

add rule inet example_table example_chain ip daddr $DNS_SERVERS accept
注意

请注意,在规则中使用大括号时具有特殊的意义,因为它们表示变量代表一个集合。

其它资源

47.2.5. 在 nftables 脚本中包含文件

nftables 脚本环境可让管理员使用 include 说明包括其他脚本。

如果您只指定没有包括绝对路径或相对路径的文件名, nftables 包含默认搜索路径中的文件,在 Red Hat Enterprise Linux 中为 /etc

例 47.2. 包含默认搜索目录中的文件

从默认搜索目录中包含一个文件:

include "example.nft"

例 47.3. 包含目录中的所有 *.nft 文件

包含以 *.nft 结尾的、存储在 /etc/nftables/rulesets/ 目录中的文件:

include "/etc/nftables/rulesets/*.nft"

请注意 include 语句不匹配与以点开头的文件。

其它资源

  • 详情请查看 Include files man page 中的 nft(8) 部分。

47.2.6. 系统引导时自动载入 nftables 规则

nftables systemd 服务加载包括在 /etc/sysconfig/nftables.conf 文件中的防火墙脚本。这部分论述了如何在系统引导时载入防火墙规则。

先决条件

  • nftables 脚本存储在 /etc/nftables/ 目录中。

流程

  1. 编辑 /etc/sysconfig/nftables.conf 文件。

    • 如果您在安装 nftables 软件包时增强了 /etc/nftables/ 中创建的 *.nft 脚本,请取消对这些脚本的 include 状态的注释。
    • 如果您从头编写脚本,添加 include 语句来包括这些脚本。例如,要在 nftables 服务启动时载入 /etc/nftables/example.nft 脚本,请添加:

      include "/etc/nftables/example.nft"
  2. (可选)启动 nftables 服务在不重启系统的情况下加载防火墙规则:

    # systemctl start nftables
  3. 启用 nftables 服务。

    # systemctl enable nftables

其它资源

47.3. 创建和管理 nftables 表、链和规则

本节介绍如何显示 nftables 规则集以及如何管理它们。

47.3.1. 标准链优先级值和文本名称

当您创建一个链时,priority 可以设置整数值或标准名称,用于指定具有相同 hook 值链的顺序。

名称和值根据 xtables 在注册其默认链时使用的优先级定义。

注意

nft list chains 命令默认显示文本优先级值。您可以通过将 -y 选项传递给命令来查看数字值。

例 47.4. 使用文本值设定优先级

以下命令使用标准优先级值 50,在 example_table 中创建一个名为 example_chain 的链:

# nft add chain inet example_table example_chain { type filter hook input priority 50 \; policy accept \; }

因为优先级是一个标准值,所以您可以使用文本值:

# nft add chain inet example_table example_chain { type filter hook input priority security \; policy accept \; }

表 47.1. 标准优先级名称、系列和 hook 兼容性列表

名称系列Hook

raw

-300

ip, ip6, inet

all

mangle

-150

ip, ip6, inet

all

dstnat

-100

ip, ip6, inet

prerouting

filter

0

ip, ip6, inet, arp, netdev

all

security

50

ip, ip6, inet

all

srcnat

100

ip, ip6, inet

postrouting

所有家族都使用相同的值,但 bridge 家族使用以下值:

表 47.2. 网桥系列的标准优先级名称和 hook 兼容性

名称Hook

dstnat

-300

prerouting

filter

-200

all

out

100

output

srcnat

300

postrouting

其它资源

  • 有关您可以在链中运行的其他操作的详情,请查看 nft(8) man page 中的 Chains 部分。

47.3.2. 显示 nftables 规则集

nftables 的规则集合包含表、链和规则。本节介绍如何显示规则集。

流程

  • 要显示规则集,请输入:

    # nft list ruleset
    table inet example_table {
      chain example_chain {
        type filter hook input priority filter; policy accept;
        tcp dport http accept
        tcp dport ssh accept
      }
    }
    注意

    默认情况下,nftables 不预先创建表。因此,在没有表的情况下显示主机上设置的规则,nft list ruleset 命令不会显示输出。

47.3.3. 创建 nftables 表

nftables 中的表是包含链、规则、集合和其他对象集合的名称空间。本节介绍如何创建表。

每个表都必须定义一个地址系列。表的地址系列定义了表进程的类型。在创建表时,您可以设置以下地址系列之一:

  • ip:仅匹配 IPv4 数据包。如果没有指定地址系列,这是默认设置。
  • ip6:只匹配 IPv6 数据包。
  • inet:匹配 IPv4 和 IPv6 数据包。
  • arp:匹配 IPv4 地址解析协议(ARP)数据包。
  • bridge:与绕过桥接设备的数据包匹配。
  • netdev:与来自 ingress 的数据包匹配。

流程

  1. 使用 nft add table 命令创建新表格。例如,要创建一个名为 example_table 的表,用于处理 IPv4 和 IPv6 数据包:

    # nft add table inet example_table
  2. 另外,还可列出规则集中的所有表:

    # nft list tables
    table inet example_table

其它资源

  • 有关地址系列的详情,请查看 Address families man page 中的 nft(8) 部分。
  • 有关您可以在表中运行的其他操作的详情,请查看 nft(8) man page 中的 Tables 部分。

47.3.4. 创建 nftables 链

chains 是规则的容器。存在以下两种规则类型:

  • 基本链:您可以使用基础链作为来自网络堆栈的数据包的入口点。
  • 常规链:您可以使用常规链作为 jump 目标,并更好地组织规则。

这个步骤描述了如何在现有表中添加基本链。

先决条件

  • 已存在您要添加新链的表。

流程

  1. 使用 nft add chain 命令创建新链。例如,要在 example_table 中创建一个名为 example_chain 的链:

    # nft add chain inet example_table example_chain { type filter hook input priority 0 \; policy accept \; }
    重要

    要避免 shell 将分号解析为命令末尾,请预先填充 \ 转义字符的分号。

    这个链过滤传入的数据包。priority 参数指定 nftables 进程使用相同 hook 值链的顺序。较低优先级的值优先于优先级更高的值。policy 参数为这个链中规则设置默认操作。请注意,如果您远程登录到服务器,并且将默认策略设置为 drop,如果没有其他规则允许远程访问,则可以立即断开连接。

  2. 另外,还可以显示所有链:

    # nft list chains
    table inet example_table {
      chain example_chain {
        type filter hook input priority filter; policy accept;
      }
    }

其它资源

  • 有关地址系列的详情,请查看 Address families man page 中的 nft(8) 部分。
  • 有关您可以在链中运行的其他操作的详情,请查看 nft(8) man page 中的 Chains 部分。

47.3.5. 在 nftables 链末尾附加规则

本节解释了如何在现有 nftables chain 末尾附加规则。

先决条件

  • 您要添加该规则的链已存在。

流程

  1. 要添加新规则,使用 nft add rule 命令。例如,在 example_tableexample_chain 中添加一条规则来允许端口 22 上的 TCP 流量:

    # nft add rule inet example_table example_chain tcp dport 22 accept

    您可以选择指定服务名称而不是端口号。在这个示例中,您可以使用 ssh 而不是端口号 22。请注意,服务名称根据在 /etc/services 文件中的条目解析为端口号。

  2. 另外,还可在 example_table 中显示所有链及其规则:

    # nft list table inet example_table
    table inet example_table {
      chain example_chain {
        type filter hook input priority filter; policy accept;
        ...
        tcp dport ssh accept
      }
    }

其它资源

  • 有关地址系列的详情,请查看 Address families man page 中的 nft(8) 部分。
  • 有关您可以在规则中运行的其他操作的详情,请查看 nft(8) man page 中的 Rules 部分。

47.3.6. 在 nftables 链开头插入规则

本节解释了如何在现有 nftables chain 的开头插入规则。

先决条件

  • 您要添加该规则的链已存在。

流程

  1. 要插入新规则,使用 nft insert rule 命令。例如,要在 example_chainexample_table 中插入一个规则来允许端口 22 上的 TCP 流量:

    # nft insert rule inet example_table example_chain tcp dport 22 accept

    您还可以指定服务名称而不是端口号。在这个示例中,您可以使用 ssh 而不是端口号 22。请注意,服务名称根据在 /etc/services 文件中的条目解析为端口号。

  2. 另外,还可在 example_table 中显示所有链及其规则:

    # nft list table inet example_table
    table inet example_table {
      chain example_chain {
        type filter hook input priority filter; policy accept;
        tcp dport ssh accept
        ...
      }
    }

其它资源

  • 有关地址系列的详情,请查看 Address families man page 中的 nft(8) 部分。
  • 有关您可以在规则中运行的其他操作的详情,请查看 nft(8) man page 中的 Rules 部分。

47.3.7. 在 nftables 链的特定位置插入规则

本节解释了如何在 nftables chain 中现有规则之前和之后插入规则。这样您可以将新规则放在正确的位置。

先决条件

  • 您要添加规则的链已存在。

流程

  1. 使用 nft -a list ruleset 命令在 example_table 中显示所有链及其规则,包括它们的句柄:

    # nft -a list table inet example_table
    table inet example_table { # handle 1
      chain example_chain { # handle 1
        type filter hook input priority filter; policy accept;
        tcp dport 22 accept # handle 2
        tcp dport 443 accept # handle 3
        tcp dport 389 accept # handle 4
      }
    }

    使用 -a 显示句柄。您需要这些信息将新规则放在下一步中。

  2. example_table 中的 example_chain 链插入新规则:

    • 要在处理 3 前插入一个允许端口 636 上的 TCP 流量的规则,请输入:

      # nft insert rule inet example_table example_chain position 3 tcp dport 636 accept
    • 要添加一个规则,在处理 3 后允许端口 80 上的 TCP 流量,请输入:

      # nft add rule inet example_table example_chain position 3 tcp dport 80 accept
  3. 另外,还可在 example_table 中显示所有链及其规则:

    # nft -a list table inet example_table
    table inet example_table { # handle 1
      chain example_chain { # 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
        tcp dport 389 accept # handle 4
      }
    }

其它资源

  • 有关地址系列的详情,请查看 Address families man page 中的 nft(8) 部分。
  • 有关您可以在规则中运行的其他操作的详情,请查看 nft(8) man page 中的 Rules 部分。

47.4. 使用 nftables 配置 NAT

使用 nftables,您可以配置以下网络地址转换(NAT)类型:

  • 伪装
  • 源 NAT(SNAT)
  • 目标 NAT(DNAT)
  • 重定向

47.4.1. 不同的 NAT 类型: masquerading、source NAT、destination NAT 和 redirect

这些是不同的网络地址转换(NAT)类型:

伪装和源 NAT(SNAT)

使用以上 NAT 类型之一更改数据包的源 IP 地址。例如:互联网服务提供商不会路由私有 IP 范围,如 10.0.0.0/8。如果您在网络中使用私有 IP 范围,并且用户可以访问互联网中的服务器,请将这些范围内的数据包源 IP 地址映射到一个公共 IP 地址。

伪装和 SNAT 都非常相似。不同之处是:

  • 伪装自动使用传出接口的 IP 地址。因此,如果传出接口使用了动态 IP 地址,则使用伪装。
  • SNAT 将数据包的源 IP 地址设置为指定的 IP 地址,且不会动态查找传出接口的 IP 地址。因此,SNAT 要比伪装更快。如果传出接口使用了固定 IP 地址,则使用 SNAT。
目标 NAT(DNAT)
使用此 NAT 类型重写传入数据包的目标地址和端口。例如,如果您的网页服务器使用来自私有 IP 范围内的 IP 地址,因此无法直接从互联网访问,您可以在路由器上设置 DNAT 规则来重定向进入该服务器的流量。
重定向
这个类型是 IDT 的特殊示例,它根据链 hook 将数据包重定向到本地机器。例如,如果服务在与标准端口不同的端口中运行,您可以将标准端口传入的流量重定向到这个特定端口。

47.4.2. 使用 nftables 配置伪装

伪装使路由器动态地更改通过接口到接口 IP 地址发送的数据包的源 IP。这意味着,如果接口被分配了新的 IP,nftables 会在替换源 IP 时自动使用新的 IP。

以下流程描述了如何将通过 ens3 接口离开主机的数据包源 IP 替换为 ens3中设置的 IP。

流程

  1. 创建一个表:

    # nft add table nat
  2. 在表中添加 preroutingpostrouting 链:

    # nft -- add chain nat prerouting { type nat hook prerouting priority -100 \; }
    # nft add chain nat postrouting { type nat hook postrouting priority 100 \; }
    重要

    即使您没有向 prerouting 链添加规则,nftables 框架也要求此链与传入的数据包回复匹配。

    请注意,您必须将 -- 选项传递给 nft 命令,以避免 shell 将负优先级值解析为 nft 命令的选项。

  3. postrouting 链添加与 ens3 接口传出数据包匹配的规则:

    # nft add rule nat postrouting oifname "ens3" masquerade

47.4.3. 使用 nftables 配置源 NAT

在路由器中,源 NAT(SNAT)可让您将通过接口发送的数据包 IP 改为专门的 IP 地址。

以下流程描述了如何通过 ens3 接口将路由器离开路由器的数据包源 IP 替换为 192.0.2.1

流程

  1. 创建一个表:

    # nft add table nat
  2. 在表中添加 preroutingpostrouting 链:

    # nft -- add chain nat prerouting { type nat hook prerouting priority -100 \; }
    # nft add chain nat postrouting { type nat hook postrouting priority 100 \; }
    重要

    即使您没有向 postrouting 链添加规则,nftables 框架也要求此链与外发数据包回复匹配。

    请注意,您必须将 -- 选项传递给 nft 命令,以避免 shell 将负优先级值解析为 nft 命令的选项。

  3. postrouting chain 添加一条规则,将通过 ens3 的外向数据包的源 IP 替换为 192.0.2.1

    # nft add rule nat postrouting oifname "ens3" snat to 192.0.2.1

47.4.4. 使用 nftables 配置目标 NAT

目标 NAT 可让您将路由器中的流量重新指向无法直接从互联网访问的主机。

以下流程描述了如何将发送到路由器的端口 80443 的流量重定向到使用 192.0.2.1 IP 地址的主机。

流程

  1. 创建一个表:

    # nft add table nat
  2. 在表中添加 preroutingpostrouting 链:

    # nft -- add chain nat prerouting { type nat hook prerouting priority -100 \; }
    # nft add chain nat postrouting { type nat hook postrouting priority 100 \; }
    重要

    即使您没有向 postrouting 链添加规则,nftables 框架也要求此链与外发数据包回复匹配。

    请注意,您必须将 -- 选项传递给 nft 命令,以避免 shell 将负优先级值解析为 nft 命令的选项。

  3. prerouting chain 添加一条规则,它将发送到端口 80443ens3 接口上进入的流量重定向到使用 192.0.2.1 IP 的主机:

    # nft add rule nat prerouting iifname ens3 tcp dport { 80, 443 } dnat to 192.0.2.1
  4. 根据您的环境,添加 SNAT 或伪装规则以更改源地址:

    1. 如果 ens3 接口使用了动态 IP 地址,请添加伪装规则:

      # nft add rule nat postrouting oifname "ens3" masquerade
    2. 如果 ens3 接口使用静态 IP 地址,请添加 SNAT 规则。例如,如果 ens3 使用 198.51.100.1 IP 地址:

      # nft add rule nat postrouting oifname "ens3" snat to 198.51.100.1

47.4.5. 使用 nftables 配置重定向

redirect 功能是目标网络地址转换(DNAT)的特殊问题单,它可根据链 hook 将数据包重定向到本地机器。

以下流程描述了如何重定向发送到本地主机的端口 22 的流量并将其转发到端口 2222

流程

  1. 创建一个表:

    # nft add table nat
  2. 在表中添加 prerouting 链:

    # nft -- add chain nat prerouting { type nat hook prerouting priority -100 \; }

    请注意,您必须将 -- 选项传递给 nft 命令,以避免 shell 将负优先级值解析为 nft 命令的选项。

  3. prerouting 链添加一条规则,该规则将端口 22 中传入的流量重定向到端口 2222:

    # nft add rule nat prerouting tcp dport 22 redirect to 2222

47.5. 使用 nftables 命令中的设置

nftables 框架原生支持集合。您可以使用一个集合,例如,规则匹配多个 IP 地址、端口号、接口或其他匹配标准。

47.5.1. 在 nftables 中使用匿名集合

匿名集合包含使用逗号分开的值,比如 { 22, 80, 443 },它们直接在规则中使用。您还可以将匿名集合用于 IP 地址或其他匹配标准。

匿名集合的缺陷是,如果要更改集合,则需要替换规则。对于动态解决方案,请使用命名的集合,如 使用 nftables 中的名为 set

先决条件

  • example_chain 链和 example_table 表在 inet 系统中存在。

流程

  1. 例如,在 example_tableexample_chain 中添加一条规则,允许进入的流量进入端口 2280443

    # nft add rule inet example_table example_chain tcp dport { 22, 80, 443 } accept
  2. 另外,还可在 example_table 中显示所有链及其规则:

    # nft list table inet example_table
    table inet example_table {
      chain example_chain {
        type filter hook input priority filter; policy accept;
        tcp dport { ssh, http, https } accept
      }
    }

47.5.2. 在 nftables 中使用命名集

nftables 框架支持 mutable 命名集。命名集是一个列表或一组元素,您可以在表中的多个规则中使用。匿名集合的另外一个好处在于,您可以更新命名的集合而不必替换使用集合的规则。

当您创建一个命名集时,必须指定集合包含的元素类型。您可以设置以下类型:

  • 对于包含 IPv4 地址或范围的集合的 ipv4_addr,如 192.0.2.1192.0.2.0/24
  • 对于包含 IPv6 地址或范围的 ipv6_addr(如 2001:db8:1::12001:db8:1::1/64)的集合。
  • 对于包含介质访问控制(MAC)地址列表集合的 ether_addr,如 52:54:00:6b:66:42
  • 包含互联网协议类型列表集合的 inet_proto,如 tcp
  • 包含互联网服务列表集合的 inet_service,如 ssh
  • 包含数据包标记列表集合的 mark。数据包标记可以是任意 32 位整数值(02147483647)。

先决条件

  • example_chain chain 和 example_table 表存在。

流程

  1. 创建一个空集。以下示例为 IPv4 地址创建了一个集合:

    • 要创建可存储多个独立 IPv4 地址的集合:

      # nft add set inet example_table example_set { type ipv4_addr \; }
    • 要创建可存储 IPv4 地址范围的集合:

      # nft add set inet example_table example_set { type ipv4_addr \; flags interval \; }
    重要

    要避免 shell 认为分号作为命令结尾,您必须用反斜杠转义分号。

  2. 另外,还可创建使用该集合的规则。例如,以下命令为 example_tableexample_chain 添加了一个规则,该规则将丢弃来自 example_set 中的 IPv4 地址的所有数据包。

    # nft add rule inet example_table example_chain ip saddr @example_set drop

    因为 example_set 仍为空,所以该规则目前无效。

  3. example_set 添加 IPv4 地址:

    • 如果您创建存储单个 IPv4 地址的集合,请输入:

      # nft add element inet example_table example_set { 192.0.2.1, 192.0.2.2 }
    • 如果您创建存储 IPv4 范围的集合,请输入:

      # nft add element inet example_table example_set { 192.0.2.0-192.0.2.255 }

      当您指定 IP 地址范围时,可以使用无类别域间路由(CIDR)标记,如上例中的 192.0.2.0/24

47.6. 在 nftables 命令中使用 verdict 映射

ver 字典映射(也称为字典)启用 nft 通过将匹配条件映射到操作,根据数据包信息执行操作。

47.6.1. 在 nftables 中使用匿名映射

匿名映射是一个直接在规则中使用的 { match_criteria : action } 声明。这个语句可以包含多个用逗号分开的映射。

匿名映射的缺陷在于,如果要更改映射,则必须替换规则。对于动态解决方案,请使用命名的映射,如 第 47.6.2 节 “在 nftables 中使用命名映射” 所述。

这个示例描述了如何使用匿名映射将 IPv4 和 IPv6 协议的 TCP 和 UDP 数据包路由到不同的链,以分别计算传入的 TCP 和 UDP 数据包。

流程

  1. 创建 example_table

    # nft add table inet example_table
  2. example_table中创建 tcp_packets 链:

    # nft add chain inet example_table tcp_packets
  3. tcp_packets 添加一条计算此链中流量的规则:

    # nft add rule inet example_table tcp_packets counter
  4. example_table 中创建 udp_packets

    # nft add chain inet example_table udp_packets
  5. udp_packets 添加一条计算此链中流量的规则:

    # nft add rule inet example_table udp_packets counter
  6. 为传入的流量创建一个链。例如,在 example_table 中创建一个名为 incoming_traffic 的链,它会过滤传入的流量:

    # nft add chain inet example_table incoming_traffic { type filter hook input priority 0 \; }
  7. incoming_traffic 添加带有匿名映射的规则:

    # nft add rule inet example_table incoming_traffic ip protocol vmap { tcp : jump tcp_packets, udp : jump udp_packets }

    匿名映射区分数据包并根据协议将其发送到不同的计时链。

  8. 要列出流量计数器,显示 example_table

    # nft list table inet example_table
    table inet example_table {
      chain tcp_packets {
        counter packets 36379 bytes 2103816
      }
    
      chain udp_packets {
        counter packets 10 bytes 1559
      }
    
      chain incoming_traffic {
        type filter hook input priority filter; policy accept;
        ip protocol vmap { tcp : jump tcp_packets, udp : jump udp_packets }
      }
    }

    tcp_packetsudp_packets chain 中的计数器会显示接收的数据包和字节数。

47.6.2. 在 nftables 中使用命名映射

nftables 框架支持命名映射。您可以在表中的多个规则中使用这些映射。匿名映射的另一个优点是,您可以更新命名的映射而不替换使用该映射的规则。

当创建命名的映射时,您必须指定元素类型:

  • 对于匹配部分包含 IPv4 地址的映射的 ipv4_addr,比如 192.0.2.1
  • 对于匹配部分包含 IPv6 地址的映射的 ipv6_addr,比如 2001:db8:1::1
  • 对于匹配部分包含介质访问控制(MAC)地址的映射的 ether_addr,比如 52:54:00:6b:66:42
  • 对于匹配部分包含互联网协议类型的映射的 inet_proto,比如 tcp
  • 对于匹配部分包含互联网服务名称端口号的映射的 inet_service,比如 ssh 或者 22
  • 对于匹配部分包含数据包标记的映射的 mark。数据包标记可以是任意 32 位整数值(02147483647)。
  • 对于匹配部分包含计数器值的映射的 counter。计数器值可以是任意正 64 位整数值。
  • 对于部分匹配包含配额值的映射的 quota。配额值可以是任意正 64 位整数值。

这个示例论述了如何根据源 IP 地址允许或丢弃传入的数据包。使用命名映射,当 IP 地址和操作动态存储在映射中时,您只需要一个规则来配置这个场景。此流程还描述了如何从映射中添加和删除条目。

流程

  1. 创建表。例如,要创建一个名为 example_table 的表,用于处理 IPv4 数据包:

    # nft add table ip example_table
  2. 创建链。例如,要在 example_table 中创建一个名为 example_chain 的链:

    # nft add chain ip example_table example_chain { type filter hook input priority 0 \; }
    重要

    要避免 shell 认为分号作为命令结尾,您必须用反斜杠转义分号。

  3. 创建一个空的映射。例如,要为 IPv4 地址创建映射:

    # nft add map ip example_table example_map { type ipv4_addr : verdict \; }
  4. 创建使用该映射的规则。例如,以下命令为 example_tableexample_chain 中添加了一个规则,它被应用于 example_map 中定义的 IPv4 地址的操作:

    # nft add rule example_table example_chain ip saddr vmap @example_map
  5. example_map添加 IPv4 地址和对应操作:

    # nft add element ip example_table example_map { 192.0.2.1 : accept, 192.0.2.2 : drop }

    这个示例定义了 IPv4 地址到操作的映射。根据以上规则,防火墙接受来自 192.0.2.1 的数据包并丢弃来自 192.0.2.2 的数据包。

  6. 另外,还可添加另一个 IP 地址和 action 语句来增强映射:

    # nft add element ip example_table example_map { 192.0.2.3 : accept }
  7. (可选)从映射中删除条目:

    # nft delete element ip example_table example_map { 192.0.2.1 }
  8. 另外,还可显示规则集:

    # nft list ruleset
    table ip example_table {
      map example_map {
        type ipv4_addr : verdict
        elements = { 192.0.2.2 : drop, 192.0.2.3 : accept }
      }
    
      chain example_chain {
        type filter hook input priority filter; policy accept;
        ip saddr vmap @example_map
      }
    }

47.7. 使用 nftables 配置端口转发

端口转发可让管理员将发送到特定目的端口的数据包转发到不同的本地或者远程端口。

例如:如果您的网页服务器没有公共 IP 地址,您可以在防火墙上设置端口转发规则,该规则将防火墙上的端口 80443 传入的数据包转发到 web 服务器。使用这个防火墙规则,互联网中的用户可以使用防火墙的 IP 或主机名访问网页服务器。

47.7.1. 将传入的数据包转发到不同的本地端口

这部分论述了如何在端口 8022 中转发进入的 IPv4 数据包到本地系统的端口 22 的示例。

流程

  1. 使用 ip 地址系列创建一个名为 nat 的表:

    # nft add table ip nat
  2. 在表中添加 preroutingpostrouting 链:

    # nft -- add chain ip nat prerouting { type nat hook prerouting priority -100 \; }
    注意

    -- 选项传递给 nft 命令,以避免 shell 将负优先级值解析为 nft 命令的选项。

  3. prerouting 链添加一条规则,将端口 8022 中传入的数据包重新指向本地端口 22

    # nft add rule ip nat prerouting tcp dport 8022 redirect to :22

47.7.2. 将特定本地端口上传入的数据包转发到不同主机

您可以使用目标网络地址转换(DNAT)规则将本地端口上传入的数据包转发到远程主机。这可让互联网中的用户访问使用专用 IP 地址在主机上运行的服务。

这个步骤描述了如何在本地端口 443 中转发传入的 IPv4 数据包到使用 192.0.2.1 IP 地址的远程系统中的同一端口号。

先决条件

  • 您以 root 用户身份登录登陆到应该转发数据包的系统。

流程

  1. 使用 ip 地址系列创建一个名为 nat 的表:

    # nft add table ip nat
  2. 在表中添加 preroutingpostrouting 链:

    # nft -- add chain ip nat prerouting { type nat hook prerouting priority -100 \; }
    # nft add chain ip nat postrouting { type nat hook postrouting priority 100 \; }
    注意

    -- 选项传递给 nft 命令,以避免 shell 将负优先级值解析为 nft 命令的选项。

  3. prerouting 链添加一条规则,该规则将端口 443 中传入的数据包重新指向 192.0.2.1上的同一端口:

    # nft add rule ip nat prerouting tcp dport 443 dnat to 192.0.2.1
  4. postrouting 链添加一条规则伪装出站流量:

    # nft add rule ip daddr 192.0.2.1 masquerade
  5. 启用数据包转发:

    # echo "net.ipv4.ip_forward=1" > /etc/sysctl.d/95-IPv4-forwarding.conf
    # sysctl -p /etc/sysctl.d/95-IPv4-forwarding.conf

47.8. 使用 nftables 来限制连接数量

您可以使用 nftables 来限制连接数量,或阻止试图建立给定数量的连接以防止它们使用太多系统资源的 IP 地址。

47.8.1. 使用 nftables 限制连接数量

ct count 实用程序中的 nft 参数可让管理员限制连接数量。这个步骤描述了如何限制进入的连接的基本示例。

先决条件

  • 存在 example_table 中的基础 example_chain

流程

  1. 添加一条规则,该规则只允许从 IPv4 地址同时连接到 SSH 端口(22),并从同一 IP 拒绝所有后续连接:

    # nft add rule ip example_table example_chain tcp dport ssh meter example_meter { ip saddr ct count over 2 } counter reject
  2. 另外,还可以显示上一步中创建的 meter:

    # nft list meter ip example_table example_meter
    table ip example_table {
      meter example_meter {
        type ipv4_addr
        size 65535
        elements = { 192.0.2.1 : ct count over 2 , 192.0.2.2 : ct count over 2  }
      }
    }

    elements 条目显示目前与该规则匹配的地址。在这个示例中,elements 列出了已连接到 SSH 端口的 IP 地址。请注意,输出不会显示活跃连接的数量,或者连接是否被拒绝。

47.8.2. 在一分钟内尝试超过十个进入的 TCP 连接的 IP 地址

nftables 框架可让管理员动态更新设置。本节解释了如何使用这个功能临时阻止在一分钟内建立十个 IPv4 TCP 连接的主机。五分钟后,nftables 会自动从拒绝列表中删除 IP 地址。

流程

  1. 使用 ip 地址系列创建 filter 表:

    # nft add table ip filter
  2. input 链添加到 filter 表:

    # nft add chain ip filter input { type filter hook input priority 0 \; }
  3. denylist 表中添加名为 filter 的集合:

    # nft add set ip filter denylist { type ipv4_addr \; flags dynamic, timeout \; timeout 5m \; }

    这个命令为 IPv4 地址创建动态设置。timeout 5m 参数定义 nftables 在 5 分钟后自动删除条目。

  4. 添加一条规则,将在一分钟内试图建立十个新的 TCP 连接的主机源 IP 地址添加到 denylist 集:

    # nft add rule ip filter input ip protocol tcp ct state new, untracked limit rate over 10/minute add @denylist { ip saddr }
  5. 添加一条规则,丢弃来自在 denylist 集中列出的 IP 地址的所有连接:

    # nft add rule ip filter input ip saddr @denylist drop

其它资源

47.9. 调试 nftables 规则

nftables 框架为管理员提供了不同的选项来调试规则,并在数据包匹配时提供不同的选项。本节描述了这些选项。

47.9.1. 创建带有计数器的规则

在识别规则是否匹配时,可以使用计数器。本节描述了如何创建带有计数器的新规则。

  • 有关在现有规则中添加计数器的更多信息,请参阅在现有规则中 添加计数器。

先决条件

  • 您要添加该规则的链已存在。

流程

  1. 在链中添加使用 counter 参数的新规则。以下示例添加一个带有计数器的规则,它允许端口 22 上的 TCP 流量,并计算与这个规则匹配的数据包和网络数据的数量:

    # nft add rule inet example_table example_chain tcp dport 22 counter accept
  2. 显示计数器值:

    # nft list ruleset
    table inet example_table {
      chain example_chain {
        type filter hook input priority filter; policy accept;
        tcp dport ssh counter packets 6872 bytes 105448565 accept
      }
    }

47.9.2. 在现有规则中添加计数器

在识别规则是否匹配时,可以使用计数器。本节论述了如何在现有规则中添加计数器。

先决条件

  • 您要添加计数器的规则已存在。

流程

  1. 在链中显示规则及其句柄:

    # nft --handle list chain inet example_table example_chain
    table inet example_table {
      chain example_chain { # handle 1
        type filter hook input priority filter; policy accept;
        tcp dport ssh accept # handle 4
      }
    }
  2. 通过替换规则而不是使用 counter 参数来添加计数器。以下示例替换了上一步中显示的规则并添加计数器:

    # nft replace rule inet example_table example_chain handle 4 tcp dport 22 counter accept
  3. 显示计数器值:

    # nft list ruleset
    table inet example_table {
      chain example_chain {
        type filter hook input priority filter; policy accept;
        tcp dport ssh counter packets 6872 bytes 105448565 accept
      }
    }

47.9.3. 监控与现有规则匹配的数据包

nftables 中的追踪功能与 nft monitor 命令相结合,可让管理员显示与规则匹配的数据包。该流程描述了如何为规则启用追踪以及与本规则匹配的监控数据包。

先决条件

  • 您要添加计数器的规则已存在。

流程

  1. 在链中显示规则及其句柄:

    # nft --handle list chain inet example_table example_chain
    table inet example_table {
      chain example_chain { # handle 1
        type filter hook input priority filter; policy accept;
        tcp dport ssh accept # handle 4
      }
    }
  2. 通过替换规则而不是使用 meta nftrace set 1 参数来添加追踪功能。以下示例替换了上一步中显示的规则并启用追踪:

    # nft replace rule inet example_table example_chain handle 4 tcp dport 22 meta nftrace set 1 accept
  3. 使用 nft monitor 命令显示追踪。以下示例过滤命令的输出,仅显示包含 inet example_table example_chain 的条目:

    # nft monitor | grep "inet example_table example_chain"
    trace id 3c5eb15e inet example_table example_chain packet: iif "enp1s0" ether saddr 52:54:00:17:ff:e4 ether daddr 52:54:00:72:2f:6e ip saddr 192.0.2.1 ip daddr 192.0.2.2 ip dscp cs0 ip ecn not-ect ip ttl 64 ip id 49710 ip protocol tcp ip length 60 tcp sport 56728 tcp dport ssh tcp flags == syn tcp window 64240
    trace id 3c5eb15e inet example_table example_chain rule tcp dport ssh nftrace set 1 accept (verdict accept)
    ...
    警告

    根据启用追踪的规则数量以及匹配的流量数量,nft monitor 命令可能会产生大量输出。使用 grep 或者其他实用程序过滤输出。

47.10. 备份和恢复 nftables 规则集

这部分论述了如何将 nftables 规则备份到文件,以及从文件中恢复规则。

管理员可以使用具有规则的文件将规则传送到不同的服务器。

47.10.1. 备份将 nftables 规则设置为文件

本节论述了如何备份设置为文件的 nftables 规则。

流程

  • 备份 nftables 规则:

    • nft list ruleset 格式:

      # nft list ruleset > file.nft
    • JSON 格式:

      # nft -j list ruleset > file.json

47.10.2. 从文件中恢复 nftables 规则集

本节论述了如何恢复 nftables 规则集。

流程

  • 恢复 nftables 规则:

    • 如果要恢复的文件为 nft list ruleset 格式或包含 nft 命令:

      # nft -f file.nft
    • 如果要恢复的文件采用 JSON 格式:

      # nft -j -f file.json

47.11. 其它资源