链名不是钩子:iptables 与 nftables
链只是容器;内核是否查阅它由钩子决定。改规则之前,先比较 iptables 的内置链与 nftables 的基础链。
两种防火墙都能过滤转发的 VPN 流量,只是把规则挂到报文路径上的方式不同。iptables 在表内自带内置链,所以 FORWARD 本身就是一个有名字的钩子点。nftables 把链的名字与类型、钩子、优先级分开,这意味着一条名叫 FORWARD 的普通链什么也过滤不了。比较这两种模型,就能解释为什么某个条目在一个工具里看着对,在另一个工具里却毫无作用。
链是容器。内核是否会查阅它,取决于具体实现,也取决于规则集如何把它挂上去。
两种模型,两种挂法
在 iptables 中,filter 表自带三条内置链:INPUT、FORWARD 和 OUTPUT;-N 创建的是用户自定义链,只有当流量从上述某条链跳转过来时它才生效。nftables 的手册则定义了基础链(声明自己的类型、钩子和优先级)和普通链(不声明这些)。基础链可以取任意名字,而一条名为 FORWARD 的普通链不会因为名字而获得任何属性。差别就在这里,而只展示规则文本的教程看不到这一点。
兼容后端还给出第二个理由:不要从命令名推断行为。iptables 文档指出在 iptables-nft 下有些操作并不相同,因此请把原生 nftables、iptables-nft 和旧式规则集当作三套系统,而不是同一套的三种写法。
在真实情境中读错误
表不存在、权限失败、内核能力不可用、指定的链不存在,这是四种不同的发现,只有一种与链有关。请记录工具版本、请求的地址族与表、网络命名空间,以及第一条精确的错误。然后确认规则集归哪个组件所有:防火墙服务或由产品托管的网关可能生成自己的链和顺序。在它们旁边再加一条原生链,托管策略丝毫未动,清单却更混乱。
容器的命名空间可能持有与宿主机不同的规则集,因此在错误上下文里执行的命令无法说明是哪条链看到了被转发的报文。因为看不到预期中的链就提升权限,这并不是诊断。
计数器证明的是到达,不是结论
规则计数器告诉你某条规则被到达了。它不会告诉你后面的规则放行了该操作,也不能证明所有替代路径都被拒绝。请在一条无害且已授权的流量中读取它们,结合规则顺序和链的挂接一起看,把它们当作一次观察而非结论。丢弃规则上的非零计数器意味着流量命中了它;对于没有命中的流量,它什么也没说。
内核 IP 转发是完全独立的设置。链的存在不会开启转发,熟悉的名字消失也不代表转发停止了。
描述你希望保护的流量
写下解封装之后的一次客户端请求:源、目的、地址族,以及它是在网关终止还是穿过网关。本地服务的输入和转发流量在不同位置被观察,因此需要不同的规则和不同的测试。对于桥接式 VPN,还要验证桥路径,因为 IP 层的链不会自动监管以太网帧。
改动远程网关之前,先保住独立的恢复通道。然后在同一套规则下确认预期流量与必需的拒绝。一条名字熟悉的新空链并不是修复。
比较最终归结为一个问题:对这份报文,内核实际调用的是哪个钩子?回答了这个,链名就不再重要。