秋栈博客

七月

远程办公企业级OpenVPN详细搭建指南

6
2023-02-17

介绍

OpenVPN 是一个基于 OpenSSL 库的应用层 VPN 实现。
openvpn可工作于两种模式:
  1. tun:一种是IP遂道路由模式,主要应用于点对点
  2. eth:一种是基于以太网的遂道桥模式, 应用于点对多点,有多个分支机构
OpenVpn要使用到TUN/TAP作为接口建立隧道,但需要内核支持。TUN 接口创建的是三层路由隧道,建立方便;
TAP 是二层网卡桥接隧道,即创建一个以太网桥接,相对复杂。
TUN 接口下所有的客户端处于一个完全独立的子网内,与VPN服务器所在的子网没有关系;
TAP 接口的好处相较之下则相当明显,客户端可以获得VPN服务器所处子网的 IP(即,忽略物理上的区别,可以完全将客户端看做是于VPN服务器处于同一子网的另一台机器)
检查内核是否支持TUN/TAP设备驱动,有正常的输出,则支持。
$ modinfo tun
filename:       /lib/modules/3.10.0-1160.71.1.el7.x86_64/kernel/drivers/net/tun.ko.xz
alias:          devname:net/tun
alias:          char-major-10-200
license:        GPL
author:         (C) 1999-2004 Max Krasnyansky 
description:    Universal TUN/TAP device driver
retpoline:      Y
rhelversion:    7.9
srcversion:     E26A36A927427B2BAE3FB17
depends:
intree:         Y
vermagic:       3.10.0-1160.71.1.el7.x86_64 SMP mod_unload modversions
signer:         CentOS Linux kernel signing key
sig_key:        6D:A7:C2:41:B1:C9:99:25:3F:B3:B0:36:89:C0:D1:E3:BE:27:82:E4
sig_hashalgo:   sha256
检查文件是否存在判断tun模块是否加载
$ ls /dev/net/tun
/dev/net/tun

网络拓扑图

初始化服务端

云主机配置:腾讯云轻量服务器,配置为2C-2G-4Mbps。系统为CentOS 7.9
$ hostnamectl set-hostname vpn-server
$ vim etc/bashrc
#追加一行
"PS1='\n\e[1;37m[\e[m\e[1;32m\u\e[m\e[1;33m@\e[m\e[1;35m\h\e[m \e[4m`pwd`\e[m\e[1;37m]\e[m\e[1;36m\e[m \A\n$ '
$ source /etc/bashrc 
效果如下: img
# 关闭selinux
$ setenforce 0
$ sed -ri '/^[^#]*SELINUX=/s#=.+$#=disabled#' /etc/selinux/config
# 安装iptables相关服务
$ yum -y install iptables iptables-services
# 查看内核转发是否开启
$ echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
$ sysctl -p

net.ipv4.ip_forward = 1
# 更新软件包
$ yum -y install epel-release && yum update -y

安装OpenVPN

yum -y install openvpn easy-rsa
# 移动到etc内,清理软链接
cp -r /usr/share/easy-rsa/ /etc/openvpn/easy-rsa
cd /etc/openvpn/easy-rsa/ && rm -f 3 3.0
# 在根目录下查找文件名为vars.example的文件改为vars复制到当前目录
find / -type f -name "vars.example" | xargs -i cp {} . && mv vars.example vars

证书配置

$ cd /etc/openvpn/easy-rsa/3.0.8
$ ./easyrsa clean-all
# 生成一个CA根证书,名称随意,但是不能和服务端证书或客户端证书名称相同。
$ ./easyrsa build-ca nopass
# 创建服务端证书并签约,server是服务端证书名称,可以用其它名称。
$ ./easyrsa gen-req server nopass
$ ./easyrsa sign server server
# 生成Diffle Human参数,它能保证密钥在网络中安全传输
$ ./easyrsa gen-dh
创建完成会看到如下提示:
DH parameters of size 2048 created at /etc/openvpn/easy-rsa/3.0.8/pki/dh.pem
# 生成加密文件
$ openvpn --genkey --secret /etc/openvpn/ta.key
# 创建客户端证书(无证书模式可以忽略),并签约。
$ ./easyrsa gen-req qiyue_client nopass
$ ./easyrsa sign client qiyue_client

整理证书

