防火墙规则修改不生效:规则顺序与连接状态
被编辑的规则可能排在更早的放行之后,或者被连接跟踪跳过。把两种测试分开,用新连接验证,并保留一条退路。
你改了规则、重载了规则集,隧道的行为却和之前一模一样。这个结果通常有两个原因,而且需要不同的测试。要么新规则排在 已经放行该数据包的更早规则之后,要么你观察的这次交互是连接跟踪中的既有会话,修改根本不会重新评估它。文件里的规则表达的是意图;真正决定数据包的是它遇到的活动路径。请按顺序完成下面的检查,并在过程中保留一条退路。
规则顺序和连接状态是两类不同的故障
有序链自上而下求值,第一条给出终结判定的规则就结束了这条链的遍历。把一条宽泛的放行插在精心编写的锁定规则上方,锁定规则就变成了文档:它在规则集里,计数器为零,永远不会有数据包到达。把同样的文本套用到另一条链上也证明不了什么。在 nftables 中,基础链按优先级挂接到钩子上,因此一条链中的放行并不会阻止另一条钩子或优先级上的链看到同一个数据包。
连接跟踪会产生第二种症状,从外部看完全一样。一旦某个流进入状态表,链顶部附近的 established-or-related 放行就会在你新规则被查询之前返回。NAT 映射也有同样的性质:转换在流开始时就已固定,所以修改过的 postrouting 规则不会重写已在运行的会话。这就是为什么改动对 09:00 新建的连接看起来有效,而对 08:55 某人打开的会话看起来毫无作用。
云安全组引入了第三种模型,专门绊倒只学过 iptables 的人。它们的入站规则是白名单,按集合求值而不是按首条匹配遍历,并且是有状态的;删除一条放行规则并不会终止已被跟踪的流。在没有首条匹配语义的平台上套用它,只会得出自信却错误的结论。
读正在生效的规则集,而不是你改过的文件
先从内核此刻真正执行的内容开始。一份从未被应用的已保存配置,能解释多得惊人的“行为没变”,而排除它只花一条命令:
nft list ruleset
iptables-save然后问你的规则到底有没有被走到。数据包计数器会直接回答,而且比任何关于意图的推理都可靠:
iptables -L FORWARD -v -n --line-numbers
nft list chain inet filter forward一次真实测试之后计数器仍为零的锁定规则,不是策略问题,而是位置问题。移动它只需改一行,而语法恰恰是最初那个 bug 的成因。在 iptables 中,-I 插到最前面,-A 追加到末尾;在 nftables 中,insert 把规则放到链首,而 add 追加到末尾。当一条规则必须胜出时,明确写出来,而不是信任碰巧被加载的顺序。
证明这个流是否被跟踪
在归咎于规则之前,先确认流量究竟是不是被跟踪的连接。不要整表倾倒,而是查询具体的那条流:
conntrack -L -p tcp --dport 1194
conntrack -E -p tcp --dport 1194列表给出状态与剩余超时,你就能据此区分已建立的会话和新的尝试;事件模式会随发生即时打印更新,在重新连接客户端时很有用。如果你还没动手,流就已经以已建立状态存在,那么任何过滤规则的改动都改变不了它的命运。这不是防火墙的 bug,而是有状态过滤的契约。
在生产网关上直接清空整张表这种捷径要忍住。conntrack -F 是可用的,但它同时会摧毁机器上所有被跟踪的流,包括你正在操作的管理会话和承载 shell 的隧道。确实需要时可以删除单条记录,但从新的源端口建立新流通常是更干净的实验。
有些路径不被跟踪,它们的行为正好相反。在 raw 表中被标记为 notrack 的流量、绕过常规钩子的某些隧道,以及把一条流拆到两个网关上的非对称路由,都会让数据包被逐个评估,或者两端的状态根本不会建立。如果每个数据包都在推动计数器,先怀疑这类路径,再怀疑顺序。
用保留退路的新连接来测试
在测试可能切断远程访问的规则之前,先建立恢复路径:云厂商控制台、串口控制台、带外管理地址,或者该规则匹配不到的第二块网卡。回滚定时器是合适的搭档,因为即使会话在测试中途死掉,它也会把之前的规则集恢复回来。
然后用状态不可能已经覆盖的交互来测试。断开并重连客户端,让新的五元组产生,同时观察两端:
tcpdump -ni eth0 'tcp port 1194'
journalctl -k -n 50 --no-pager两个方向都要测。阻断新的入站握手的规则仍可能放行回程,反之亦然;单向生效正是锁定只做对一半的常见原因。测试后再重连一次,确认稳定状态,而不只是改动之前那个会话的状态;还要确认合法的管理访问仍然可用。为单个端口写的规则,往往会匹配到比预期更宽的范围。
把用途和位置与规则一起记录下来
没有记录用途的规则,是没人敢删的规则。每条锁定规则旁边都应记下:它保护的那条流、它所在的链与钩子、相对更宽放行它必须保持的位置,以及证明它的那次测试。半年后有人改动周边规则集时,这条备注就是唯一提醒:这个位置是刻意的。
在关闭这次改动之前,确认四件事:运行中的规则集与文件一致;新规则在全新尝试之后计数器不为零;反向路径与管理访问仍然可用;并且你重连过一次,证明的是稳定状态,而不是改动之前那个会话的状态。以上任何一项未经验证,改动就没有完成——无论隧道当下表现如何。