需求
年初,我将原来部署在老家的机柜搬迁到了我现在家里的机房,同时在老家装了一台 N150 的小主机,维持一些基本的服务,为了将老家的设备与我现在的机房接入到同一个三层内网中,我需要异地组网的工具。
调查了一圈,最终我还是选择了我比较熟悉的 OpenVPN。
有朋友就要问了:“哎呀,为什么不用 WireGuard / Tailscale / … 呢?又轻量又安全还方便,通信开销比 OpenVPN 小多了!”
这就不得不提到我做的一个测试了,我简单测试了一下 OpenVPN 与 WireGuard 建立的隧道性能,发现基本上没有差别,并且我现在使用的运营商虽然有公网 IPv4 地址,但是会识别 WireGuard 协议并且封禁。
拓扑规划
首先规划了两个可用区:AZ-HQ 和 AZ-DBHY, AZ-HQ 是我现在家里的机房, AZ-DBHY 是老家的小主机
AZ-HQ 网段规划
| 网段 | 用途 |
|---|---|
10.0.0.0/16 | 虚拟机以及硬件设备,智能家居设备也在这个网段中,在三层交换机中再通过 vlan 划分 24 长度的子网 |
172.16.1.0/24 | 用于 Kubernetes 集群的 LoadBalancer 地址 |
192.168.2.0/24 | 用于客户端设备 |
AZ-DBHY 网段规划
| 网段 | 用途 |
|---|---|
10.1.0.0/16 | 虚拟机以及硬件设备,在软路由中再通过 vlan 划分 24 长度的子网 |
192.168.1.0/24 | 用于客户端设备 |
完整拓扑

OpenVPN Access
事实上 OpenVPN 有一个可以自托管的 Access 服务,可以实现我的需求,但是这个服务居然是闭源付费的,试用版虽然可以连接一个对等网络,但是闭源这事让人有些不快,我决定还是使用纯开源的 OpenVPN 通过配置来实现。
配置过程
基本思路
基本思路是需要在两端都配置一个 OpenVPN 的对等节点,并开启路由功能,允许将发往对端的包通过路由转发到 OpenVPN 的隧道中。
然后将从隧道中发出的包通过路由转发到网关,再经由网关到达目标主机。
在我的场景中,AZ-HQ 端需要配置一台 Linux VM 作为 OpenVPN 节点,而 AZ-DBHY 端使用 iKuai 路由中的 OpenVPN 服务即可。
加密套件生成
为了保障在公网上安全的通信,需要生成加密套件,包含以下内容:
- CA
- 服务端证书
- 客户端证书
- 加密 Key
我们使用 OpenVPN 开源的 Easy-RSA 来进行便捷的生成。
注意:为了方便演示,我使用了 nopass,实际生产使用中,建议设置密码。
初始化套件并构建 CA
easyrsa init-pki
easyrsa build-ca nopass
此时会在当前目录下生成一个 pki 目录,我将这个目录放置在 /etc/openvpn/pki。
生成服务端证书
easyrsa gen-req server nopass
easyrsa sign-req server server
生成客户端证书
注意此处的客户端名称很重要,由于需要配置对等路由,此处最好明确客户端的名称,示例中我使用 az-dbhy。
easyrsa gen-req az-dbhy nopass
easyrsa sign-req client az-dbhy
使用 openvpn 命令行生成 tls key
openvpn --genkey > ta.key
创建服务端配置文件
/etc/openvpn/server/server.conf
port 1194 # 通信端口
proto udp # 通信协议
dev tun # 使用 tun 设备,便于进行路由转发
server 10.255.255.0 255.255.255.0 # 配置隧道网段,使用的是不与两端网络有冲突的网段
# 禁用压缩
comp-lzo no
allow-compression no
# 推送路由,将服务端的网络信息公告给对端
push "route 10.0.0.0 255.255.0.0"
push "route 172.16.1.0 255.255.255.0"
push "route 192.168.2.0 255.255.255.0" # User
# 认证证书
ca /etc/openvpn/pki/ca.crt
cert /etc/openvpn/pki/issued/server.crt
key /etc/openvpn/pki/private/server.key
# 加密
tls-crypt /etc/openvpn/ta.key
cipher AES-256-GCM
auth SHA256
# 探活
keepalive 1 5
# 允许通过 SIGUSR1 重启时保持 tun 设备和 key 文件
persist-key
persist-tun
# 日志
verb 3
# 客户端配置
client-config-dir /etc/openvpn/ccd
# 启动后 hook
script-security 2
up /etc/openvpn/scripts/up.sh
这里有三个关键点需要注意:
- 需要将服务端所在网络所有的子网都公告给客户端
- 通过
client-config-dir中的配置将客户端的网络告知服务端 - 使用
up启动脚本在服务端启动后自动设置静态路由
/etc/openvpn/ccd/az-dbhy
注意:这里的文件名与客户端证书中的 CN 名称必须一致,以便 OpenVPN 服务端在客户端连接上来时找到对应的客户端配置文件
ifconfig-push 10.255.255.2 10.255.255.1 # 设置隧道设备的 ip 地址
# 设置对端的网段
iroute 10.1.0.0 255.255.0.0
iroute 192.168.1.0 255.255.255.0
/etc/openvpn/scripts/up.sh
由于以上的配置文件都只是针对 OpenVPN 隧道本身的,不会改变操作系统上的静态路由,所以需要通过 up hook 来配置转发到对端的静态路由。
#!/usr/bin/env bash
/usr/sbin/ip route add 10.1.0.0/16 via 10.255.255.2 dev tun0
/usr/sbin/ip route add 192.168.1.0/24 via 10.255.255.2 dev tun0
exit 0
启动服务端
使用 systemctl start --enable openvpn-server@server 来启动 OpenVPN 服务端。
同时需要编辑 /etc/sysctl.conf 添加 net.ipv4.ip_forward=1,来开启 Linux 的 IPv4 转发功能,使用 sysctl -w net.ipv4.ip_forward=1 临时生效。
配置客户端
在 iKuai 路由的 网络设置 > VPN 客户端 > OpenVPN 路径下点击 添加,即可开始添加一个客户端。


点击 保存 完成配置之后,即可点击 启用 来开启连接了。
爱快软路由的问题
上面的配置已经可以使两侧建立通信,但是我在实测中发现,可以从 AZ-HQ 到 AZ-DBHY 建立通信,而反过来则不行。
在 OpenVPN 的节点上通过 ping 双方的 tun 设备地址,都可以正常通信。
我尝试在服务端的 OpenVPN 服务端的 tun 设备上抓包,从 AZ-DBHY 子网尝试 ping AZ-HQ 的子网,发现收到的源地址都是 10.255.255.2,此时我就注意到可能是在 iKuai 中有默认 NAT 策略,导致 iKuai 侧传入 OpenVPN 隧道的包源地址都被替换为了 tun 设备的地址。
经过一番搜索,找到了这篇 博客,其中提到,需要创建一条特殊的 NAT 放通策略,让进入 iKuai 侧 tun 设备的包不再进行 NAT,而是直接带着原始的源地址进行路由转发。