服务端
cd /etc/openvpn/server
cp /etc/openvpn/easy-rsa/3.0.8/pki/dh.pem /etc/openvpn/server
cp /etc/openvpn/easy-rsa/3.0.8/pki/ca.crt /etc/openvpn/server
cp /etc/openvpn/easy-rsa/3.0.8/pki/issued/server.crt /etc/openvpn/server
cp /etc/openvpn/easy-rsa/3.0.8/pki/private/server.key /etc/openvpn/server
#如下所示
$ ll
total 20
-rw------- 1 root root 1168 Feb 17 10:18 ca.crt
-rw------- 1 root root  424 Feb 17 10:17 dh.pem
-rw------- 1 root root 4545 Feb 17 10:18 server.crt
-rw------- 1 root root 1704 Feb 17 10:18 server.key
客户端
cd /etc/openvpn/client
cp /etc/openvpn/easy-rsa/3.0.8/pki/issued/qiyue_client.crt /etc/openvpn/client
cp /etc/openvpn/easy-rsa/3.0.8/pki/private/qiyue_client.key /etc/openvpn/client
cp /etc/openvpn/easy-rsa/3.0.8/pki/ca.crt /etc/openvpn/client
mv /etc/openvpn/ta.key /etc/openvpn/client
# 如下所示
$ ll
total 16
-rw------- 1 root root 4444 Feb 17 10:23 qiyue_client.crt
-rw------- 1 root root 1704 Feb 17 10:23 qiyue_client.key
-rw------- 1 root root  636 Feb 17 10:32 ta.key

查看网段

