隧道建立起来了,客户端能 ping 通 VPN 服务器,而一跳之外机器上的共享却始终没有响应。客户端配置里没有任何东西能解释这一点,因为请求确实离开了客户端,也确实到达了。失败的是回程,而失败的设备通常是 LAN 网关,而不是 VPN 主机。追踪响应报文就能解释这一整类症状。

追响应,别追请求

VPN 服务器解密客户端报文,并在源地址字段仍保留客户端地址的情况下把它转发到 LAN。目标主机向该地址回复,此时起决定作用的是它自己的路由表。它的默认网关对隧道地址池一无所知,于是响应朝互联网服务商方向发出,并在这条路径的某处被丢弃。

在客户端上,症状是静默超时:没有拒绝,没有重置,什么都没有。共享浏览卡住,ping 报告无响应,服务器日志看起来一切正常,因为服务器该做的都做了。

把回程路由放在主机查找的位置

每台 LAN 主机都会查询自己的默认网关,所以 网关是唯一需要知道隧道地址池在哪里的设备。在那里添加一条静态路由,每台主机无需改动就能继承正确路径。

如果网关通过 DHCP 选项下发自己的地址,就用这种方式分发路由,让新机器自动获取。否则路由只存在于一个配置文件里,下一次重建就会消失。

# on the LAN gateway; tunnel pool 172.20.5.0/24, VPN server 10.60.4.10
ip route add 172.20.5.0/24 via 10.60.4.10
ip route get 172.20.5.14

为什么源 NAT 看起来修好了问题

把客户端地址池伪装在 VPN 服务器地址之后,通过从报文中去掉客户端地址来消除问题。响应于是回到服务器,而服务器早已知道如何到达隧道,一切正常。代价是内部主机再也看不出客户端是谁,每条连接都像是从同一台机器发出的。

这种取舍有时是对的,比如供应商设备无法配置静态路由时。规则不要写得比当前地址池更宽,因为按 /24 写的规则在地址池改为 /16 的那天就会失效,而第一个征兆是一批新客户端能连上却到不了任何地方。

iptables -t nat -A POSTROUTING -s 172.20.5.0/24 -o ens18 -j MASQUERADE
iptables -t nat -L POSTROUTING -n -v

云端路径需要三项设置

在 VPC 中,同样的缺口以另一种形式出现,因为子网根本没有通往客户端网段的路由。路由表需要一条指向实例网络接口的隧道地址池条目,接口还必须关闭源与目标检查,平台才会接受源地址不是自身地址的报文。安全组和网络 ACL 也必须在两个方向上放行该地址池。

每一种遗漏都会造成不同的局部故障。漏掉路由表条目,私有实例就会向一个无法把响应送回地址池的子网回复。检查仍开启时,平台会在实例已经发出响应之后把它丢弃。缺少安全组规则,请求本身根本到不了,客户端会在更早一层看到超时。

按这个顺序做检查

先确认转发和过滤策略,再去怪路由,因为不转发的主机也会以同样静默的方式失败。然后在承载隧道的接口上读取生效的反向路径设置,因为严格过滤会丢弃来自意外方向的响应。

最后在客户端 ping LAN 主机时,在那台主机上抓包。到达却没有对应响应的请求证明了回程,而从未到达的请求则把故障推回服务器或隧道。

sysctl net.ipv4.ip_forward net.ipv4.conf.all.rp_filter
tcpdump -ni any host 172.20.5.14 and icmp

源与目标检查记录在 弹性网络接口指南中。要设置在真正承载隧道流量的接口上,而不是同一实例上无关的网卡。

相关文章