把放行规则放在错误的位置,它就等于不存在。在 nftables 中,选 insert 还是 add 决定了新语句落在被引用规则的哪一侧,而句柄指向的是实际规则集中的那条规则,不是它的行号。位置错了,数据包会先碰到 drop。

下面的比较只用一个固定对端和一次外层监听交换。它刻意收得很窄;替换整条链是另一种改动,风险面也不同。

两种位置,两种结果

insert 把规则放在被引用规则之前。add 把它放在之后。如果被引用规则就是该流量的最终 drop,那么即使规则文本完全相同,这两条命令的结果也相反。

省略位置有自己的默认行为,这也是不要不看清锚点就从论坛照抄命令的另一个理由。锚点就是全部决定。敲命令之前先把想要的一侧写下来。在文件里 insert 和 add 只差一个词。

位置是相对于某条链中的规则而言的。把同一条语句移到另一条链或另一个 hook 优先级,会改变它能看到的包,即使规则语法看起来没问题。

句柄标识的是规则,不是行号

像 42 这样的句柄是当前规则集状态里的标识符。它不是第四十二条规则,也不会跨主机、跨重建或跨重新加载配置的管理器保持稳定。

先定位链和真正的最终 drop,再选择锚点。然后在显示 drop 的同一份列表里确认那条规则的句柄,让编号和规则一起读。从旧列表读到的句柄可能已经属于另一条规则。要在将应用改动的会话里重新读一次。

iptables --version
nft -a list ruleset

iptables 的版本说明用的是哪个后端。iptables-nft 规则集和独立管理的原生表可以共存,所以只看一个前端的列表并不自动等于完整的过滤全貌。选链之前把两个视图都查一遍。

找到真正执行 drop 的链和规则

读 input hook、链的优先级,以及拒绝这条监听流量的规则。inet 表可以同时承载两个地址族,但单条规则仍可能只匹配 IPv4。

取一个已批准的对端,用文档网段的 198.51.100.25 作为例子。经过审查的原生文件可以把外层放行放在句柄 42 之前:

insert rule inet filter input handle 42 iifname "eth0" ip saddr 198.51.100.25 udp dport { 500, 4500 } counter accept

检查之后替换地址族、表、链、句柄、入口接口和对端。示例用的是 nft 1.0.9 文档中的句柄定位形式,它应写在输入文件里;在交互输入命令时,花括号的引号处理是另一件事。

把配置归属弄清楚。会重建规则的管理器可能覆盖运行时修改,而把旧句柄写死的启动脚本要么失败,要么挂到错误的规则上。

放行有作用域,drop 是终点

放行判定会在当前路径上结束评估,但之后生效的基础链仍可能把包丢弃。drop 判定是终点。所以放行计数器上升并不能证明流量成功。

要检查所有相关 hook 优先级,而不是孤立地看一条链。包可以通过 input 过滤器,却仍被另一张表在另一个优先级上的 hook 拒绝。把所有能看到这条流量的基础链都列出来。

这条规则只覆盖外层监听交换。传输封装的 ESP、原生 ESP、IPv6 对端,以及隧道内已解密流量,都需要各自的策略。它不是完整的站点 VPN 防火墙。

先检查文件,再验证实际结果

应用之前先对审查过的文件做语法和事务检查。检查模式只报告 nft 能否解析并暂存这次改动;它不会应用,也不能证明另一个 hook 会放行。在所有者应用之前,文件始终只是提案:

nft -c -f reviewed-vpn-rule.nft

通过所有者的受控流程应用,并保留恢复通道。如果规则写错,你本想绕过的 drop 也可能切断你用来修复的路径。

改动之后观察一次新的已批准对端尝试,并确认已批准对端之外的来源仍被拒绝。把长期规则交给拥有规则集的那个管理器,因为运行时句柄定位的是当前对象,而不是跨启动标识它们。

相关文章