$ ifconfig
eth0: flags=4163  mtu 1500
        inet 10.0.8.6  netmask 255.255.252.0  broadcast 10.0.11.255
        inet6 fe80::5054:ff:fecc:7f7e  prefixlen 64  scopeid 0x20
        ether 52:54:00:cc:7f:7e  txqueuelen 1000  (Ethernet)
        RX packets 206357  bytes 270304849 (257.7 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 67122  bytes 6802473 (6.4 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

配置转发

也可
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p tcp --sport 1999 -j ACCEPT
# VPN网段和云主机内网网段
iptables -t nat -A POSTROUTING -s 10.0.1.0/24 -j MASQUERADE
iptables -t nat -A POSTROUTING -s 10.0.8.0/22 -j MASQUERADE

自动认证脚本

#!/bin/sh
# 此脚本将根据纯文本文件对 OpenVPN 用户进行身份验证。 
# passfile 应该只包含每个用户一行,用户名在前,后跟一个或多个空格或制表符,然后是密码。

PASSFILE="/etc/openvpn/qiyue_password_file"
LOG_FILE="/etc/openvpn/log/openvpn-password.log"
TIME_STAMP=`date "+%Y-%m-%d %T"`
###########################################################

if [ ! -r "${PASSFILE}" ]; then
  echo "${TIME_STAMP}: Could not open password file \"${PASSFILE}\" for reading." >> ${LOG_FILE}
  exit 1
fi

CORRECT_PASSWORD=`awk '!/^;/&&!/^#/&&$1=="'${username}'"{print $2;exit}' ${PASSFILE}`

if [ "${CORRECT_PASSWORD}" = "" ]; then
  echo "${TIME_STAMP}: User does not exist: username=\"${username}\", password=\"${password}\"." >> ${LOG_FILE}
  exit 1
fi

if [ "${password}" = "${CORRECT_PASSWORD}" ]; then
  echo "${TIME_STAMP}: Successful authentication: username=\"${username}\"." >> ${LOG_FILE}
  exit 0
fi

echo "${TIME_STAMP}: Incorrect password: username=\"${username}\", password=\"${password}\"." >> ${LOG_FILE}
exit 1
 

服务端配置文件

具体可以参考官网的详细参数: 服务端:https://github.com/OpenVPN/openvpn/blob/master/sample/sample-config-files/server.conf
vim /etc/openvpn/server.conf
本次示例:
#云主机内网ip
local 10.0.8.6
port 1999
proto tcp
dev tun
ca /etc/openvpn/server/ca.crt
cert /etc/openvpn/server/server.crt
key /etc/openvpn/server/server.key
dh /etc/openvpn/server/dh.pem
#记录某个Client获得的IP地址,防止openvpn重新启动后“忘记”Client曾经使用过的IP地址
ifconfig-pool-persist /etc/openvpn/ipp.txt
#给客户端分配地址池,注意:不能和VPN服务器内网网段有相同
server 10.0.1.0 255.255.255.0
# 下发给客户端的需要走VPN的网络流量,其它网段不走VPN,可正常上网。
push "route 192.168.0.0 255.255.255.0"
push "route 192.168.18.0 255.255.255.0"
push "route 10.0.8.0 255.255.252.0"
# 下发网关
push "redirect-gateway def1 bypass-dhcp"
# 下发给客户端的DNS
push "dhcp-option DNS 223.5.5.5"
push "dhcp-option DNS 223.6.6.6"
tls-auth /etc/openvpn/client/ta.key 0
#客户端之间互相通信
client-to-client
#给客户端指定固定的VPN地址
#client-config-dir /etc/openvpn/ccd
#存活时间,10秒ping一次,120 如未收到响应则视为断线
keepalive 20 120
#传输数据压缩
comp-lzo
#定义运行openvpn的用户和用户组
user root
group root
#通过keepalive检测超时后,重新启动×××,不重新读取keys,保留第一次使用的keys
persist-key
#通过keepalive检测超时后,重新启动×××,一直保持tun设备是linkup的,否则网络连接会先linkdown然后linkup。
persist-tun
#采用的加密算法
cipher AES-256-CBC
# 运行状态日志和错误日志位置
status /etc/openvpn/log/openvpn-status.log
log-append /etc/openvpn/log/openvpn.log
#相当于debug level
verb 3
mute 20
duplicate-cn
script-security 3
auth-user-pass-verify /etc/openvpn/check-password.sh via-env
username-as-common-name
auth-nocache

初始化客户端

hostnamectl set-hostname vpn-client
vim etc/bashrc
#追加一行
"PS1='\n\e[1;37m[\e[m\e[1;32m\u\e[m\e[1;33m@\e[m\e[1;35m\h\e[m \e[4m`pwd`\e[m\e[1;37m]\e[m\e[1;36m\e[m \A\n$ '
source /etc/bashrc 
setenforce 0
sed -ri '/^[^#]*SELINUX=/s#=.+$#=disabled#' /etc/selinux/config
yum -y install epel-release && yum update -y
yum -y install openvpn
 

客户端配置文件

具体可以参考官网的详细参数: 客户端配置文件:https://github.com/OpenVPN/openvpn/blob/master/sample/sample-config-files/client.conf
vim /etc/openvpn/client/qiyue_client.ovpn
本次示例:
client
remote "服务端公网IP" 1999
#设置Server的IP地址和端口,这个地方需要严格和Server端保持一致。
#remote-random #随机选择一个Server连接,否则按照顺序从上到下依次连接。该选项默认不启用。
#resolv-retry infinite #始终重新解析Server的IP地址(如果remote后面跟的是域名)
#保证Server IP地址是动态的使用DDNS动态更新DNS后,Client在自动重新连接时重新解析Server的IP地址。这样无需人为重新启动,即可重新接入VPN。
#nobind #定义在本机不邦定任何端口监听incoming数据。
proto tcp
dev tun
comp-lzo
ca ca.crt
cert qiyue_client.crt
key qiyue_client.key
tls-auth ta.key 1
route-method exe
route-delay 2
verb 3
cipher AES-256-CBC
auth-nocache
#使用账号密码验证
auth-user-pass
客户端文件验证
$ pwd
/etc/openvpn/client
$ ll
total 24
-rw-------. 1 root root 1168 Feb 17 12:31 ca.crt
-rw-------. 1 root root 4444 Feb 17 12:30 qiyue_client.crt
-rw-------. 1 root root 1704 Feb 17 12:30 qiyue_client.key
-rw-r--r--. 1 root root  787 Feb 17 11:59 qiyue_client.ovpn
-rw-------. 1 root root  636 Feb 17 12:30 ta.key
 

创建密码文件

touch /etc/openvpn/qiyue_password_file
chmod 400 /etc/openvpn/qiyue_password_file
生成随机密码:http://www.metools.info/other/o45.html
$ cat /etc/openvpn/qiyue_password_file
qiyue krspc2ag0nu2v6lr087y925rug4qndkd
user 1y4d944xm1nazqdhwedpqlnxr53d0spq
 

服务端管理

为了方便启停,采用通用的进程管理程序:supervisor。
yum install supervisor -y && systemctl enable --now supervisord
cd /etc/supervisord.d/
vim openvpn.ini
#添加如下内容
[program: openvpn]
command=/usr/sbin/openvpn --cd /etc/openvpn/ --config server.conf ;
directory=/etc/openvpn/ ; 
autorestart=true ;
autostart=true ;
stderr_logfile=/var/log/openvpn.err.log ;
stdout_logfile=/var/log/openvpn.out.log ;
environment=ASPNETCORE_ENVIRONMENT=Production ;
user=root ;
stopsignal=INT
startsecs=5 ;
#启动
supervisorctl start openvpn
#停止
supervisorctl stop openvpn
查看状态
$ ps -ef |grep openvpn
root      4856 32017  0 14:02 ?        00:00:00 /usr/sbin/openvpn --cd /etc/openvpn/ --config server.conf
root      4896  3024  0 14:02 pts/3    00:00:00 grep --color=auto openvpn
$ netstat -nltp|grep 1999
tcp        0      0 0.0.0.0:1999            0.0.0.0:*               LISTEN      32019/openvpn
如果开启后没有打开1999端口,说明开启服务失败,可能是配置文件有错,也有可能是权限不够,可以查询日志解决。

Linux客户端测试

整理文件
cd /etc/openvpn
tree
# 查看目录文件
.
├── client
│   ├── ca.crt
│   ├── qiyue
│   ├── qiyue_client.crt
│   ├── qiyue_client.key
│   ├── qiyue_client.ovpn
│   └── ta.key
└── server
启动连接
#设置连接密码:第一行用户名,第二行密码。
$ cat /etc/openvpn/client/qiyue
qiyue
krspc2ag0nu2v6lr087y925rug4qndkd
#启动连接
$ openvpn --daemon --cd /etc/openvpn/client --config qiyue_aliyun.ovpn --auth-user-pass /etc/openvpn/client/qiyue --log-append /var/log/openvpn.log
使用管理软件控制
yum install supervisor -y && systemctl enable --now supervisord
cd /etc/supervisord.d/
vim openvpn.ini
#添加如下内容
[program: openvpn]
command=/usr/sbin/openvpn --cd /etc/openvpn/client/ --config qiyue_client.ovpn --auth-user-pass qiyue --log-append /var/log/openvpn.log ;
directory=/etc/openvpn/client ; 
autorestart=true ;
autostart=true ;
stderr_logfile=/var/log/openvpn.err.log ;
stdout_logfile=/var/log/openvpn.out.log ;
environment=ASPNETCORE_ENVIRONMENT=Production ;
user=root ;
stopsignal=INT
startsecs=5 ;
#启动
systemctl restart supervisord
supervisorctl start openvpn
#停止
supervisorctl stop openvpn
我这里客户端启动报错了
$ supervisorctl start openvpn
openvpn: ERROR (spawn error)
查看日志
$ cat /var/log/openvpn.log
# 为空,说明在OpenVPN启动之前的报错。那我们去排查supervisor的问题。
$ cat /var/log/openvpn.out.log
Options error: In [CMD-LINE]:1: Error opening configuration file: qiyue_aliyun.ovpn
Use --help for more information.
原来是日志文件不对,改成qiyue_client.ovpn重试。 查看状态
$ ps -ef |grep openvpn
root      4856 32017  0 14:02 ?        00:00:00 /usr/sbin/openvpn --cd /etc/openvpn/ --config server.conf
root      4896  3024  0 14:02 pts/3    00:00:00 grep --color=auto openvpn
$ netstat -nltp|grep 1999
tcp        0      0 0.0.0.0:1999            0.0.0.0:*               LISTEN      32019/openvpn
  状态日志,可以看到连接的用户、IP地址与时间。
$ cat /etc/openvpn/log/openvpn-status.log
查看用户登录日志
$ tail -1 /etc/openvpn/log/openvpn-password.log
2023-02-17 21:37:00: Successful authentication: username="qiyue".
查看ipinfo.io返回的公网IP,确认为云主机公网IP
$ curl ipinfo.io
{
  "ip": "xxx.xxx.xxx.xxx",
  "city": "Shenzhen",
  "region": "Guangdong",
  "country": "CN",
  "loc": "22.5455,114.0683",
  "org": "AS45090 Shenzhen Tencent Computer Systems Company Limited",
  "timezone": "Asia/Shanghai",
  "readme": "https://ipinfo.io/missingauth"
}

MacOS客户端测试

打包client端文件并修改文件夹名为tblk文件后缀,以便Tunnelblick识别。 修改后会识别该文件夹为配置文件,双击连接即可。 下载Tunnelblick稳定版(stable): 自动翻译:https://tunnelblick-net.translate.goog/downloads.html?_x_tr_sl=en&_x_tr_tl=zh-CN&_x_tr_hl=zh-CN 原官网:https://tunnelblick.net/downloads.html   访问:https://www.ip138.com 查看IP地址为云主机公网IP,成功。 终端查看
$ ifconfig
...
utun5: flags=8051 mtu 1500
	inet 10.0.1.6 --> 10.0.1.5 netmask 0xffffffff
PING云主机内网IP
$ ping 10.0.8.6 -c 3
PING 10.0.8.6 (10.0.8.6): 56 data bytes
64 bytes from 10.0.8.6: icmp_seq=0 ttl=64 time=39.248 ms
64 bytes from 10.0.8.6: icmp_seq=1 ttl=64 time=32.542 ms
64 bytes from 10.0.8.6: icmp_seq=2 ttl=64 time=43.443 ms

--- 10.0.8.6 ping statistics ---
3 packets transmitted, 3 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 32.542/38.411/43.443/4.489 ms

连通性测试

查看服务端状态日志:
cat /etc/openvpn/log/openvpn-status.log
可以看到登录了两台客户端。 在我们MacOS端ssh到另一个Linux客户端测试: 在另一台Linux客户端测试到云主机的通信
$ ping 10.0.8.6 -c 3
PING 10.0.8.6 (10.0.8.6) 56(84) bytes of data.
64 bytes from 10.0.8.6: icmp_seq=1 ttl=64 time=92.8 ms
64 bytes from 10.0.8.6: icmp_seq=2 ttl=64 time=30.1 ms
64 bytes from 10.0.8.6: icmp_seq=3 ttl=64 time=30.3 ms

--- 10.0.8.6 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2004ms
rtt min/avg/max/mdev = 30.139/51.125/92.845/29.501 ms
测试到局域网其他主机的通信:
$ ping 192.168.0.102 -c 3
PING 192.168.0.102 (192.168.0.102) 56(84) bytes of data.
64 bytes from 192.168.0.102: icmp_seq=1 ttl=64 time=0.271 ms
64 bytes from 192.168.0.102: icmp_seq=2 ttl=64 time=0.189 ms
64 bytes from 192.168.0.102: icmp_seq=3 ttl=64 time=0.202 ms

--- 192.168.0.102 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1999ms
rtt min/avg/max/mdev = 0.189/0.220/0.271/0.039 ms
 

客户端非直连

我们测试一下在客户端的其他非client直连去连接VPN网段,发现无法通信。 查看当前路由表
$ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.0.1     0.0.0.0         UG    100    0        0 ens33
192.168.0.0     0.0.0.0         255.255.255.0   U     100    0        0 ens33
192.168.122.0   0.0.0.0         255.255.255.0   U     0      0        0 virbr0
client机器开启内核同网段的转发:
$ echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
$ sysctl -p
net.ipv4.ip_forward = 1
在局域网其他主机加一条静态路由,让去VPN网段的下一跳地址指向client的地址。
$ ip route add 10.0.1.0/24 via 192.168.0.110

$ ping 10.0.1.10 -c 3
PING 10.0.1.10 (10.0.1.10) 56(84) bytes of data.
64 bytes from 10.0.1.10: icmp_seq=1 ttl=64 time=0.206 ms
64 bytes from 10.0.1.10: icmp_seq=2 ttl=64 time=0.214 ms
64 bytes from 10.0.1.10: icmp_seq=3 ttl=64 time=0.228 ms

--- 10.0.1.10 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1999ms
rtt min/avg/max/mdev = 0.206/0.216/0.228/0.009 ms
查看当前路由表
$ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.0.1     0.0.0.0         UG    100    0        0 ens33
10.0.1.0        192.168.0.110   255.255.255.0   UG    0      0        0 ens33
192.168.0.0     0.0.0.0         255.255.255.0   U     100    0        0 ens33
192.168.122.0   0.0.0.0         255.255.255.0   U     0      0        0 virbr0

设置固定IP(可选)

在服务端/etc/openvpn/server.conf中增加如下配置:
#为客户端指定VPN网段的IP
client-config-dir /etc/openvpn/ccd
在服务端/etc/openvpn/ccd/设置用户qiyue的IP为 10.8.0.11
cat /etc/openvpn/ccd/qiyue

ifconfig-push 10.0.1.11 255.255.255.0
重启客户端和服务端的openvpn服务
supervisorctl restart openvpn
查看用户qiyue登录所在的client的IP信息,成功获取到指定IP。
$ ip a
1: lo:  mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: ens33:  mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 00:0c:29:38:b3:7b brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.110/24 brd 192.168.0.255 scope global noprefixroute ens33
       valid_lft forever preferred_lft forever
    inet6 fe80::8fd1:3f0c:fb58:2c90/64 scope link noprefixroute
       valid_lft forever preferred_lft forever
14: tun0:  mtu 1500 qdisc pfifo_fast state UNKNOWN group default qlen 100
    link/none
    inet 10.0.1.11 peer 255.255.255.0/32 scope global tun0
       valid_lft forever preferred_lft forever
    inet6 fe80::6e3e:7b5d:68ce:6f08/64 scope link flags 800
       valid_lft forever preferred_lft forever
 
  • 0