打开 VPN 客户端的路由设置,你会看到两个形状不同、却常常贴着同一张标签的控件。一个让你列出应该进入隧道的目标,其余流量继续走本地网络。另一个则假定一切都走隧道,然后问你该把什么挖回来。两者在第一次测试时都能给出可用的隧道,而且都靠往清单里加行来维护,所以除非有人装了新东西,这个差别一直看不出来。

这是默认值的选择,不是功能。在清单写到二十条之前就把它定下来。

默认值要从客户端里读,不要从宣传册上读

WireGuard 把这件事做得格外直白,因为 AllowedIPs 只是一张包含清单,别无他用。带 0.0.0.0/0, ::/0 的对端会把一切都送进隧道;带 10.20.0.0/16 的对端只把那个网段送进去。它没有排除语法,所以如果你想放过一个子网,要么把其余前缀逐一列出,要么在接口之外再加策略路由。

OpenVPN从反方向走到同一个地方:redirect-gateway def1 建立完整隧道,显式的 route 指令则添加或覆盖特定前缀。商业客户端通常把两者都藏在复选框和一张目标表后面,所以在相信标签之前,先看清表到底改的是哪一个。

要问的不是复选框叫什么名字。要问的是:一个目标不在任何一张清单里的数据包,会变成什么样。把答案写下来,其余的配置自然从它推出来。跳过这个问题,第一次说不清的故障就会由那天离客户端最近的人来定夺。

跟住一个没人列进清单的应用

在只包含的设计里,这个应用走本地网络和本地 DNS 直接出去。在只排除的设计里,它走隧道,范围取决于服务端允许什么;新的 SaaS 工具第一天就能用,还是收到一张说 VPN 把它弄坏了的工单,差别往往就在这里。

两种默认值本身都不更安全。只包含收窄了隧道承载的范围,让本地网络成为阻力最小的路径;只排除则把隧道当作正常路径,把例外当作例外,这更接近多数企业策略真正想要的。

QuestionInclude-onlyExclude-only
Unlisted new appgoes directgoes through the tunnel
A site that breaks over the VPNadd it to the listcarve it out
Internal names still resolveonly if routed and servedusually, until split DNS says otherwise
Upkeepone entry per internal serviceone entry per misbehaving public service

注意平台列出的对象是哪一类

Android 的 VpnService.Builder 把允许的应用清单和禁止的应用清单分成两个模型暴露出来,而按应用的规则是按包名而不是按目标来选。放进这种清单的浏览器会把它打开的每一个目标都带上,这和路由表是两种粒度,也没法修好一个能正常工作的应用里那一个坏掉的站点。

桌面客户端常把两类混在同一个界面里,所以要确认你要加的这一行是一个包、一个主机名,还是一个前缀。它们失败的方式不同,解决的地方也不同。主机名那一行通常变成一条路由加一个名字,而包那一行变成客户端内部的策略判断。把你加的是哪一种写下来,因为之后来排查的人一定会假定成另一种。

测失败状态,不要只测顺利路径

被排除在隧道外的应用,并不会自动获得干净的直接访问。如果客户端同时还执行了无 VPN 即断网的规则,被排除的流量可能整体被丢弃,那看起来像应用坏了,而不像路由决策。这个情况要主动去测,别等用户报上来才知道。

然后把 DNS 和 IPv6 当作两个独立项目来查。只承载 IPv4 的隧道会给支持 v6 的应用留一条绕行路,而 split DNS 决定的是:对完全不进隧道的流量,内部名字还能不能解析。跑一遍清单内的应用、清单外的应用、一次名字解析和一次重连,把结果和配置放在一起。

把默认值写在下一个人找得到的地方

把选定的默认值、例外以及它们的责任人,记录在客户端配置文件所在的同一处,因为下一位管理员会不读这段理由就加一条记录。注上日期和每条例外的原因,这样某个站点后来修好了,那条过时的挖洞就能放心删掉。没有理由的清单会一直长下去。

有人报某一个目标坏了,改例外,不要改基准。为了修一个站点就翻转默认值,等于把所有不在清单里的应用悄悄挪到隧道的另一侧。

相关文章