1 问题解析

VPN 的使用前提是需要知道对方主机 ip 地址及开放的端口。

我遇到的问题是,自用的机器 A 和服务器 B 都没有固定 IP,机器 A 在局域网里,机器 B 使用物联网卡,本身没有对外的 IP 地址,物联网厂商也不提供端口映射。

这样 A 只能使用“向日葵”类的远程桌面软件连接服务器 B,它的问题是:

  • 同一时间只能供一个连接操作
  • 远程桌面传输屏幕变化,比只传输字符的 ssh 类工具慢很多
  • 向日葵在用户登录后才能启动,所以如果远程无人职守,重启后就连不上了
  • 不能复制粘贴,拷个东西费死劲,很多命令只能手敲,界面还反应慢

我的解决方案是:

  • 使用 VPN 隧道,在一台有外部 IP 的云服务器 C 上搭建 VPN,然后用 A 和 B 同时连接 C,此时它们在同一网络中,即可使用 A 访问 B。
  • 进一步简化:只在 B 和 C 上配置 VPN,然后把 B 的端口映射成到 C 的端口,任意机器通过 C 来访问 B,这种做法的优点是不用配置 A,缺点是所有需要暴露的端口都需要映射。选择哪一种方法,主要看需求。

2 生成密钥

2.1 Easy-RSA

Easy-RSA 是一款用于生成和管理公共密钥基础设施(PKI)的开源工具。

2.2 生成密钥

(环境 CentOS)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
yum install -y openvpn 
yum install -y easy-rsa
cd /usr/share/easy-rsa/3

./easyrsa init-pki #初始化,生成pki文件夹,用于存放证书文件
./easyrsa build-ca #生成ca根证书,需要输入两次根证书密码,和 common name

./easyrsa gen-req server nopass #创建服务器端证书, nopass表示不加密私钥文件
./easyrsa sign server server #签约服务端证书

./easyrsa gen-req client1 #创建客户端证书,这里需要输入客户端PEM密码(每次连都要输密码),也可用nopass不加密
./easyrsa sign client client1 #给client端证书签名

./easyrsa gen-dh #创建Diffie-Hellman文件, 时间比较长

上面示例了一个 client 的生成方法,请据此生成多个 client,因为同一 client 只能获取一个 ip 地址,多个 client 无法同时互联。

3 服务端配置

3.1 复制密钥

1
2
3
4
5
mkdir /etc/openvpn/server/pki -p
cp -r /usr/share/easy-rsa/3/pki/ca.crt /etc/openvpn/server/pki/
cp -r /usr/share/easy-rsa/3/pki/issued/server.crt /etc/openvpn/server/pki/
cp -r /usr/share/easy-rsa/3/pki/private/server.key /etc/openvpn/server/pki/
cp -r /usr/share/easy-rsa/3/pki/dh.pem /etc/openvpn/server/pki/

创建抵御 dos 攻击的文件

1
2
openvpn --genkey --secret ta.key  
mv ta.key /etc/openvpn/server/pki/

3.2 配置文件

1
2
cp -r /usr/share/doc/openvpn-2.4.12/sample/sample-config-files server.conf /etc/openvpn/ 
# 可以过滤掉注释行:grep '^[^#|;]' server/server.conf

修改一下 server.conf,请参考:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
port 1194
proto tcp
dev tun
ca /etc/openvpn/server/pki/ca.crt
cert /etc/openvpn/server/pki/server.crt
key /etc/openvpn/server/pki/server.key # This file should be kept secret
dh /etc/openvpn/server/pki/dh.pem
client-to-client # 重要,让client间可以相互访问
server 10.9.0.0 255.255.255.0 # 10.8.0.0 与我本地docker冲突,故使用10.9...
ifconfig-pool-persist /var/log/openvpn/ipp.txt
keepalive 10 120
tls-auth /etc/openvpn/server/pki/ta.key 0 # This file is secret
cipher AES-256-CBC
persist-key
persist-tun
status /var/log/openvpn/openvpn-status.log
verb 3

