引言

这篇文章其实是2019年年末记录的,当时是因为疫情原因公司需要协助员工在家办公。开发部门和总部安全部门认为直接吧公司内网系统开放访问不安全,一个一个给员工家里公网IP加白名单也不现实。就临危受命接下了搭建VPN的任务。这次将文档整理发出其实在纠结会不会被请去喝茶,毕竟话题敏感。希望人没事

PS:文档在记录时并没有太多中文参考资料,是自己摸索的,所以可能生成的有些证书并没有用上(多余的操作)。大家海涵

OpenVPN介绍

  • OpenVPN是一个用于创建虚拟专用网络加密通道的软件包,最早由James Yonan编写。OpenVPN允许创建的VPN使用公开密钥、电子证书、或者用户名/密码来进行身份验证。
  • 它大量使用了OpenSSL加密库中的SSLv3/TLSv1协议函数库。
  • 目前OpenVPN能在Solaris、Linux、OpenBSD、FreeBSD、NetBSD、Mac OS X与Microsoft Windows以及Android、iOS、MacOS(2020年官方推出Mac客户端)上运行,并包含了许多安全性的功能。它并不是一个基于Web的VPN软件,也不与IPsec及其他VPN
  • OpenVPN2.0后引入了用户名/口令组合的身份验证方式,它可以省略客户端证书,但是仍有一份服务器证书需要被用作加密。 OpenVPN所有的通信都基于一个单一的IP端口, 默认且推荐使用UDP协议通讯,同时TCP也被支持。OpenVPN连接能通过大多数的代理服务器,并且能够在NAT的环境中很好地工作。服务端具有向客 户端“推送”某些网络配置信息的功能,这些信息包括:IP地址、路由设置等。
  • OpenVPN提供了两种虚拟网络接口:通用Tun/Tap驱动,通过它们, 可以建立三层IP隧道,或者虚拟二层以太网,后者可以传送任何类型的二层以太网络数据。传送的数据可通过LZO算法压缩。在选择协议时候,需要注意2个加密隧道之间的网络状况,如有高延迟或者丢包较多的情况下,请选择TCP协议作为底层协议,UDP协议由于存在无连接和重传机制,导致要隧道上层的协议进行重传,效率非常低下。

VPN的主要作用是在局域网外部时也可以访问局域网的资源,比如公司内网有许多资源只能公司内网访问,而我们外派出差员工还想访问公司内网资源[如:GitLab/测试库/其他测试环境接口]就需要通过连接公司公网出口暴露VPN来实现。

由于众所周知的原因,几种简单的VPN协议比如L2TP/IPsec和PPTP协议在大陆地区已经被干扰的基本无法正常使用了。因此也就SSL VPN还可以使用。SSL VPN有非常多种,但很多都是商业软件,不开源也不适合个人搭建。而OpenVPN是一款开源的SSL VPN,可以很容易找到搭建的方法,非常符合我们的要求。

OpenVpn

开始搭建(基于账号密码认证)

安装依赖

1
yum install -y openssl openssl-devel lzo lzo-devel pam pam-devel pam_mysql automake pkgconfig gcc gcc-c++

规范安装目录

1
mkdir -p /application/

安装 easy-rsa 并生成相关证书

easy-rsa 下载地址:网页链接

1
2
3
[root@m01 ~]# unzip -d /application/ easy-rsa-old-master.zip
[root@m01 /application/easy-rsa-old-master]# ls
configure.ac COPYING COPYRIGHT.GPL distro doc easy-rsa Makefile.am

生成相关证书

1
2
3
4
5
[root@m01 /application/easy-rsa-old-master]# cd /application/easy-rsa-old-master/easy-rsa/2.0/
[root@m01 /application/easy-rsa-old-master/easy-rsa/2.0]# ls
build-ca build-key build-key-server clean-all openssl-0.9.6.cnf pkitool vars
build-dh build-key-pass build-req inherit-inter openssl-0.9.8.cnf revoke-full whichopensslcnf
build-inter build-key-pkcs12 build-req-pass list-crl openssl-1.0.0.cnf sign-req

