NAT 检测只回答一个问题:地址或端口在传输途中是否被改变?它并不回答防火墙是否放行 IP 协议 50。完全没有转换的站点网关,也可能位于丢弃 native ESP 的策略之后,而这个需求必须被配置,而不是被发现。机制就是 ESP-in-UDP,它是一个决定,而不是自行出现的兜底方案。

检测是对地址做算术,而不是对防火墙做探测

IKEv2 携带 NAT 检测载荷,其中包含源地址、目的地址以及端口的哈希。每一侧都把收到的内容与本地看到的内容作比较,不一致就说明有东西转换了该数据包。该交换中没有任何环节去测试中间路由器是否转发协议 50。

实际后果值得直说。曾经在 native ESP 下正常工作的隧道,不会自己发现新近收紧的防火墙,它的下一次尝试会在数据阶段失败,而 IKE 仍然成功。你会看到已建立的 IKE 安全关联和 没有任何受保护流量,看起来像路由故障,直到你在线路上抓包。

因此,封装是你所配置连接的一种属性,而不是对等端根据网络行为协商出来的结果。

encap 如何改变交换

在通过 swanctl.conf 管理的现代 strongSwan 部署中,连接上的 encap = yes 设置会影响 NAT 检测交换,使双方都为 ESP 使用 UDP 封装。受保护的数据包随后在 UDP 内传输,通常使用端口 4500,两端必须就这一安排达成一致。

connections {
    site {
        encap = yes
        # existing approved peer, authentication and child settings
    }
}

该片段属于真实的连接定义,并不是完整的站点配置。保留你已经在运行的 IPsec 对等地址、认证和 child 流量选择器;唯一新增的键改变的是受保护数据的封装方式,而不是它保护什么。

strongSwan 的 NAT-traversal 指南 描述了这一行为,包括在启用 MOBIKE 时与端口浮动之间的交互。

使用属于你所运行后端的那一个设置

strongSwan 有两套配置后端,两者的键并不相同。swanctl.conf 连接使用 encap,而传统的 ipsec.conf 连接使用 forceencaps。把一个后端的关键字放进另一个后端,得到的配置会毫无怨言地加载成功,却什么也不改变,这比报错更糟。

在编辑任何内容之前,先确认实际运行的是哪个守护进程。许多发行版里两个文件都存在,因为其中一个是早先迁移留下的,而真正生效的是服务单元所启动的那个。要去读那个单元,而不是根据文件时间戳猜测。这里花五秒做的检查,可以免得你花几个小时去测试一个根本没被加载的设置。

通过连接的所有者应用改动,并在受控窗口内重新建立关联。已经安装的 child 会保持当前的封装方式,直到被替换,所以仅修改文件并不能证明新安排已经生效。

要验证被封装的数据,而不仅是端口

在受保护流量经过时在网关上抓包,然后与 strongSwan 自身的视图作比较:

$ sudo tcpdump -ni eth0 'udp port 4500' -c 20
$ sudo swanctl --list-sas --ike site
$ sudo ip xfrm state list

native ESP 表现为 IP 协议 50,看起来完全不像 UDP。封装后的 ESP 表现为 4500 上的 UDP 数据报,其载荷并不是 IKE。同一端口上的 IKE 流量并不能证明封装,因为 MOBIKE 可以把 IKE 移到那里,而数据路径仍保持 native,所以要读载荷,而不是端口号。

还要记住路径 MTU。封装会增加开销,原本承载 native ESP 的路径在数据包变大后可能需要调整。必需的 ICMP 处理应当已经是设计的一部分;封装并不会消除这一依赖。

当隧道内新的应用流量在抓包中于两端都表现为封装后的 ESP,并且内部服务及其返回路由仍然正常工作时,才可以接受这项改动。达不到这些,就只是一份在文件里看起来正确的配置。

相关文章