环境介绍

1
2
3
4
Linux服务器:centos7.7 (192.168.170.100)
openvpn:2.4.11 (公网IP地址:PORT)
easyrsa:v3 (用来实现openvpn证书认证)
插件:google authentication

环境部署

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
openvpn所需依赖包:
]# yum clean cache #清空下缓存
]# rpm -Uvh https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
]# yum makecache
]# yum install re2c libtool openvpn openvpn-devel gcc-objc tree easy-rsa google-authenticator qrencode -y # 环境准备
安装成功后
]# rpm -ql openvpn #查看openvpn相关配置文件
/etc/openvpn
/etc/openvpn/client
/etc/openvpn/server
/run/openvpn-client
/run/openvpn-server
/usr/lib/systemd/system/openvpn-client@.service
/usr/lib/systemd/system/openvpn-server@.service
/usr/lib/systemd/system/openvpn@.service .......
]# rpm -ql easy-rsa #查看证书生成工具的相关文件
/usr/share/doc/easy-rsa-3.0.8
/usr/share/doc/easy-rsa-3.0.8/COPYING.md
/usr/share/doc/easy-rsa-3.0.8/ChangeLog
/usr/share/doc/easy-rsa-3.0.8/README.md
/usr/share/doc/easy-rsa-3.0.8/README.quickstart.md
/usr/share/doc/easy-rsa-3.0.8/vars.example

证书生成

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
71
72
73
74
75
76
77
78
79
80
81
82
83
 ]# mkdir -p /etc/openvpn/easy-rsa-server
]# cp -r /usr/share/easy-rsa/3 /etc/openvpn/easy-rsa-server/
]# cp -r /usr/share/easy-rsa/3 /etc/openvpn/easy-rsa-client/
# 更改根证书的过期时间(一般设置的时间比较长避免证书过期而带来不必要的麻烦)
set_var EASYRSA_CA_EXPIRE xxxx
# 证书过期时间
set_var EASYRSA_CERT_EXPIRE xxxx
]# cd /etc/openvpn/easy-rsa/server
]# ./easyrsa init-pki # 在当前目录下进行初始化以生成服务器的相关文件
]# ./easyrsa build-ca # 生成根证书
Enter New CA Key Passphrase: # 生成根证书所需的密码(可为空)
Re-Enter New CA Key Passphrase:
Generating RSA private key, 2048 bit long modulus (2 primes)
.............................+++++
................................+++++
e is 65537 (0x010001)
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Common Name (eg: your user, host, or server name) [Easy-RSA CA]: # 这里可以直接默认

CA creation complete and you may now import and sign cert requests.
Your new CA certificate file for publishing is at:
/etc/openvpn/easy-rsa-server/pki/ca.crt # 生成的证书文件
]# ./easyrsa gen-req server nopass (创建服务器端的证书,可以不加nopass参数,意为不加密私钥文件)
Using SSL: openssl OpenSSL 1.1.1f 31 Mar 2020
Generating a RSA private key
.....................+++++
......................................................+++++
writing new private key to '/etc/openvpn/easy-rsa-server/pki/private/server.key.rdsFG9aW6P'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Common Name (eg: your user, host, or server name) [server]:

Keypair and certificate request completed. Your files are:
req: /etc/openvpn/easy-rsa-server/pki/reqs/server.req # 证书请求文件
key: /etc/openvpn/easy-rsa-server/pki/private/server.key # 证书的密钥文件
]# ./easyrsa sign server server # 给证书进行签名
You are about to sign the following certificate.
Please check over the details shown below for accuracy. Note that this request
has not been cryptographically verified. Please be sure it came from a trusted
source or that you have verified the request checksum with the sender.