准备 openssl 相关文件

1
2
3
[root@m01 /application/easy-rsa-old-master/easy-rsa/2.0]# ln -s openssl-1.0.0.cnf openssl.cnf
[root@m01 /application/easy-rsa-old-master/easy-rsa/2.0]# ll openssl.cnf
lrwxrwxrwx 1 root root 17 2月 2 01:34 openssl.cnf -> openssl-1.0.0.cnf

编辑证书基本信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
pwd 
/application/easy-rsa-old-master/easy-rsa/2.0
vim vars
67 export KEY_COUNTRY="CN"
68 export KEY_PROVINCE="SH"
69 export KEY_CITY="Shanghai"
70 export KEY_ORG="360XXX"
71 export KEY_EMAIL="yaXXXXXhi-jk@360jiXXXng.net"

. vars #生效环境变量

##生成证书,以下命令全部一直回车
./clean-all
(y/n)y
#清空所有证书(keys目录下)
./build-ca
(y/n)y
#生成服务器ca证书

##生成证书如下:
ls keys/
ca.crt ca.key index.txt serial

生成服务器端秘钥

1
2
./build-key-server server
(y/n)y

生成密钥如下

1
2
ls keys
01.pem ca.crt ca.key index.txt index.txt.attr index.txt.old serial serial.old server.crt server.csr server.key

生成客户端证书

1
2
3
./build-key client
(y/n)y
(y/n)y

创建迪菲·赫尔曼密钥

1
2
## 会生成dh2048.pem文件(生成过程比较慢,在此期间不要去中断它)
./build-dh

查看生成的所有证书

1
2
3
[root@m01 /application/easy-rsa-old-master/easy-rsa/2.0]# ls keys/
01.pem ca.crt client.crt client.key index.txt index.txt.attr.old serial server.crt server.key
02.pem ca.key client.csr dh2048.pem index.txt.attr index.txt.old serial.old server.csr

安装openvpn

1
2
3
4
5
6
7
8
9
10
[root@m01 ~]# tar -xf openvpn-2.4.8.tar.gz 
[root@m01 ~]# cd openvpn-2.4.8/
./configure --prefix=/application/openvpn-2.4.8 && make && make install

[root@m01 /application]# ln -s openvpn-2.4.8 openvpn
[root@m01 /application]# ll
总用量 0
drwxr-xr-x 5 root root 178 1月 23 2018 easy-rsa-old-master
lrwxrwxrwx 1 root root 13 2月 2 02:06 openvpn -> openvpn-2.4.8
drwxr-xr-x 6 root root 57 2月 2 02:06 openvpn-2.4.8

配置openvpn

1
2
3
4
配置 OpenVPN 服务端
创建配置文件目录和证书目录:
mkdir -p /etc/openvpn # openvpn 配置文件路径
mkdir -p /etc/openvpn/pki # openvpn 证书存放位置

生成ta.key文件(防DDos攻击、UDP淹没等恶意攻击)

1
2
3
[root@m01 /application/easy-rsa-old-master/easy-rsa/2.0/keys]# /application/openvpn/sbin/openvpn --genkey --secret ta.key 
[root@m01 /application/easy-rsa-old-master/easy-rsa/2.0/keys]# ls
ta.key

将签名生成的 CA 证书秘钥和服务端证书秘钥拷贝到证书目录中

1
2
3
4
5
6
7
8
9
[root@m01 /application/easy-rsa-old-master/easy-rsa/2.0/keys]# cp {ca.crt,ca.key,dh2048.pem,server.crt,server.key,ta.key} /etc/openvpn/pki/
[root@ZJVPN /application/easy-rsa-old-master/easy-rsa/2.0/keys]# ll /etc/openvpn/pki/
总用量 28
-rw-r--r-- 1 root root 2382 2月 2 05:43 ca.crt
-rw------- 1 root root 3268 2月 2 05:43 ca.key
-rw-r--r-- 1 root root 424 2月 2 05:43 dh2048.pem
-rw-r--r-- 1 root root 8146 2月 2 05:43 server.crt
-rw------- 1 root root 3272 2月 2 05:43 server.key
-rw------- 1 root root 636 2月 2 05:43 ta.key

