一条只对一个私有子网生效的 strongSwan 连接,不会因为有人改了一行就承载互联网流量。全隧道是两端就 child SA 保护哪些地址达成的约定,每一端都相对自己来称呼对端。一端配对、另一端配错,child SA 要么起不来,要么比预期更窄。下面五项检查沿着这条约定,从客户端经网关一直走到互联网。

先检查客户端选择器

客户端用 remote_ts 描述它希望在远端保护什么,而它发出的一切都来自自己分配到的地址。对互联网流量而言,这个值必须覆盖用户实际会到达的目的地,实际上就是整个 IPv4 空间。仍然只列一个子网的客户端,无论界面上配置文件叫什么,要求的都是拆分隧道。源端通常是 dynamic,因为地址在连接时才分配。在那里写死源地址,地址池一变就会失效。

children {
    net {
        local_ts  = dynamic
        remote_ts = 0.0.0.0/0
    }
}

这个片段不是完整的连接文件,它假定认证、身份和虚拟地址池都已配置好。客户端的源选择器和服务器分配的地址仍然必须一致,否则 child SA 会以双方都没预料到的范围建立。用 swanctl --load-all 加载它,并在假定成功之前先读输出。能够解析的文件也可能因为选择器与对端提议不重叠而被拒绝。

在网关自己的这一侧检查选择器

网关从相反方向理解同一个概念。互联网目的地对网关来说是本地的,所以它的 local_ts 是宽的那个,而 remote_ts 描述客户端地址池。这个颠倒正是全隧道配置中最常见的错误,而且它能通过审阅,因为两行单看都显得合理。

children {
    rw {
        local_ts  = 0.0.0.0/0
        remote_ts = 10.77.0.0/24
    }
}

把客户端片段复制进网关文件,得到的是一对什么有用信息都没描述的选择器,因为两端都会声称自己承担同一个角色。strongSwan 转发指南明确记录客户端与网关的配对,值得在连接文件旁边读,而不是等出错之后再读。网关文件还需要 remote_ts 所指地址池的定义。指定不存在地址池的选择器会毫无怨言地加载,然后在连接时失败。

旧式 ipsec.conf 使用带不同密钥的左右角色,而 rightsubnet 这一行并不是 swanctl 取值的直接替代。把两种语法分别放在不同文件里,不要把其中一个粘进另一个。把它们混在同一个目录,正是原本可用的连接在重启后悄悄回退的原因。

检查协商出来的 child SA,而不是文件

保存下来的配置只能证明意图,仅此而已。对端可以收窄所请求的范围,已有的 child SA 也不会仅因为下面的文件改了就被替换。在改动别的任何东西之前,先确认实际安装的是什么。

swanctl --list-conns
swanctl --list-sas
ip xfrm policy list

第一条命令显示守护进程加载了什么,第二条显示已建立的 child SA,第三条显示内核将要应用的安全策略。如果文件写的是 0.0.0.0/0 而列表只显示一个子网,说明协商收窄了范围,再读文件也解释不了。终止 child、重新发起,再比较这三份输出。以同样窄范围回来的 child,是被远端而不是本地文件收窄的,这属于和另一位管理员沟通,而不是本地修改。

读策略列表里的两个方向,因为只装一个方向而不装另一个方向的隧道,是披着配置外衣的路由问题。正向和反向策略应当以镜像方式指定同一对选择器,缺了镜像说明问题在守护进程或提议里,而不在选择器文本里。

检查转发和回程

选择器只是把流量放进网关,并不会让网关继续转发这些流量。必须启用转发、防火墙必须放行该流,回复也必须找到回到客户端地址池的路。这是三项独立的要求,每一项失败时的症状都不同。缺少转发规则表现为黑洞,缺少回程路由则表现为单向隧道。

sysctl net.ipv4.ip_forward
iptables -t nat -S | grep -i masq
ip route get 1.1.1.1

私有客户端地址通常需要在离开公网接口之前做有意的源地址转换,而显式路由的分配则需要另一种处理。如果涉及 NAT,请保留实现所支持的例外,避免安全策略本应检查的流量被绕过转换。从别的网关抄来的宽泛 masquerade 规则会改动远比作者本意多得多的流。只为客户端地址池写这条规则,并留下注释说明它为何存在。

在客户端上,确认后端实际安装的路由,而不是你假设的路由。关闭了路由安装的管理器需要手工维护的路由设计,而且只要隧道变化,这份设计就得更新。重新连接之后要检查路由表,不只是在刚启动之后。

检查隧道仍未覆盖的部分

这对选择器只覆盖 IPv4,别的都不管。IPv6、DNS 以及任何应用层策略都是独立的决定,宽泛的 IPv4 选择器会默默忽略它们。承载每个 IPv4 数据包的隧道,仍然可能泄漏每一次名称查询。

明确说明预期的 DNS 路径,因为仍然通过本地解析器解析名称的全隧道会把查询泄漏到隧道之外,并破坏私有名称。如果客户端分到了 IPv6 地址,这个地址族也需要同样的处理,否则主机会优先选择隧道未保护的路径。用一个只有私有侧才能回答的名称来测试,并把公网答案视为泄漏。

resolvectl query intranet.example.internal
traceroute -n 1.1.1.1

用这两条命令,加上上面列表中协商出的选择器,确认整条链。如果 traceroute 显示本地路由器是第一跳,那么无论选择器声称什么,流量都没有走隧道。只有在所选流量被放行、转发并经由设计好的网关返回时,互联网访问才算完整。在配置里找到 0.0.0.0/0 只是这条链的起点,而不是它的证明。

相关文章