nftables 操作手册

8 分钟
本页目录

规则存在、命中、最终放行是三个判断。先确定流量走 input、forward 还是 output,再确定谁拥有这些表。以下配置是教学片段,不能直接覆盖混有 Docker、Tailscale、Nikki 等管理规则的主机。

当前任务入口
加了 accept 仍不通路径与判决
持久配置反复加载原子批次与替换
定位命中规则计数器与 trace
转发 / NAT地址改写与放行

先找 hook,再读规则顺序

先识别表的归属与 hook

# 列出地址族和表名,确认哪些由本手册示例管理
sudo nft list tables

inet、ip、ip6 的规则范围不同;Docker/VPN 管理的表不并入本次编辑。

# 读取所有链的 hook、priority、policy 与 handle
sudo nft -a list ruleset

-a 显示对象句柄,后续精确变更需要它;包含整个系统的视图用于判断后续链是否还能丢包,不意味着要整体覆盖。

# 聚焦自己拥有的过滤表
sudo nft list table inet ops_filter

首次安装前 NotFound 是正常前提;已存在则先判断能否由本文件完整管理。

先确认包应该走哪条路径

# 对照目标的出口、下一跳和源地址
ip route get 192.0.2.10

替换为真实目标;它查询本机选路,不直接模拟所有转发包的 mark/来源。

# 确认本机目标端口确有服务监听
ss -lntup

没有监听时新增 accept 不会创建服务;转发到别机的目标则在目标机执行这项检查。

正在呈现知识画面
accept 为什么还会被后面的链 drop

同一 input hook 上,较早 base chain 的 accept 不保证最终放行,后续链仍能 drop。forward/output 流量则不经过这两条 input 链。

外部到本机走 input;经本机转发走 forward;本机进程发出的包走 output。base chain 的 priority 数字较小先执行,相同优先级之间不依赖稳定顺序。accept 结束当前链,但后续同 hook 的 base chain 仍可能 drop;drop 通常立即终止。普通链需被 jump/goto 引用,不因名为 input 就自动收到包。

一次提交不等于自动清除旧规则

nft -f 把文件作为一个批次提交;文件只 add 时,重复执行可能叠加规则。先明确自己拥有的表和替换边界。下面创建独立 ops_filter,默认 accept,仅展示结构;不是完整安全策略,也不会替其他表放行。

table inet ops_filter {
    set admin4 {
        type ipv4_addr
        flags interval
        elements = { 192.0.2.0/24 }
    }
    chain input {
        type filter hook input priority 10; policy accept;
        ct state established,related counter accept
        iifname "lo" counter accept
        meta l4proto ipv6-icmp counter accept
        ip saddr @admin4 tcp dport 22 counter accept
    }
}

首次加载:文件、校验、提交、实测

把上面的完整示例保存为 ./ops-filter.nft,替换 admin4 网段并确认 inet ops_filter 尚不存在;此文件只管理该表。先保留当前自有表的配置与独立管理连接。

# 检查当前内核状态下该批次能否提交
sudo nft -c -f ./ops-filter.nft

-c 不实际修改规则;失败先修复语法、对象引用或内核能力。它不会验证 SSH 可达。

# 一次提交已审阅且通过检查的规则批次
sudo nft -f ./ops-filter.nft

失败读错误并重新 list,不把只成功校验当成加载成功。

# 核对实际规则、集合内容与句柄
sudo nft -a list table inet ops_filter

从独立客户端发起新 SSH/业务连接,并检查规则计数。持久化应把同一已验证文件纳入系统的加载入口,用 systemctl cat nftables.service 确认实际路径;别假定所有发行版都读同一文件。

首次确认表不存在后加载。后续若整个表都由此文件管理,创建一个批次文件:第一行 delete table inet ops_filter,后面接完整 table 定义;先 -c 再 -f。delete 要求表存在,这不是无条件幂等安装脚本。不要用全局 flush ruleset 清掉其他程序管理的 NAT、集合与链。-c 检查可提交性,不证明远程 SSH 和业务必然可达,变更后从独立连接实测并保留原配置回退。

用 handle 精确修改,用窄条件抓证据