Request subject, to be signed as a server certificate for 1080 days:
subject=
commonName = server
Type the word 'yes' to continue, or any other input to abort.
Confirm request details: yes
Using configuration from /etc/openvpn/easy-rsa-server/pki/safessl-easyrsa.cnf
Enter pass phrase for /etc/openvpn/easy-rsa-server/pki/private/ca.key:
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
commonName :ASN.1 12:'server'
Certificate is to be certified until Nov 7 06:46:16 2025 GMT (1080 days)
Write out database with 1 new entries
Data Base Updated
Certificate created at: /etc/openvpn/easy-rsa-server/pki/issued/server.crt
]# ./easyrsa gen-dh # 密钥交换时的算法
DH parameters of size 2048 created at /etc/openvpn/easy-rsa-server/pki/dh.pem

]# cd /etc/openvpn/easy-rsa-client
]# ./easyrsa init-pki # 在当前目录下进行初始化以生成客户端的证书生成的相关文件
]# ./easyrsa gen-req test nopass# 生成客户端证书文件
/etc/openvpn/easy-rsa-cient/pki/reqs/test.req
]# cd /etc/openvpn/easy-rsa-server
]# ./easyrsa import-req /etc/openvpn/easy-rsa-cient/pki/reqs/test.req test
]# ./easyrsa sign client test # 给客户端的证书进行签名
subject=
commonName = client
Type the word 'yes' to continue, or any other input to abort.
Confirm request details: yes

将生成出的证书文件都放入一个目录下,方便管理

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
 ]# cp /usr/share/doc/openvpn-2.4.12/sample/sample-config-files/server.conf /etc/openvpn/server.conf
]# cat /etc/openvpn.conf (将以下配置写入文件)
port 1194 # 服务器启动端口
proto udp # 端口协议
dev tun # 采用路由隧道模式tun
ca /etc/openvpn/certs/ca.crt # 证书文件的位置
cert /etc/openvpn/certs/server.crt # 服务器公钥的位置
key /etc/openvpn/certs/server.key # 服务器私钥的位置
dh /etc/openvpn/certs/dh.pem # 密钥交换算法存在的位置
keepalive 10 120
topology subnet # 网络拓扑模式;包含三种模式(net30 | p2p | subnet)
server 10.8.0.0 255.255.255.0 # 连接上服务器给客户端分发的地址望断
ifconfig-pool-persist ipp.txt # 第一次连接后会将该地址持久化道文件中,相当于分发给客户端的静态地址
push "dhcp-option DNS 223.5.5.5" # 给客户端推送的解析服务器
push "dhcp-option DNS 114.114.114.114"
#push "redirect-gateway def1 bypass-dhcp" # 将所有穿出IP的流量都用过VPN进行重定向,这里不建议设置
push "route 192.168.170.0 255.255.255.0" # 给客户端添加内网网段
#tls-auth /etc/openvpn/certs/ta.key 0 # 在TLS控制通道上添加HMAC身份验证,减轻Dos和TLS堆栈攻击 可以通过 openvpn --genkey FILENAME进行生成
auth SHA256
cipher AES-128-GCM # 指定密码算法
user nobody # 启动的用户
group nobody # 组
#tls-server
#tls-version-min 1.2
#tls-cipher TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256 # 指定tls连接时使用哪些密码套件
max-clients 2048 # 客户端最大连接数量
persist-key # 当keepalive超时后,重新启动vpn,但不重新读取key,即保留第一次使用的key
persist-tun # 检测超时后,重新启动vpn,一直保持tun是linkup状态,否则的话网络会先down再up
status openvpn-status.log # 日志记录位置
log-append openvpn.log # openvpn日志记录日志
verb 3
explicit-exit-notify 1 # 在 UDP 服务器模式下,向连接的客户端发送restart控制通道命令,默认是1,2代表进到下一个服务器 除非启用该选项,否则服务器不会发送任何通知
crl-verify /etc/openvpn/certs/crl.pem # 吊销证书的文件;./easyrsa revoke NAME;吊销后会生成对应的pem文件
plugin /usr/lib64/openvpn/plugins/openvpn-plugin-auth-pam.so "openvpn login USERNAME password PASSWORD pin OTP" # 指定用来认证的模块

