2. nftables基础操作

nftables 的所有配置都通过一个统一的用户态工具 nft 完成。与 iptables 时代“一个协议一个命令”(iptables、ip6tables、arptables、ebtables)不同,nft 用同一套语法同时处理 IPv4、IPv6、ARP、网桥等家族,配置对象被组织成清晰的四层结构:表(table)→ 链(chain)→ 规则(rule)→ 表达式/语句(expression/statement)。理解这四层以及它们之间的从属关系,是掌握 nftables 的第一步。

一、nft 命令行工具的基本用法

nft 的调用形式非常规整:nft [选项] 命令 对象 参数。最常用的命令是 add、create、list、delete、flush、insert、replace。其中 add 与 create 的区别值得注意:add 在对象已存在时不会报错,而 create 在对象已存在时会明确报错,因此在脚本中希望“确保新建、避免误改已有对象”时,用 create 更安全。

nft 支持两种输入方式:一是把命令直接写在命令行上,适合交互式调试;二是把多条命令写入文件后用 nft -f 文件名 批量加载,适合脚本化和持久化。批量加载时,nft 会先解析整个文件,再原子性地应用,这一点对生产环境非常重要——要么全部生效,要么全部不生效,不会出现“改了一半”的中间状态。

# 交互式:查看当前所有规则集
nft list ruleset

# 批量加载:从文件读取并原子应用
nft -f /etc/nftables.conf

# 只检查语法、不实际应用(-c 表示 check)
nft -c -f /etc/nftables.conf

二、表(table):规则的顶层容器

表是 nftables 中最高层的容器,它必须属于某个协议家族。家族决定了这张表能处理哪类流量,常见取值有:ip(IPv4)、ip6(IPv6)、inet(同时处理 IPv4 和 IPv6)、arp、bridge、netdev。选择 inet 家族可以用一张表同时覆盖双栈,是当前较推荐的做法。

创建表的基本语法是 nft add table 家族 表名。表本身不包含任何匹配逻辑,它只是一个命名空间,用来把相关的链和规则组织在一起。删除表会连同其中的所有链和规则一起删除,因此 nft delete table inet filter 是一个“清空整张表”的强操作,执行前应确认。

nft add table inet filter
nft list tables
nft list table inet filter

三、链(chain):规则的挂载点

链隶属于某张表,它决定了规则在数据包的哪个处理阶段被匹配。链分为两种类型:常规链(regular chain)和基链(base chain)。常规链不绑定任何钩子,只用于被 jump 或 goto 调用,适合把公共规则抽出来复用;基链则必须绑定到 Netfilter 的某个钩子(hook),并指定类型和优先级,才能真正“看到”流量。

基链的钩子有五个:prerouting、input、forward、output、postrouting。链类型常见有 filter、nat、route。优先级用整数或标准名表示,数值越小越早执行,例如 filter 对应 0,dstnat 对应 -100,srcnat 对应 100。创建基链的语法如下:

# 创建一条 input 钩子上的过滤基链
nft add chain inet filter input \
  '{ type filter hook input priority 0 ; policy accept ; }'

# 创建一条常规链,供 jump 复用
nft add chain inet filter tcp_checks

链的 policy 是“默认动作”,当所有规则都没有匹配到该数据包时生效,取值通常是 accept 或 drop。生产环境在规则完善后一般会把 input 链策略设为 drop,但调试阶段建议先保持 accept,避免把自己锁在门外。

四、规则(rule):匹配与动作

规则由“匹配表达式”和“执行语句”两部分组成,语法形如 nft add rule 家族 表名 链名 匹配条件 动作。规则在链中按顺序自上而下匹配,一旦命中就执行对应动作,除非动作是 jump/goto 等会改变控制流的语句。

常见匹配对象包括:协议(ip protocol tcp)、源/目的地址(ip saddr 192.168.1.0/24)、端口(tcp dport 22)、连接状态(ct state established,related)、接口(iifname "eth0")等。常见动作包括 accept、drop、reject、jump、goto、return、log、counter、masquerade、dnat、snat。多个匹配条件之间默认是逻辑与的关系。

