双栈 VPN 地址池:在 strongSwan 中同时处理两类地址族
内层 IPv4 与 IPv6 地址池、受保护前缀和外层隧道族是三个彼此独立的决定。在称之为双栈之前,请分别规划。
网关可以同时给移动用户分配 IPv4 与 IPv6 虚拟地址,而外层隧道仍然走 IPv4。这些是彼此独立的决定,把它们当成一件事,正是双栈方案通常崩塌的地方。客户端的内层地址族、它能到达的受保护前缀,以及承载封装的外层传输,是三个彼此独立的选择。
多数混乱来自把「双栈」一词套在只有一处是双栈的设计上。一个服务可以在内部是双栈、在其外部地址上是单栈,却依然对客户端工作得很好。它做不到的是碰巧能工作。
内层两个地址族,外层一条路径
外层连接是 IKE over UDP,它需要从客户端所在之处能够到达的一个地址族。内层 IPv6 地址并不需要支持 IPv6 的运营商,因为内层数据包是外层数据包里的载荷。许多在运行的部署都在通过仅支持 IPv4 的上行链路承载 IPv6 前缀。
外层路径真正限制的是可达性与 NAT 穿越。如果网关只通告一个 IPv4 地址,处在纯 IPv6 网络中的客户端无论你配置了多少地址池都无法连接。外层地址族应当依据客户端群体来决定,而不是依据内层地址规划。
为客户端在每一族中分配一个地址
要求两族在客户端只是一行声明,前提是网关有东西可分配。具名地址池让来源与服务网段彼此分离,正是这一性质让防火墙规则在半年后仍然可读。
pools {
pool4 { addrs = 10.90.0.0/24 }
pool6 { addrs = fd42:90::/64 }
}
connections {
private-access {
version = 2
pools = pool4, pool6
local { auth = pubkey certs = gateway.pem id = vpn.example.com }
remote { auth = pubkey cacerts = ca.pem vips = 0.0.0.0, :: }
children {
private { local_ts = 10.60.0.0/16, fd42:60::/48 remote_ts = dynamic }
}
}
}
该vips值会为每一族各请求一个地址,而地址池列表决定这些地址从何而来。strongSwan 关于虚拟 IP 的文档涵盖了多次请求与地址池分配,也包括这样一点:请求特定地址的客户端只是表达一种偏好,响应方可以覆盖它。不要构建假定客户端总能拿到同一值的访问规则。
请生成你自己的唯一本地前缀,而不要照抄示例。两个都部署fd42::/16的组织在第一次互联时就会发现冲突,而排查它并不愉快。
选择器描述的是目的地,而不是源
子 SA 的本地流量选择器说明网关背后有哪些受保护网段,远端选择器则描述客户端可以作为源使用什么。虚拟地址池地址属于该策略的源侧;把它放进目的地列表是对配置的常见误读。
两类地址族都需要网关上的回程路由与允许的转发。向选择器加入前缀却没有回到地址池的路由,会产生一条能建立、能通过单向流量、却静默丢弃回应的隧道。选择器是策略,不是路由。
把地址池与服务前缀放在地址规划的不同区块中。当两者重叠时,本该到达受保护主机的路由可能匹配到地址池地址,从而消失在隧道里。
DNS 与回应路径决定它是否看起来已经完工
解析器的应答与到达解析器所用的传输无关。一个名字可以返回 A 记录、AAAA 记录或两者,而客户端会按自己的偏好规则来选择。如果内部服务发布了 AAAA 记录,却没有任何东西为那个前缀做路由,双栈网络上的客户端会先尝试 IPv6 路径,然后一直等到它失败。在此期间,隧道配置看起来毫无问题。
因此要按名字决定哪些地址族应当可用,然后检查相应前缀是否在选择器里、是否有回程路由。这是逐个服务的决定。不加区分地同时发布两种记录,正是原本只跑 IPv4 的服务开始让一半用户超时的原因。
在宣布完成之前验证两类地址族
能加载的配置不等于能工作的配置。请阅读守护进程报告的状态,确认地址池分配,然后查看客户端实际安装的路由。
swanctl --list-sas
swanctl --list-pools
ip -6 addr show scope global
ip -6 route get fd42:60::10
前两个命令显示已建立的子 SA 以及哪些地址池还有空闲地址,于是「第一个客户端能用、第十个就失败」的方案会显现出来。后两个命令显示客户端是否从池中拿到了全局 IPv6 地址,以及去往受保护前缀的流量实际会走向哪里。请把分配到的地址与你配置的地址池比对,而不是与你期望的地址比对。如果响应方按其策略选了别的值,路由与防火墙规则仍然必须与它的选择对齐。
然后每一族各测一台受保护主机,再测一个同时解析到两族的名字。单族测试对你尚未演练的那一半方案毫无说明,而缺失的路由通常就在那一半里。