一个 OpenVPN 枢纽与三个 LAN:地址规划与 iroute 文件
四个互不重叠的前缀,每个分支一条内核路由,每个所有者一个 iroute。一套三站点枢纽的工作顺序,名字出错时它会悄无声息地失败。
三个私有 LAN 可以在同一个枢纽上汇合,而任何站点都无需了解其他站点的内部结构,整个设计只依赖四项简短的配置决定。让它运转的不是什么特殊的协议特性,而是一份谁都不用猜的地址规划,加上逐客户端路由,告诉枢纽哪个分支拥有哪个前缀。二者中任一出错,隧道依然会起来,而正是这一点让故障变得难以理解。
按顺序完成各个步骤。每一步都依赖上一步的规划,而且没有任何一步需要新的证书颁发机构。
从一份谁都不用猜的规划开始
在动配置文件之前,先写下四个互不重叠的前缀:枢纽自有的 LAN、每个分支的 LAN,以及分配给漫游客户端的地址池。互不重叠是让其余一切保持简单的关键,因为范围一旦交叠,路由就变成最长匹配的谜题,再多的逐客户端配置也无法修复。然后确认每台 LAN 的默认网关是哪台机器,因为不是本 LAN 网关的分支路由器必须把远端路由安装到完全不同的地方。最后确定标识每个分支的认证名称,因为这些名称就是查找逐客户端文件的键。
示例规划让地址池远离三个 LAN。你的规划会有所不同,唯一的硬性规则是不存在两个重叠的范围。
Hub LAN 172.20.0.0/24
Branch A LAN 172.21.0.0/24
Branch B LAN 172.22.0.0/24
VPN pool 10.77.0.0/24
identity branch-a owns 172.21.0.0/24
identity branch-b owns 172.22.0.0/24告诉枢纽内核各个分支在哪里
枢纽每个分支需要两项信息:一条把前缀送进隧道设备的内核路由,以及一条 iroute 条目,告诉 OpenVPN 进程哪个客户端拥有它。只有内核路由时,数据包会被送到隧道就停在那里,因为守护进程还得决定由哪个对等端接收。只有 iroute 条目时,这些数据包会留在内核里,没有可走的路由。这两行都属于守护进程真正加载的配置,在调试其他任何东西之前值得先核实这一点。
dev tun
topology subnet
server 10.77.0.0 255.255.255.0
disable-dco
route 172.21.0.0 255.255.255.0
push "route 172.20.0.0 255.255.255.0"
client-config-dir /etc/openvpn/ccd
route 172.22.0.0 255.255.255.0当内核要作为规则执行点时,保持 client-to-client 关闭。该选项在守护进程内部中继对等端之间的流量,这很方便,同时也意味着你的转发规则永远看不到这条流。下文的一切都假设内核能看到每个分支间的数据包。
给每个分支它需要的路由
每个分支都需要通往其他分支和枢纽 LAN 的路由,而不需要通往自己直连 LAN 的路由。从枢纽推送远端前缀,而不是把它们粘贴到每台路由器里,这样日后增加第四个站点只需新增一个逐客户端文件,而不是改四处。逐客户端文件还带着 iroute 行,让归属与通告留在同一个地方。按服务器的权限模型让该目录及其文件可读,否则查找会失败且没有任何提示。
# /etc/openvpn/ccd/branch-a
iroute 172.21.0.0 255.255.255.0
push "route 172.22.0.0 255.255.255.0"第四个站点遵循同样的模式:一个独立的认证名称、一个自有前缀、一条枢纽内核路由、一条 iroute 条目,以及只向获准访问它的站点发送通告。
在宣布完成之前双向测试
在三台路由器上都启用 IPv4 转发,然后确认防火墙在两个方向都放行预期的流量,因为单向测试在回程路径损坏时通过的概率比任何人预想的都高。站点之间不需要做源地址转换,而保留原始源地址正是让每个分支能把自身策略施加到请求上的原因。
从某个分支 LAN 的一台主机访问另一个分支的服务,然后反转方向,再从分支访问枢纽 LAN。这三个结果是不同的证据,任何一个都不能推出其他两个。如果分支路由器不是其 LAN 的默认网关,就在真正的网关上安装远端前缀,并且当隧道客户端需要向该 LAN 发起连接时,也要把地址池包含进去。
把规划、逐客户端文件和防火墙规则记录成一份文档,因为下一次增加站点将以此为评判依据。
这个设计能成立,是因为每一层只负责一件事:规划保持前缀唯一,内核路由搬运数据包,iroute 指出所有者,防火墙决定什么被允许。当某处出问题时,这份清单也正是排查的顺序。