防火墙的表单只问 TCP 端口,最顺手的填法就是 50。这条规则什么都不会放行,因为 ESP 是 IP 协议号 50,而协议号不是端口。两个命名空间只是共用了一些小整数,别的毫无关系;这个错误在 IKE 完成、受保护流量始终不来之前一直看不见。

共用小整数的两个命名空间

IP 首部有一个协议字段,用来说明后面跟着什么:TCP 是 6,UDP 是 17,ICMP 是 1,ESP 是 50,AH 是 51。TCP 或 UDP 首部则带有端口号,用来在目标主机上选择进程。协议 50 和 TCP 端口 50 是互不相干的两个值,只是对话框容易让人以为可以互换。

所以端口转发规则表达不了原生 ESP。ESP 没有可转发的目标端口,而只懂端口规则的设备对这种报文根本没有词汇。正确的规则应当指明 IP 协议。任何端口对话框对这个报文都是错的工具。

这层混淆还有后半段。报文被加密后,里面的一切都不可见,包括原连接的 TCP 首部。防火墙去检查这种报文的端口,看的是外层首部,而原生情况下外层根本没有端口可查。

先原生 ESP,再谈封装替代

原生 ESP 直接作为 IP 协议承载,效率高和麻烦多是同一件事的两面。效率高是因为没有额外首部,麻烦多是因为任何做地址和端口转换的设备都跟不住它。NAT 穿透正是为此存在:把 ESP 报文包进 UDP,让转换设备有端口可改写。

Form on the wireHow it is identifiedWhat the firewall must permit
IKE negotiationUDP with a destination portUDP 500, and 4500 once traversal is detected
Native ESPIP protocol 50 in the outer headerThe protocol itself, with no port to name
ESP inside UDPUDP 4500 carrying an ESP payloadThe UDP port pair, which translation devices can track

两种形态是同一数据通道的两种部署,不是一层套一层。放行原生 ESP 不等于放行封装形态,放行 UDP 4500 也不等于放行原生流量,所以规则集要跟部署对齐,而不是碰巧把两种都覆盖上。

识别同样重要。两端在协商时决定用哪种形态,切换通常发生在它们之间探测到 NAT 的时候。因此隧道建立之后路径一变,流量就可能跑进你的规则从未覆盖的命名空间里。

查清连接实际用的是哪种形态

去问端点,别对着防火墙猜。在 Linux 上,状态条目会报告关联是否使用 UDP 封装、涉及哪些端口。抓包能从线路上回答同一个问题,两者都比读一遍规则集然后祈祷快。

ip xfrm state | grep -A3 encap
tcpdump -ni any 'ip proto 50 or udp port 4500'

第一条命令在用到 NAT 穿透时给出封装类型,没用时什么也不显示。第二条显示两种形态里哪一种真的在过接口,这能把“配置看着没错”的争论一次了结。

反向证据要放在同一步里。一条报零命中的规则是在告诉你报文从没到过它,那是路由或选择器的问题,不是防火墙的问题。

然后单独确认控制通道。IKE 有自己的端口和日志,协商完成却没有数据,正是本文所说的那段割裂。

把规则写在正确的命名空间里

协议规则和端口规则是两条不同的指令,工具必须表达你想说的那一条。nftables 里协议匹配是显式的,封装形态则另有自己的 UDP 规则。两条规则放在一起,复核时才看得出这是一对。

nft add rule inet filter input ip protocol esp accept
nft add rule inet filter input udp dport { 500, 4500 } accept

别为了让错误诊断自圆其说而放宽任何一条规则。在每个接口上对 ESP 协议全放行,或者放开任意到任意的 UDP,能压住症状,却把你本来想建立的管控全拆了。形态一旦确定,窄口径的支持流向几乎总是够用。

最后,隧道通了应用不通时,别把层搞混。到达网关的报文仍然需要转发、回程路由和放行策略,这些都不是 ESP 规则能表达的。缺端口解释的是一类具体故障,不是所有 IPsec 故障。把改规则和查路由当成两个独立结论。

把观察到的形态、新增的协议规则、事后看到的计数器记下来。三行字,下一个人就能在一分钟内分清命名空间错误和路由错误,而不是耗掉一下午。

相关文章