为什么一条 OpenVPN 路由会输给 strongSwan XFRM 策略
某条 OpenVPN 路由不能覆盖 XFRM 策略:先读路由与策略选择器,再收窄、设置例外或改用 XFRM 接口。
数据包可以走 OpenVPN 路由,却仍被 IPsec 策略截获,而这种组合除了从正确角度看之外,处处都像路由故障。Linux 先解析路由,再查询独立的安全策略数据库,因此更具体的目标路由不会挤掉匹配的 XFRM 策略。可见症状是流量离开主机后再也不到达,而两条隧道都自称健康。有三种方案可以解决,选哪一种取决于该前缀本应由两条路径中的哪一条承载。
两个决定,不是一个
路由选择回答一个范围很窄的问题:哪个下一跳和源地址应该承载这个数据包。安全策略数据库回答另一个问题:这个数据包是否需要 IPsec 保护,在哪些选择器之下。两者为同一个数据包运行,而 满足前者对后者毫无说明。所以改度量值或加静态路由都可能对症状毫无影响。
过宽的选择器是常见原因。一条保护 172.16.0.0/12 的策略同时也覆盖 172.20.8.0/24,而管理员本意可能是走 OpenVPN 隧道到达后者。意图只存在于路由表里,策略数据库从未得知。
请把它当作两条已配置保护路径之间的冲突,而不是两个度量值的较量。修正应落在作用范围配错的那一侧。
读内核真正选中的路由
从内核执行的查找开始,包括源地址,以及任何可能把它引到另一张表的策略规则。主表里的路由可能永远不被参考,只要更早的规则选中了另一张表;最长前缀匹配也只在规则选中的那张表内生效。
ip rule show
ip route show table all
ip route get 172.20.8.20 from 10.44.7.15
最关键的是最后那条命令,它同时打印选中的路由和内核会使用的源地址。把该输出与 IPsec 策略匹配的选择器对照。当路由指向 OpenVPN 接口、而策略仍匹配同一对地址时,你在不猜测的情况下复现了冲突。
在贴进工单之前,先把无关地址从输出里剔掉。生产主机的路由表包含内部拓扑,只有回答问题的那些行值得粘贴。
同时读一遍一并生效的策略
策略列表显示方向、选择器,以及决定匹配数据包如何处理的那份模板。先读出方向,再读入方向以覆盖回程流量,因为单向例外会造出一条能开始却永不完成的连接。
ip -s xfrm policy
swanctl --list-sas
把列表中的选择器与连接协商出的前缀对照。关掉 strongSwan 的路由安装并不会移除这些策略,改度量值也不会改变策略保护哪一对源和目的。这两个看似可行的做法值得尽早排除,因为人们最先尝试它们。
如果列表里没有你预期前缀的策略,问题在别处,路由表大概率说的是真话。如果列表里有一条覆盖该前缀的更宽策略,你就找到了截获点。
两条路径都合理时的三种方案
当宽前缀只是图方便而非硬性要求时,收窄 IPsec 选择器是最干净的做法。把远端流量选择器缩到真正属于 IPsec 背后的子网,可以一次性消除所有主机的冲突,代价是一次重新协商密钥,而不是新增组件。
必须在保留宽选择器时,passthrough 例外是外科手术式的做法。strongSwan 支持一条独立连接,其 child 使用 pass 模式,从而告诉内核匹配的流量不得由 IPsec 处理。loopback 远端地址可避免该连接被入站对端选中,选择器必须描述真正被授权的那一对,而不是全部。
connections {
openvpn-exception {
remote_addrs = 127.0.0.1
children {
openvpn-exception {
local_ts = 10.44.7.0/24
remote_ts = 172.20.8.0/24
mode = pass
start_action = trap
}
}
}
}
strongSwan 配置示例记录了这种策略类型以及 loopback 对端约定。第三种方案是 基于路由的 XFRM 接口设计,它把策略绑定到经由命名接口路由的流量,而不是绑定到全局选择器。这是迁移而非微调,流量仍须满足协商出的选择器。
| Option | Use it when | Cost |
|---|---|---|
| Narrow the selector | The broad prefix was convenience | A rekey and a policy review |
| Passthrough child | The broad selector must stay | One exception per intended pair |
| XFRM interface | Policy should follow routing | A design migration |
在不削弱其余部分的前提下验证例外
加载改动后,测试例外,以及一个必须保持受保护的普通目的地。检查两个方向,然后停掉 OpenVPN 隧道,确认被例外覆盖的流量会怎样。隧道断开时悄悄把该前缀明文发出的例外,应当是刻意做出的策略决定,而不是事故中才发现的事实。
记录哪个前缀走哪条路径以及原因。否则下一位看到宽选择器的工程师会重开同一场排查,而答案就躺在一封没人读的 commit 信息里。