基于OpenVPN实现多个局域网之间点对点通信

1.OpenVPN原理

OpenVPN通过虚拟网卡技术以tun或者tap驱动建立三层ip隧道或者虚拟二层以太网来传送数据,传输协议可以使用tcp或udp,数据传输的稳定性取决于隧道终端的网络状况,延迟高的建议使用tcp协议作为底层协议

OpenVVPN传输结构中,服务端会给客户端分配一个虚拟ip,当客户端去访问一个远程ip,会通过路由机制将数据包(tun模式)或者数据帧(tap)发送到虚拟网卡上,服务程序接收该数据处理后,通过socket从外网发出,远程服务通过socket从外网接收数据处理后发送给虚拟网卡,最后被目标应用接收,这就是利用OpenVPN数据传输的一个单向传输过程,数据响应过程也是一样

OpenVPN利用OpenSSL库来对数据进行加密,提供数据传输的安全保障,同时OpenVPN提供了多种身份验证方式:预享私钥、第三方证书、用户名密码,本文采用第三方证书配合用户密码进行认证

2.应用场景

搭建OpenVPN可以方便远程办公,OpenVPN服务端的通信端口必须要能通过公网被访问,根据具体情况可以将服务端部署在具有固定ip的网段或者无固定ip的网段,但是后者就需要将OpenVPN服务端通信端口代理或者映射到公网,一般如果考虑安全可以把服务端部署在云上,在本地机器和目的局域网中的目的主机上部署客户端,通过服务端分配的虚拟ip去访问目的主机,如果为了更方便的去访问目的网络可以直接把服务端部署在目的网络中的某台主机上

3.部署结构及实现需求

本文把服务端部署在一台阿里云ECS服务器上
网络结构如下

06hui-tu-3

主机信息

ip os 服务
192.168.0.101 Win10 OpenVPN Client
192.168.0.102 Win10
192.168.43.172 Win10 OpenVPN Client
192.168.43.86 Win10
公网ip 47.10.10.10 内网:172.19.159.10 CentOS7 OpenVPN Server
172.19.159.3 CentOS7

实现需求:

  • 网段1主机可以访问网段3所有主机
  • OpenVPN服务端可以访问网段1所有主机
  • 网段2的主机可以访问网段3的所有主机
  • OpenVPN服务端可以访问网段2的所有主机
  • 网段1的所有主机可以访问网段2的OpenVPN客户端
  • 网段2的所有主机可以访问网段1的OpenVPN客户端

TIP:这里的访问是指访问主机的真实地址

4.OpenVPN服务端部署

下载相关软件