# 在创建客户端文件时需要在系统内创建一个不需要登录的账户并配置密码(这里的账户和密码即为openvpn连接登录时所需的账户和密码)
]# 客户端配置文件
client
dev tun
proto udp
remote 公网IP 服务端口
verify-x509-name server name
resolv-retry infinite
nobind
persist-key
persist-tun
remote-cert-tls server
cipher AES-128-GCM
auth SHA256
auth-nocache
auth-user-pass #需要客户端提供账户和密码才可以访问
static-challenge "Enter 2FA Authenticator code:" 1
reneg-sec 0
verb 3
<ca>
输入根证书内容
</ca>
<cert>
输入客户端证书内容
</cert>
<key>
输入客户端的密钥内容
</key>

Google Authenticator实现

1
2
3
4
google-authenticator -t -d -f -r 3 -R 30 -W -s "/opt/openvpn/google-auth/xxx"
qrencode -t PNG -o "/opt/openvpn/google-auth/xxx.png" "otpauth://totp/vpn客户端分配的名称@${HOST}?secret=(google命令执行后文件的第一行)&issuer=openvpn"

上述操作完成后直接将png获取到然后在手机上下载Google Authenticator进行扫描二维码即可

防火墙配置

1
2
3
4
5
由于openvpn只负责流量的转发,并没有nat功能,因此需要借助防火墙来实现nat功能
添加一下规则:

]# iptables -A INPUT -p tcp --dport 1194 -j ACCEPT # openvpn服务端口
]# iptables -t nat -A POSTROUTING -s 10.8.0.0/24(vpn网段) -o ens112(内网网卡) -j MASQUERADE #可以添加多条

PAM配置

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
将以下内容输入到: /etc/pam.d/openvpn
标注:
第一列:代表模块类型:
auth:用来对用户的身份进行识别;例如:用户输入密码,判断用户是否是root
account:对账号的各个属性进行检查;如:是否允许登录,是否达到最大用户数,或者root用户是否允许在这个终端登录
session:模块用来定义用户登录前的;以及用户退出后所要进行的操作;如:登录链接信息,用户数据的打开和关闭,挂在文件系统等。
password:使用用户信息来更新;如修改密码
第二列:代表控制标记
required:表示某个模块对用户的验证失败,也要等到所有的模块执行完成后,PAM才返回错误信息。这样做是为了不让用户知道被哪个模块拒绝,如果用户验证成功,所有的模块都会返回成功信息
requisite:与required相似,但是如果这个模块返回失败,会立即返回失败,表示此类型失败,不再进行后边的验证
sufficient:表示如果一个用户通过这个模块的验证,PAM结构就立刻返回验证成功信息(即使前面有模块fail掉,也会把fail结果忽略,把控制权交回应用程序。后面的层叠模块即使使用requisite或required控制标志,也不再执行,如果验证失败,则与optional相同)
optional:表示即使本行指定的模块验证失败,也允许用户接收应用程序提供的服务,一般返回PAM_IGNORE
第三列:代表模块路径
常用模块:
pam_unix.so auth
pam_unix.so account
pam_unix.so password
pam_shells.so(auth|account)
pam_deny.so (account|auth|password|session)
pam_securetty.so auth
pam_listfile.so (account|auth|password|session)
pam_cracklib.so password (用于插入到一个程序的密码栈中,用于检查密码的强度)
pam_limits.so session 定义使用系统资源的上线,root用户也会受此限制,可以通过/etc/security/limits.conf或/etc/security/limits.d/*.conf来设定
第四列:代表模块参数


account required pam_unix.so
auth required pam_unix.so
auth substack password-auth
auth include postlogin
account required pam_sepermit.so
account required pam_nologin.so
account include password-auth
password include password-auth
auth requisite /usr/lib64/security/pam_google_authenticator.so secret=/opt/openvpn/google-auth/${USER} user=root authtok_prompt=pin
# secret表示根据每次创建新VPN用户时生成的身份验证器检查传入的OTP令牌,如果OTP与google令牌匹配则进行验证;user表示用什么用户来查看对应的secret文件;authtok_prompt表示检查用户传入的令牌

登录示例

linux中对应账户的账号和密码

手机上查看对应的动态口令(该口令可以通过上面的google 命令配置多少秒变更一次)

第一步中输入的密码

到这里就连接成功啦