# 允许已建立的连接
nft add rule inet filter input ct state established,related accept

# 允许回环
nft add rule inet filter input iifname "lo" accept

# 允许 SSH,并计数
nft add rule inet filter input tcp dport 22 counter accept

# 丢弃其余入站
nft add rule inet filter input drop

规则的插入位置可以用 insert(插到链首)或 add(默认追加到链尾)控制,也可以用 handle 精确定位。每条规则在创建时都会被分配一个句柄号,通过 nft -a list chain 可以看到。删除或插入到指定位置时,句柄号是关键依据:

nft -a list chain inet filter input
nft delete rule inet filter input handle 5
nft insert rule inet filter input handle 3 tcp dport 443 accept

五、规则持久化

nft 的规则默认只存在于内核内存中,重启后全部丢失。持久化的标准做法是把规则集导出为文件,再让系统在启动时加载。导出用 nft list ruleset > /etc/nftables.conf,加载用 nft -f /etc/nftables.conf。多数发行版提供 nftables.service,它会在开机时读取 /etc/nftables.conf 并应用,因此只要把导出的文件放到该路径并启用服务即可。

导出后的文件建议保留注释和空行,便于人工审阅;修改后先用 nft -c -f 做语法检查,再正式加载,可以避免因一处笔误导致整份规则集加载失败。

下面这张图把“表—链—规则”的从属关系、基链与常规链的差异,以及持久化的数据流向放在一起,便于对照理解。

nftables 对象层级与持久化路径 表 table(家族 inet / ip / ip6 / bridge …) nft add table inet filter 基链 base chain hook input · type filter · priority 0 · policy drop 常规链 regular chain 无 hook,供 jump / goto 复用 规则 rule(按顺序匹配) ① ct state established,related → accept ② iifname "lo" → accept ③ tcp dport 22 → counter accept ④ 默认 → drop(policy) 每条规则有 handle 号,用于精确删除/插入 规则 rule(被调用时执行) ① tcp flags syn → ct state new accept ② tcp dport 80,443 → accept ③ return(回到调用链) jump tcp_checks 时进入此链 jump 持久化:内核内存 ⇄ 配置文件 内核规则集(运行时) /etc/nftables.conf nftables.service 开机加载 list ruleset > nft -f 重启后自动恢复

六、一个可复用的最小配置示例

把上面的操作串起来,下面是一份可直接参考的最小配置。它创建 inet 家族的表、一条 input 基链和一条常规链,并给出常见放行规则。示例中的地址和端口均为教学假设值,实际使用时请按自己的网络环境替换。

#!/usr/sbin/nft -f
flush ruleset

table inet filter {
    chain input {
        type filter hook input priority 0; policy drop;
        ct state established,related accept
        iifname "lo" accept
        ip saddr 192.168.1.0/24 tcp dport 22 accept
        tcp dport { 80, 443 } accept
        icmp type echo-request accept
    }

    chain forward {
        type filter hook forward priority 0; policy drop;
    }

    chain output {
        type filter hook output priority 0; policy accept;
    }
}

加载后可用 nft list ruleset 查看实际生效内容,用 nft -a list chain inet filter input 查看每条规则的句柄号。若某条规则未按预期生效,先确认它是否位于正确的链和钩子上,再检查前面的规则是否已经提前匹配并执行了终止动作——nftables 的“顺序匹配、命中即停”特性,是排查规则不生效时最常被忽略的一点。

在远程管理的主机上把 input 链策略直接改为 drop 之前,务必先确认已有一条允许当前管理连接(如 SSH 源地址+端口)的 accept 规则,否则一旦加载就会立即断开连接。建议先用 nft -c -f 检查,再在可回滚的会话中应用。

掌握表、链、规则这三层对象以及 nft 的增删查改语法后,后续的集合(set)、映射(map)、 verdict map、命名对象等高级特性,都是在这套结构上做扩展。把基础操作练熟,尤其是句柄定位和批量加载,能让后续的规则维护效率明显提升。