拷贝配置文件模版

1
2
3
4
5
6
7
8
9
10
11
12
13
[root@m01 ~]# cp openvpn-2.4.8/sample/sample-config-files/server.conf /etc/openvpn/
[root@m01 ~]# tree /etc/openvpn/
/etc/openvpn/
├── pki
├── pki
│   ├── ca.crt
│   ├── ca.key
│   ├── dh2048.pem
│   ├── ipp.txt
│   ├── server.crt
│   ├── server.key
│   └── ta.key
└── server.conf

编辑openvpn配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
[root@m01 ~]# cat /etc/openvpn/server.conf 
local 0.0.0.0
port 8888
proto tcp
dev tun
ca /etc/openvpn/pki/ca.crt
cert /etc/openvpn/pki/server.crt
key /etc/openvpn/pki/server.key # This file should be kept secret
dh /etc/openvpn/pki/dh2048.pem
server 10.10.10.0 255.255.255.0 # 分配给客户端的虚拟局域网段
push "dhcp-option DNS 192.168.200.177" #客户端连入后使用的DNS
push "dhcp-option DNS 114.114.114.114"
ifconfig-pool-persist ipp.txt
#k8sDNS
push "route 192.168.200.177 255.255.255.255" # 推送路由和DNS到客户端
#k8s负载
push "route 192.168.200.120 255.255.255.255"
#gitlab新
push "route 192.168.1.55 255.255.255.255"
push "route 192.168.1.249 255.255.255.255"
#内网测试库
push "route 192.168.1.233 255.255.255.255"
#felix电脑
push "route 192.168.1.32 255.255.255.255"
#老麦测试机
push "route 192.168.1.238 255.255.255.255"
#jacky电脑
push "route 192.168.1.17 255.255.255.255"
#mo电脑
push "route 192.168.1.121 255.255.255.255"
#三楼
push "route 182.18.3.0 255.255.255.0"
#VPN
push "route 192.168.1.160 255.255.255.255"
#测试机网段
push "route 192.168.200.0 255.255.255.0"
#lke
push "route 192.168.2.0 255.255.255.0"
push "route 192.168.1.19 255.255.255.255"
#hali
push "route 192.168.1.114 255.255.255.255"
#August
push "route 192.168.1.14 255.255.255.255"
#签名层
push "route 192.168.1.71 255.255.255.255"
#国内文章
push "route 192.168.1.171 255.255.255.255"

client-to-client
keepalive 10 120
tls-auth /etc/openvpn/pki/ta.key 0 # This file is secret
cipher AES-256-CBC
comp-lzo
;max-clients 50
user nobody
group nobody
auth-user-pass-verify /etc/openvpn/checkpsw.sh via-env
script-security 3
#client-cert-not-required
;link-mtu 1500
verify-client-cert none
username-as-common-name
persist-key
persist-tun
status /var/log/openvpn-status.log
log /var/log/openvpn.log
log-append /var/log/openvpn.log
verb 3

#这里选择了推送部分路由通过tun虚拟网卡设备,如果是0.0.0.0的话会导致连上VPN的客户端上网卡顿,即全部流量都走VPN访问

上传密码验证脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
[root@m01 /etc/openvpn]# ls
checkpsw.sh pki server.conf
[root@m01 /etc/openvpn]# ll
总用量 8
-rw-r--r-- 1 root root 1167 2月 2 02:59 checkpsw.sh #验证账号密码脚本
drwxr-xr-x 2 root root 102 2月 2 05:43 pki #账号密码文件 账号一行一个
-rw-r--r-- 1 root root 1174 2月 2 05:47 server.conf
[root@ZJVPN /etc/openvpn]# chmod +x checkpsw.sh
[root@ZJVPN /etc/openvpn]# ll
总用量 8
-rwxr-xr-x 1 root root 1167 2月 2 02:59 checkpsw.sh
drwxr-xr-x 2 root root 102 2月 2 05:43 pki
-rw-r--r-- 1 root root 1174 2月 2 05:47 server.conf

