VPN 客户端里的“允许本地网络”开关听起来像是覆盖了整栋楼里的一切。实际上它通常只是给隧道的默认路由添上几条私有地址例外,然后就到此为止。你的存储 VLAN 仍然可达,前提只是 针对该前缀的一条路由能在隧道建立后仍然存在,而在许多客户端上它并不存在。

这是路由问题而不是 VPN 设置问题,这是好消息:它有确定的答案。

“允许本地网络”究竟覆盖了什么

各客户端的实现并不相同。有的为机器所在的子网添加路由,有的添加 RFC 1918 网段,少数只把默认网关排除在外。它们都不知道你的办公室路由器还承载着存储 VLAN、实验室网段或打印机子网,因为这些前缀并不在客户端能看到的那条链路上。请阅读客户端自己的文档以了解确切行为,而不要假定这个标签描述了你的拓扑。

在全隧道设计中这一区别最为要紧:客户端有意替换默认路由,让所有流量都走 VPN。这正是该设计的全部意义,也正是本地被路由的网段随之消失的原因。它们从来不曾由自己的路由覆盖;它们一直搭乘在那条刚刚被替换掉的默认路由上。客户端无法为它不知道存在的东西做路由,它只了解挂在自己接口上的前缀。其余一切必须由懂这套拓扑的人来描述。

直连路由止于第一台路由器

一台位于 192.168.10.20/24、网关为 192.168.10.1 的工作站会自动获得 192.168.10.0/24 的直连路由。位于 192.168.20.0/24 的存储 VLAN 是另一个前缀,只能经由该网关到达,而工作站除默认路由之外没有通往它的路由。在隧道建立之前,普通默认路由恰好把存储流量送往 192.168.10.1,于是一切正常。

当 VPN 安装全隧道的默认路由时,发往 192.168.20.1 的数据包会跟随新的默认路由进入隧道。远端网关根本不知道这个前缀在哪里,于是把它们丢弃或转发到毫无用处的地方。存储 VLAN 上并没有配置错误;它的路径是借来的,而借来的东西被收回了。

诱惑在于伸手去要一个宽泛的例外,比如一对 /1 路由或第二条默认路由。要抵制它。例外越窄,就越容易解释、审计,并在网络变化时撤除。

把例外放进胜出的那张表

在同一张路由表内,/24 凭最长前缀匹配胜过 /0 或 /1,所以经由本地网关通往存储 VLAN 的具体路由就足够了,前提是这张表确实会被查询。在 Linux 上常规写法很短:

ip route add 192.168.20.0/24 via 192.168.10.1 dev eth0

策略路由会让这项保证复杂化。靠前的一条规则可能把流量送到某张表,而那张表自己的默认路由指向隧道,于是持有你 /24 的主表永远不被触及。在认定这条路由具有权威性之前先检查规则,因为错误表里一条看似正确的条目,恰好能解释一个看起来像路由损坏的故障。

然后通过客户端支持的机制或拥有该接口的网络管理器把条目持久化。在提示符下敲进去的路由会在下次重连时消失,而绑定到某个 Wi-Fi 网关的路由无法带到笔记本下次访问的网络。

名称与策略不跟随路由

修好路由恢复的是数据包,不是名称。如果本地设备是通过被推送的、对它们一无所知的解析器来解析,那么即使地址可达,挂载仍会失败。本地名称需要本地 DNS 或分离 DNS 的安排,而那是与路由例外分开的另一个决定。

策略又是另一回事。VPN 客户端的本地访问过滤器可以刻意禁止通往私有网段的流量,而一条路由并不能覆盖那条规则。在不可信网络上,私有地址也不能证明目标就是你的存储服务器,所以例外应当和路由本身一样具体。

一份可以交接的约定

完成后的设计应当读起来像一份短清单:一般互联网流量走隧道,而这些被点名的本地前缀经由本地路由器保留自己的路径。把前缀、下一跳以及与之配套的 DNS 后缀写下来,并把清单保持得足够短,让别人不必读路由表就能核对。

注意本地网段与远端网段之间的重叠。若存储 VLAN 与 VPN 后面的某个东西共享前缀,解析结果会落到更具体的那条路由上,产生的行为会以看似时断时续的方式令人困惑。给一侧重新编号,通常比永远调试这份歧义更便宜。

把这份约定测两次:一次在隧道开启时,一次在隧道关闭时。如果本地路径只在其中一种状态下有效,那这个例外还算不上约定。

相关文章