串联两个 VPN:它藏住了什么,又要付出什么
嵌套隧道改变的是谁能看到哪些端点,而不是谁能看到你。让内层端点经外层隧道可达,然后检查 DNS、MTU 与故障时的行为。
两条嵌套隧道并不能让你匿名。它们只是把观测分散到两家服务商,因此任何一方都无法同时看到你的家庭地址和目的地。这是一个真实但狭窄的好处,而且它会带来路由、DNS 和 MTU 问题,这些问题在你第一次真正依赖它时就会显现。
1. 先决定第二跳用来做什么
在动手配置之前,把目标写成一句话。如果目标是让你的 ISP 看不到你访问哪些站点,一条隧道就够了。如果目标是让任何单家服务商都无法把你的地址和流量对应起来,那就需要两条。如果目标是让任何人都无法识别你,多少跳都做不到。
这条链的强度只等于较弱那家服务商的日志水平,以及两家各自留存的账户记录。付款、注册邮箱和应用遥测完全在隧道之外。
2. 让内层端点能经外层隧道到达
最常见的失败是内层隧道根本建立不起来。全隧道模式的外层连接占着默认路由,于是内层客户端去连自己端点的尝试会被外层隧道吞掉,被丢弃或陷入环路。内层端点需要一条经外层接口的明确路由,并且要在内层客户端启动之前装好。否则握手会与路由赛跑并输掉。
WireGuard 从另一个方向展示了同样的冲突。每个对端的 AllowedIPs 决定哪些目的地进入该隧道,因此一条声称拥有所有地址的内层接口也会声称拥有外层端点,除非有更具体的路由胜出。两条接口上重叠的 AllowedIPs,就是一种有一方悄悄失败的配置。
容器串联让这种分离变得明确,也让陷阱变得明显。一个拥有自己命名空间、自己默认路由、内部跑着客户端的容器,会转发主机发给它的流量;主机自己并不会开始走那条路。开启转发,为容器的隧道添加一条路由,再把服务指向容器地址。
3. 让 DNS 走同一条路
DNS 必须走在流量走的地方。如果内层服务商下发了 DNS 服务器,而主机仍在查询本地服务器,这些查询就会精确暴露隧道本想隐藏的站点。要在每条隧道接口上抓包检查,而不是相信客户端的状态面板。这里的泄漏在正常浏览时看不出来,一抓包就很明显。
分离 DNS 的设计会让这件事更难,因为本地名称的查询本来就可以合法地落在隧道之外。要主动写出例外清单,而不是靠碰巧应答的那个解析器去发现它。
4. 在用户替你发现之前先设好 MTU
MTU 是另一个可预见的受害者。两层封装各减去一个头部,而已经降低了路径 MTU 的外层隧道会让内层空间更小。症状很熟悉:小请求能成功,大传输和 TLS 握手会卡住。
ip link set dev wg1 mtu 1280 是粗暴的修法,在转发器上钳制 MSS 则更干净。重试连接根本不是修法。
5. 测试你真正会遇到的故障
故意弄断外层隧道,看会发生什么。一条在外层跳点死掉时悄悄退回直连路径的链,比没有链更糟,因为它恰好在你以为被保护的那一刻泄漏。一个能停掉所有流量的 kill switch 才是应该追求的行为。把结果记录下来,因为你不会想在事故中途重新发现它。
延迟和第二个故障点是长期成本。两跳也意味着当事情在不方便的时刻出问题时,有两条支持队列。