[OpenVPN Server](ftp://106.14.161.86/OpenVPN/windows_server/)   
[OpenVPN Client](ftp://106.14.161.86/OpenVPN/windows_client/lang13002-openvpn-portable-v1.0.zip)   
[esay-rsa](ftp://106.14.161.86/OpenVPN/esay-rsa/v3.0.6.tar.gz)   
[pam_sqlite](ftp://106.14.161.86/OpenVPN/pam_sqlite/pam_sqlite3.zip)

安装必要的依赖包

yum install lz4-devel lzo-devel pam-devel openssl-devel systemd-devel sqlite-devel 
perl perl-devel autoconf libaio libtool automake 

上传OpenVPN服务端包到服务器源码安装

pwd
/root/openvpn/openvpn-2.4.7
autoreconf -i -v -f
./configure --prefix=/usr/local/openvpn --enable-lzo --enable-lz4 --enable-crypto --enable-server --enable-plugins --enable-port-share --enable-iproute2 --enable-pf --enable-plugin-auth-pam --enable-pam-dlopen --enable-systemd 
make
make install

OpenVPN服务加入系统服务

sed -i  '/ExecStart/s/^/#/g' /usr/lib/systemd/system/openvpn.service
sed -i '/ExecStart/a\ExecStart=/usr/local/openvpn/sbin/openvpn --config /etc/openvpn/server/server.conf' /usr/lib/systemd/system/openvpn.service
systemctl enable openvpn
systemctl daemon-reload

5.证书生成

上传esay-rsa包到服务器解压

[root@private easy-rsa-3.0.6]# pwd
/root/openvpn/easy-rsa-3.0.6
[root@private easy-rsa-3.0.6]# cd easyrsa3
[root@private easyrsa3]# pwd
/root/openvpn/easy-rsa-3.0.6/easyrsa3
[root@private easyrsa3]# ll
total 84
-rwxrwxr-x 1 root root 48729 Feb  2  2019 easyrsa
-rw-rw-r-- 1 root root  4651 Feb  2  2019 openssl-easyrsa.cnf
drwx------ 8 root root  4096 Jun  6 11:57 pki
-rw------- 1 root root   636 Jun  4 03:34 ta.key
-rw-rw-r-- 1 root root  8576 Feb  2  2019 vars.example
drwxrwxr-x 2 root root  4096 Feb  2  2019 x509-types

生成全局配置文件vars

set_var EASYRSA_REQ_COUNTRY     "CN"
set_var EASYRSA_REQ_PROVINCE    "HUBEI"
set_var EASYRSA_REQ_CITY        "WUHAN"
set_var EASYRSA_REQ_ORG "ZJ"
set_var EASYRSA_REQ_EMAIL       "zj@test.com"
set_var EASYRSA_REQ_OU          "ZJ"
set_var EASYRSA_KEY_SIZE        2048
set_var EASYRSA_ALGO            rsa

5.1生成服务端证书

pwd
/root/openvpn/easy-rsa-3.0.6/easyrsa3
./easyrsa init-pki 
./easyrsa build-ca  ## 需要输入一个ca密码 linuxwt 通用名 along
./easyrsa gen-dh 

将生成的证书拷贝到OpenVPN配置文件所在的目录

cp pki/ca.crt /etc/openvpn/server/
cp pki/private/server.key /etc/openvpn/server/
cp pki/issued/server.crt /etc/openvpn/server/
cp pki/dh.pem /etc/openvpn/server/
cp ta.key /etc/openvpn/server/

5.2生成客户端证书

./easy-rsa build-client-full client1 nopass ##需要输入ca linuxwt,注意这里的client1是自定义的,可以修改,同时要记住ca密码

5.3生成tls证书

/usr/local/openvpn/sbin/openvpn --genkey --secret ta.key

6.添加sqlite认证

上传pam_sqlite包到服务器

pwd
/root/openvpn/pam_sqlite3
make && make install   

添加pam认证文件
cat /etc/pam.d/openvpn

auth        required    pam_sqlite3.so db=/etc/openvpn/openvpn.db table=t_user user=username passwd=password active=1 expire=expire crypt=1
account     required    pam_sqlite3.so db=/etc/openvpn/openvpn.db table=t_user user=username passwd=password active=1 expire=expire crypt=1

创建sqlite3数据库文件

sqlite3 /etc/openvpn/openvpn.db
sqlite> create table t_user (
     username text not null, 
     password text not null, 
     active int, 
     expire text
);
sqlite> .quit

创建一个用户
insert into t_user (username,password,active,expire) values ("yyl","123456",1,"2020-11-11");

删除一个用户
delete from t_user where username="huzhi";

查看表结构
cat /etc/openvpn/openvpn.db

7.服务端配置

根据不同的配置能实现不同的通信需求,同时不同的通信需求会需要在相关的主机上进行相应的配置

7.1.虚拟ip通信

这种情况也是默认配置,OpenVPN服务端会给每一个部署了客户端的主机分配一个虚拟ip,这个虚拟ip的网段在服务端的配置文件里设置

服务端配置
cat /etc/openvpn/server/server.conf

port 1194  # 默认通信端口
proto tcp # tcp底层协议
dev tun # 路由模式
topology subnet

# 下面四个是服务端证书
ca /etc/openvpn/server/ca.crt
cert /etc/openvpn/server/server.crt
key /etc/openvpn/server/server.key
dh /etc/openvpn/server/dh.pem

cipher AES-256-CBC
auth SHA512
tls-version-min 1.2
tls-cipher TLS-DHE-RSA-WITH-AES-256-GCM-SHA384:TLS-DHE-RSA-WITH-AES-128-GCM-SHA256:TLS-DHE-RSA-WITH-AES-256-CBC-SHA:TLS-DHE-RSA-WITH-CAMELLIA-256-CBC-SHA:TLS-DHE-RSA-WITH-AES-128-CBC-SHA:TLS-DHE-RSA-WITH-CAMELLIA-128-CBC-SHA

# tls认证
tls-auth /etc/openvpn/server/ta.key 0

user nobody
group nobody

server 10.8.0.0 255.255.255.0 # 服务端设置的整个VPN的虚拟网段
ifconfig-pool-persist ipp.txt # 记录每个客户端第一次登录,后面分配同样的虚拟ip给主机
;push "redirect-gateway def1 bypass-dhcp"
push "dhcp-option DNS 202.103.24.68"
push "dhcp-option DNS 8.8.8.8"
;push "route-gateway 10.200.227.114"
client-to-client # 客户端通信

keepalive 10 120
comp-lzo
compress "lz4"
persist-key
persist-tun
status /var/log/openvpn-status.log
log    /var/log/openvpn.log
verb 3 

客户端配置
cat client.opvn

client
dev tun
proto tcp
remote 47.10.10.10 1194

allow-recursive-routing

resolv-retry infinite
nobind
persist-key
persist-tun

remote-cert-tls server
auth-user-pass
auth-nocache
ca ca.crt # 服务端拷贝过来
cert client1.crt  
key client1.key

remote-cert-tls server
auth-user-pass
auth-nocache

cipher AES-256-CBC
auth SHA512
tls-version-min 1.2
tls-cipher TLS-DHE-RSA-WITH-AES-256-GCM-SHA384:TLS-DHE-RSA-WITH-AES-128-GCM-SHA256:TLS-DHE-RSA-WITH-AES-256-CBC-SHA:TLS-DHE-RSA-WITH-CAMELLIA-256-CBC-SHA:TLS-DHE-RSA-WITH-AES-128-CBC-SHA:TLS-DHE-RSA-WITH-CAMELLIA-128-CBC-SHA

tls-auth ta.key 1 # 服务端拷贝过来

comp-lzo
compress lz4
verb 3
mute 20

服务端上生成的客户端证书

pwd
/root/openvpn/easy-rsa-3.0.6/easyrsa3
/root/openvpn/easy-rsa-3.0.6/easyrsa3/pki/private/client1.key
/root/openvpn/easy-rsa-3.0.6/easyrsa3/pki/issued/client1.crt

将服务端上的证书client1.key、client1.crt、ca.crt、ta.key放到客户端的目录data\config下,每个客户端上最好只有一个客户端证书,一定要注意客户端证书名字一定要与客户端配置文件里的证书名一致

在正式进行通信测试前还需要满足下面的两点

# 开启路由转发
echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf  
echo 1 > /proc/sys/net/ipv4/ip_forward
# 开启ping功能
echo "net.ipv4.icmp_echo_ignore_all = 0" >> /etc/sysctl.conf
echo 0 > "/proc/sys/net/ipv4/icmp_echo_ignore_all"
/sbin/sysctl -p

启动OpenVPN服务
sysytemctl start openvpn
打开客户端就可以进行连接了,连接成功后可以使用虚拟ip访问虚拟局域网的各个虚拟ip了

OpenVPN服务端虚拟网段、虚拟网关

ip addr  
tun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UNKNOWN group default qlen 100
    link/none 
    inet 10.8.0.1/24 brd 10.8.0.255 scope global tun0
    
route -n  
10.8.0.0        0.0.0.0         255.255.255.0   U     0      0        0 tun0

7.2.网段1主机可以访问网段3所有主机

要达到标题需求,分三步:

  • OpenVPN客户端192.168.0.101可以访问服务端172.19.159.10
  • OpenVPN客户端192.168.0.101可以访问服务端同网段主机172.19.159.3
  • OpenVPN客户端同网段主机192.168.0.102可以访问服务端

在进行配置前请在上面两台主机192.168.0.101和192.168.0.102开启路由转发和ping功能,在172.19.159.3开启ping功能

windows开启路由转发功能可以在service里设置Routing and Remote Accsess为自动

服务端配置文件添加配置

...
push "route 172.19.144.0 255.255.240.0"
...

重启服务端后,服务端会将该路由推送到各个客户端,客户端发送给该网段(服务端内网)的数据会在下面的这条路由规则下进行传送

route print
172.19.144.0    255.255.240.0         10.8.0.1         10.8.0.2     35

这条规则的意思是从10.8.0.2这个接口发往目的网段172.19.144.0的数据包会从10.8.0.1这个网关出去
ping 172.19.159.10可以通,但是ping 172.19.159.3不通,这是因为该主机并不能识别来自10.8.0.0网段的数据,这里可以进行SNAT来实现
iptables -t nat -A POSTROUTING -s 10.8.0.0/255.255.255.0 -j SNAT --to-source 172.19.159.10
这样从地址段10.8.0.0出来的数据就变成从172.10.159.10发出的,可以被服务端其他主机识别到,这时就可以ping通172.19.159.3了

下面如果要客户端同网段的主机192.168.0.102也能够访问网段3所有主机,需要在服务端进行如下配置

...
route 192.168.0.0 255.255.255.0
client-config-dir /etc/openvpn/ccd
...
mkdri -p /etc/openvpn/ccd   
cd /etc/openvpn/ccd
echo "iroute 192.168.0.0 255.255.255.0" > client1

注意上面的client1必须与客户端上的配置文件名一致
重启服务端,这个时候可以从172.19.159.10上ping通客户端192.168.0.101,要想达到第三步需求还需要做一个操作,设置一条静态路由将192.168.0.102上发出的数据转发到客户端192.168.0.101上,可以直接转也可以在网段1的网关上设置
route add -p 172.19.144.0 mask 255.255.240.0 192.168.0.101
现在就可以从192.168.0.102上ping通网络3的所有主机了,同时可以发现从服务端172.19.159.10也能ping通192.168.0.102了,到此我们就实现了从网段1访问网段3的需求,顺便还实现了从服务端访问网段1的所有主机的需求

同理也可以实现网段2所有主机访问网段3所有主机、网段3服务端访问网段2所有主机
添加服务端配置

...
route 192.168.43.0 255.255.255.0
...
...
echo "iroute 192.168.43.0 255.255.255.0" > 
...

设置静态路由将192.168.43.86发出的数据转发到192.168.43.172
route add -p 172.19.144.0 mask 255.255.240.0 192.168.43.172

7.3.网段1主机可以访问网段2的OpenVPN客户端

要实现也需要分两步:

  • 网段1的OpenVPN客户端可以访问网段2的OpenVPN客户端
  • 网段1的其他主机可以访问网段2的OpenVPN客户端

服务端添加推送路由配置

...
push "route 192.168.0.0 255.255.255.0"
push "route 192.168.43.0 255.255.255.0"
...

重启服务端,这时网段1的客户端可以和网段2的客户端互相访问了,两者的访问都会经过服务端的一个中间隧道口

route -n
192.168.0.0     10.8.0.2        255.255.255.0   UG    0      0        0 tun0
192.168.43.0    10.8.0.2        255.255.255.0   UG    0      0        0 tun0

如果想要网段1的其他主机可以访问网段2的客户端,需要在该主机上添加路由
route add -p 192.168.43.0 mask 255.255.255.0 192.168.0.101
如果想要网段2的其他主机可以访问网段1的客户端,需要在该主机上添加路由
route add -p 192.168.0.0 mask 255.255.255.0 192.168.43.172

8.VPN子网说明

要满足服务端访问客户端网络段的所有主机必须如7中建立子网,且每一个子网只能有唯一的common name(文中为client1),不能同时在同一客户端网络段设置多个vpn子网

还有一点要特别说明要让非客户端的主机去访问服务端网络段所有主机必须建立vpn子网,单单进行路由转发数据到客户端是不够的

9.参考文章

openvpn服务端与客户端网段互通