cat checkpsw.sh
#!/bin/sh
###########################################################
# checkpsw.sh (C) 2004 Mathias Sundman
#
# This script will authenticate OpenVPN users against
# a plain text file. The passfile should simply contain
# one row per user with the username first followed by
# one or more space(s) or tab(s) and then the password.
PASSFILE="/etc/openvpn/psw-file"
LOG_FILE="/application/openvpn/logs/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

[root@m01 ~]# ll /etc/openvpn/psw-file
-rw-r--r-- 1 root root 151 12月 1 11:05 /etc/openvpn/psw-file
[root@m01 ~]# cat /etc/openvpn/psw-file
felix 591740

配置systemd启动脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
cat /usr/lib/systemd/system/openvpn.service
[Unit]
Description=openvpn
After=network.target

[Service]
EnvironmentFile=-/etc/openvpn/openvpn
ExecStart=/application/openvpn/sbin/openvpn --config /etc/openvpn/server.conf
Restart=on-failure
Type=simple
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target

systemctl enable --now openvpn.service

2021年补图
补图

配置 iptables 策略:

1
2
3
4
5
iptables -t nat -A POSTROUTING -s 10.10.10.0/24 -o eth0 -j MASQUERADE
也可以这样做:添加 iptables 转发规则,对所有源地址(openvpn为客户端分配的地址)为 10.10.10.0/24 的数据包转发后进行源地址转换,伪装成 openvpn 服务器内网地址 x.x.x.x, 这样 VPN 客户端就可以访问服务器内网的其他机器了。

iptables -t nat -A POSTROUTING -s 10.10.10.0/24 -j SNAT --to-source 192.168.1.160
iptables -I INPUT -p tcp -m tcp --dport 8888 -j ACCEPT

开启内核路由转发功能

1
2
3
4
vim /etc/sysctl.conf
添加如下内容
net.ipv4.ip_forward = 1
sysctl -p

安装OpenVPN客户端

1
2
3
4
5
6
7
客户端安装完成后,将以下证书和秘钥文件拷贝到安装目录中的 config 目录下:
[root@ZJVPN /etc/openvpn/pki]# cd /application/easy-rsa-old-master/easy-rsa/2.0/keys/
[root@ZJVPN /application/easy-rsa-old-master/easy-rsa/2.0/keys]# ls
01.pem ca.crt client.crt client.key index.txt index.txt.attr.old serial server.crt server.key
02.pem ca.key client.csr dh2048.pem index.txt.attr index.txt.old serial.old server.csr ta.key
[root@ZJVPN /application/easy-rsa-old-master/easy-rsa/2.0/keys]# sz {ca.crt,client.crt,client.key,ta.key}

Windows
windows客户端
windows客户端
检查路由是否已经推送到客户端
windows客户端
MacOS
MacOS
MacOS
检查路由是否已经推送到客户端
upload successful

这里选择了推送部分路由通过tun虚拟网卡设备,如果是0.0.0.0的话会导致连上VPN的客户端上网卡顿,即全部流量都走VPN访问。

到此为止,我们就构建好了一个点到站VPN,如果你的服务器在公司内网需要麻烦IT同事将你的VPN端口如我这里的8888端口通过公司防火墙或者最外层路由设备做DNAT映射出去。在OPENVPN服务端配置文件中添加相关内网资源如Git仓库、测试数据库的内网IP 加入到路由中。在psw-file给出差员工开账号,吧相关证书和配置文件发给他们让他们可以连接你的VPN

客户端下载:https://openvpn.net/vpn-client/

客户端主配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
client
dev tun
proto tcp
remote 你的公网ip 8888
resolv-retry infinite
nobind
persist-key
persist-tun
ca ca.crt
cert client.crt
key client.key
remote-cert-tls server
tls-auth ta.key 1
cipher AES-256-CBC
auth-user-pass
auth-nocache
comp-lzo