精确修改规则:handle 不是行号

# 重新读取当前链的规则及 handle
sudo nft -a list chain inet ops_filter input

下文 42 仅表示刚核对的目标 handle;规则增删后重新读取,不能长期保存一个号码直接复用。

# 在链尾追加一个限来源与端口的示例规则
sudo nft add rule inet ops_filter input ip saddr 198.51.100.10 tcp dport 443 counter accept

add 在末尾;insert 默认在开头。先看前面是否已有终止判决,再决定位置。此规则在默认 accept 链中主要用于命中观察,不自动形成拒绝其他来源的策略。

# 按刚核对的 handle 原位替换规则
sudo nft replace rule inet ops_filter input handle 42 ip saddr 198.51.100.10 tcp dport 8443 counter accept

这是修改既有规则的选择,不应紧接上面的 add 机械执行;列出规则并用新连接确认端口变化,再同步源配置。

# 删除已确认不再需要的那条规则
sudo nft delete rule inet ops_filter input handle 42

删除后重新 list;若是临时实验,恢复原配置并检查计数/连通性。

集合元素的添加与撤销是分阶段操作

# 把已确认的管理地址加入现有集合
sudo nft 'add element inet ops_filter admin4 { 198.51.100.10 }'

引号把花括号作为 nft 语法传入;用 sudo nft list set inet ops_filter admin4 核对。地址若已被区间覆盖,先读现有元素再修改。

# 不再需要该授权地址时移除对应元素
sudo nft 'delete element inet ops_filter admin4 { 198.51.100.10 }'

这是后续撤销,不与添加同时执行;不要删除整个共享集合。

trace:设置标记、复现、再清理

以下在自有表中新建尚不存在的 trace_input,只跟踪到本机、来自指定地址的 TCP 443;它不能解释此前已被更早 hook 丢弃的包。

# 创建早于常规 input 过滤的临时追踪链
sudo nft 'add chain inet ops_filter trace_input { type filter hook input priority -301; policy accept; }'
# 仅为目标测试流量开启 nftrace
sudo nft add rule inet ops_filter trace_input ip saddr 198.51.100.10 tcp dport 443 meta nftrace set 1
# 保持此终端观察追踪事件,在另一端复现请求
sudo nft monitor trace

沿同一 trace id 读链与 verdict;没有输出先核对流量是否经过标记规则。Ctrl-C 只停止监听,不移除标记。

# 实验结束后清空这条专用临时链
sudo nft flush chain inet ops_filter trace_input

确认 trace_input 仅包含本次规则,再执行;然后单独删除空链。

# 删除已清空的临时追踪链
sudo nft delete chain inet ops_filter trace_input

再 list table 确认临时对象消失;不清空其他链或整个 ruleset。

handle 来自当前列表,删除/replace 前重新读取,不把行号当 handle。规则只有 counter 才有相应计数。monitor trace 只接收已设置 nftrace 的包,不会自动启用所有包跟踪;需要时在自己拥有的早期链加入限源地址、端口的 meta nftrace set 1,记录 handle,复现一次后删除该临时规则。输出可能包含通信地址。

DNAT 改目的地址,不代替转发许可

检查层需要满足
Routing转发开启、目标路由和回程路由成立
DNAT外来包通常在 prerouting;本机发包需考虑 output 路径
Forward filter规则按 DNAT 后地址/端口等实际字段匹配,允许所需流量
SNAT / masquerade需要时解决返回路径与源地址转换,不无条件加在所有出口
table ip ops_nat {
    chain prerouting {
        type nat hook prerouting priority dstnat; policy accept;
        iifname "wan0" ip daddr 192.0.2.20 tcp dport 8443 dnat to 10.0.0.10:443
    }
}

这是 IPv4 DNAT 片段,仅用于自主管理的网关;wan0、公网/内网地址均需替换,并另行设计 forward 与回程。NAT 绑定由连接跟踪维护,改规则后已有连接可能沿用旧映射,使用新连接验证。iptables-nft 与 Docker 原生 nftables 后端不是同一配置模式;不以 iptables=false 代替 Docker 后端设置。

Chain verdicts · Atomic replacement · NAT · Docker nftables