两台 OpenVPN 服务器上的客户端能互相访问吗?
两台 OpenVPN 服务器会形成两个客户端池,而不是一张网络。要用不同的子网、双向路由和明确的信任边界在它们之间做路由。
再搭一台 OpenVPN 服务器只是多了一个客户端接入点,仅此而已。它不会把这些客户端放进第一台服务器的网络,名为 client-to-client 的选项只改变单个服务器进程内部的转发。只有当你刻意在两者之间做路由、并为每个方向指定负责人时,两个池才会变成一张网络。下文全部假定采用用户态 TUN 模式并关闭该内部转发,因此由内核来做决定。
误区:一份账号列表,就是一张网络
认证与可达性常被混为一谈,因为两者都写在服务器配置里。一个能在任意一台网关登录的用户,从外面看就像同一系统上开了两道门的用户。但在内部,每台服务器只从自己的池里分配地址,并且只为它终结的客户端安装路由。于是,网关 A 上的客户端把数据包发给网关 B 上的客户端时,请求的是一个自己的服务器从未听说过的目的地。数据包走上 A 的默认路由,离开隧道,到达一台从未被要求放行它的防火墙。
所以再加一台服务器并不会增加冗余、发现机制或共享广播域。它增加的是一个需要规划的、被路由的网络,而规划要从地址分配开始。
给每台网关各自的地址池和中转地址
两段不能与任何其他地址重叠的范围,加上主机之间一条小中转链路,就是全部地址规划。把中转放在你自己控制的私有网段上,并在任何一台服务器发放租约之前把数字写下来。
| Gateway | Client pool | Transit address |
|---|---|---|
| gateway-a | 10.64.10.0/24 | 172.28.44.1/30 |
| gateway-b | 10.64.20.0/24 | 172.28.44.2/30 |
不要为了保持配置文件一致,就忍不住给两台服务器同一个池。内核路由无法判断共享范围里某个任意地址当前属于哪台主机,于是路由表只会指向一台网关,而客户端可能挂在另一台上。接口名是本地约定,不是共享状态。这里两台主机都把隧道设备叫 tun0、把中转链路叫 transit0,这没问题,因为没有任何数据包携带这些名字。
在两侧都装上路由
每台网关都需要一条经中转地址指向对方池的路由,而且要让任意一台充当跳点,转发必须打开。先确认当前路由表,再添加条目,并通过主机的网络管理器让它持久化,而不是靠重连钩子。
sysctl net.ipv4.ip_forward
ip route add 10.64.20.0/24 via 172.28.44.2 dev transit0
ip route get 10.64.20.37
在另一台主机上对调地址池后镜像同样的配置。用无关脚本反复追加同一条路由,正是主机最后留下多份副本、下次有人读路由表时发现莫名其妙变动的原因。如果中转链路本身就是一条隧道,这条路由就指向该隧道的拓扑,而跳点必须在它重连之后依然存在。中转地址消失三十秒,池间路径也随之消失——这是值得事先说明、而不是事后才发现的设计特性。
推送客户端路由,但别假装拥有它
客户端需要一条通往远端池的路由,否则它们自己的操作系统会把数据包发给默认网关,隧道根本看不到。服务器配置正是下发它的自然位置。
push "route 10.64.20.0 255.255.255.0"
把镜像行写进另一台服务器的配置,然后重连一台测试客户端并读它的路由表。忽略推送路由的客户端需要自己那份已批准的配置,而一条永远到不了的路由不是访问控制。不要给客户端一条覆盖另一台网关整个池的 iroute。该指令是为挂在一台已连接客户端背后的子网准备的,用它来掩盖网关之间缺失的路由,只会造出 一台自以为拥有它根本到不了的地址的服务器.
证明一个请求可行,再拒绝另一个
挑一个真实操作,例如从 10.64.10.21 的客户端访问 10.64.20.37 上的 HTTPS 服务,并在两台主机上只放行它。每台防火墙在入站侧放行请求、在回程侧放行被跟踪的应答;目的端自己的服务策略仍须接受它。
iptables -A FORWARD -i tun0 -o transit0 -s 10.64.10.21 -d 10.64.20.37 -p tcp --dport 443 -j ACCEPT
iptables -A FORWARD -i transit0 -o tun0 -d 10.64.10.21 -s 10.64.20.37 -p tcp --sport 443 -m conntrack --ctstate ESTABLISHED -j ACCEPT
有双向路由时,源 NAT 既无必要、在这里还有实害:转换源地址会隐藏是哪个客户端发的请求,并让目的端基于源的策略失效。检查一下用于上外网的那条 masquerade 规则是否也顺手匹配了这条路径。然后测试拒绝。应当留在池 B 本地的服务,必须继续对池 A 不可达;同一网关内的客户端对也要测,因为你刚改过的转发策略同样作用于它们。
再修正一下心智模型:中转链路承载的是已解密的内层数据包,所以它是信任边界,而不是私有保证。如果它穿过你不控制的网络,就用一条经批准的加密隧道保护这一跳,并按其真实拓扑路由。端到端 TLS 仍然会自行认证应用,这也是内层服务应当继续使用它的原因。