你把 route 10.60.0.0 255.255.255.0 加进服务端配置,重启,重新连接笔记本,可那个子网从这台笔记本依旧无法访问。这行配置被接受了,服务启动得很干净,日志里也看不出任何问题。实际情况是:路由装在了你编辑的那台机器上——网关现在以为自己能到达那个前缀,而客户端从未被告知任何事。位置就是全部问题所在,同一行配置可以住在四个地方。

四个位置以及各自由谁接收

路由可以由客户端自己的配置添加,可以通过全局 push 提供给每个客户端,可以通过客户端专属文件提供给某一个客户端,也可以写进服务端自己的路由上下文。

这四种情况使用的语法几乎一样,结果却完全不同。前三种影响的是客户端往隧道里发送什么;第四种影响的是服务端如何到达某个网络,它在服务端自己需要一条路径时有用,被误当成分发手段时则有害。

写这行之前,先确定你要的是四种里的哪一种,然后确认接收方。

PlacementEffectReceiver
Client profile routeInstalled by the client at connectThat client only
Global push "route ..."Sent to every client that accepts pushed optionsAll pulling clients
Client-specific pushSent to the matching client contextOne identity
Server config route, no pushAdded to the server's own tableThe gateway itself

为什么服务端里的裸路由让人意外

管理员最先看的往往是服务端配置,而那里的一条路由指令读起来像是关于 VPN 网络的声明。其实不是。OpenVPN 把它当作在运行守护进程的机器上做本地路由安装,这与你用 ip route 做一次改动然后忘掉,是同一种性质的事。

分发要求路由被 push,而 push 要求客户端去拉取。被禁用拉取选项后启动的客户端会完全忽略这项提供。在重写服务端文件之前,先检查这次握手的两端,因为服务端日志不会抱怨:从它那一侧看,提供已经发出。被接受的路由也只在下次连接时生效,绝不会落在你当前已经建立的会话里。

一旦把两件事分开看,故障就不再神秘:服务端能到达那个前缀,客户端不能,两句话同时成立。

每客户端文件以及它们匹配的身份

客户端专属文件以服务端通过认证的身份命名,默认就是证书的通用名称。桌面客户端里好听的连接名并不是那个值,对不上就会产生一个永远不会被读取的文件。

先查清服务端实际使用的目录,再确认文件名与已认证身份完全一致,包括大小写。

grep -n 'client-config-dir' /etc/openvpn/server/server.conf
ls -l /etc/openvpn/ccd/

在该文件内部,路由仍然必须被 push,因为单客户端上下文是施加到该客户端会话上的一组选项,而不是本地路由表。那里写裸路由,和全局文件里写裸路由是同一个问题。

客户端自己的文件与掌管它的工具

放在客户端配置里的路由会在该客户端连接时安装、断开时移除,这对静态目标而言是正确的生命周期。

如果配置文件由网络管理器掌管,它可能只导入一次这些选项,然后保留自己的一份副本。之后再改下载来的文件不会改变任何东西,这正是「路由明明在,却不生效」的常见原因。

查当前生效的配置文件,而不是你下载下来的那个文件,并检查管理器显示的已导入设置。然后连接,问系统某个具体地址走的是哪条路径,而不是因为文本文件里写着路由就假定隧道赢了。

先在客户端验证,再检查回程

去问客户端自己的路由表目标地址怎么走,而不是问服务端的。查前缀里的单台主机就足以显示将被使用的接口和下一跳。

ip route get 10.60.0.10

如果查询指向隧道,说明分发成功了,剩下的问题在别处。如果它指向默认网关,那么路由没有安装、被客户端侧的选项过滤掉了,或者属于客户端并未使用的配置文件。

假定分发成功,却依然没有回应:那就检查网关上的转发、对端的回程路由,以及两者之间的任何防火墙。路由只选择路径,不授予访问权限。

相关文章