测试配置文件是否正常:

1
openvpn --config /etc/openvpn/server.conf

3.3 以服务方式启动 openvpn

1
2
systemctl start openvpn@server
systemctl enable openvpn@server

查看网络状态

1
netstats -lntup|grep 设置的端口号

查看虚拟网卡,可看到 tun0 设备

1
ifconfig

3.4 注意事项

  • 注意如果在云服务器配置,请在安全策略中打开端口

4 客户端配置

4.1 基本配置

之前我们网管同学都是将下列配置文件写入一个 ovpn 文件方便大家使用,自己做就不那么麻烦了。把刚才生成的以下文件复制到客户端对应目录:

ca.crt client1.crt client1.key dh.pem ta.key

生成配置端文件 client.ovpn

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
client
dev tun
proto tcp
remote 43.140.247.164 1194
resolv-retry infinite
nobind
persist-key
persist-tun
ca /etc/openvpn/client/pki/ca.crt
cert /etc/openvpn/client/pki/client1.crt
key /etc/openvpn/client/pki/client1.key
remote-cert-tls server
tls-auth /etc/openvpn/client/pki/ta.key 1
cipher AES-256-CBC
verb 3

连接

1
sudo nohup openvpn --config tencent.ovpn &

注意事项:不同机器用不同的 client 文件,否则分配的是同一 ip,无法相互访问

4.2 配置路由

VPN 一般有两种常见用途:

  • 想利用 vpn 科学上网,在此情况下,希望所有访问都走 vpn 网路,使用上述设置即可。
  • 想利用 vpn 访问远程网络,其它网络访问,还使用之前本地网络配置,这种情况下请做以下修改:

修改 client.ovpn 配置文件,加入:

1
route-nopull # 不改变路由

然后手动设置本地路由,把对指定网段的访问指向 vpn 网关:

1
sudo ip route add 10.9.0.0/24 via 10.9.0.5

注意:每次断开再连 vpn 都请重新设置 route

说明:建议在服务器 B 设置 route-nopull,因为它是被连的一端,这样即可以保证可用,又避免影响其网络状态。

5 查看状态

5.1 查看当前哪些客户端正在连接

1
cat /var/log/openvpn/openvpn-status.log

6 进一步简化

做完上述配置,机器 A 访问机器 B 时需要先连接 vpn,还是比较麻烦,下面使用内网穿透技术,进一步简化:只在机器 C 和机器 B 间建立 openvpn 连接,然后做端口映射,把机器 B 的端口映射到机器 C 的端口。在机器 C 上做如下配置:

1
ssh -L 0.0.0.0:3302:Server_B_IP:22 -f -N root@Server_B_IP

这样就把 机器 B 的 22 端口映射到了 机器 C 的 3302 端口,我们可以在任何一台机器 (A1, A2, A3...) 上,访问机器 B,同时也不用在 A 上安装 vpn。其中设置 0.0.0.0 使任何主机都可以访问 3302 端口。

这样做的缺点是:需要对每个用到的端口做映射。

7 问题与限制

  • 在客户端设置 openvpn 时,可能影响向日葵的连接,导致需要重启向日葵

    在 ssh 正常连接服务器 B 的情况下,可以用 “ssh -X 地址”,连接 X 桌面,然后在本地操作向日葵,尽管这么操作很奇怪。

  • 机器 A 可以访问服务器 B,ssh 连接后可使用 B 所在网络的资源;但不能像直连 VPN 一样使用 B 所在的整个网络,比如安装在 A 上的 Navicat 不能通过端口直接连接 B 机器所在网络中其它机器上的数据库。

  • 经过简化配置,相当于我们可以将 服务器 B 的所有端口通过服务器 C 的地址对外暴露。由此,也可以在 服务器 B 上配置 VPN 服务,供其它机器访问 B 所在的整个网络。

8 参考

很详细的教程