运维人员必备:从零搭建OpenVPN内网穿透服务完整指南
你是否曾面临以下困境?
- 居家办公时无法连接到公司内部网络?
- 开发测试环境仅限办公室网络才能访问?
- 尝试远程调试服务器却被防火墙规则无情阻挡?
解决这些问题的方案其实非常明确:你需要搭建一个VPN。
前期环境准备
1. 安装OpenVPN服务器端软件及证书管理工具
# 安装OpenVPN主程序
[root@openvpn-server ~]#yum -y install openvpn
# 安装证书管理工具easy-rsa
[root@openvpn-server ~]#yum -y install easy-rsa
# 查看openvpn软件包包含的文件列表
[root@openvpn-server ~]#rpm -ql openvpn
/etc/openvpn
/etc/openvpn/client
...(此处省略详细列表)...
# 查看easy-rsa软件包包含的文件
[root@openvpn-server 3]#rpm -ql easy-rsa
/usr/share/doc/easy-rsa
...(此处省略详细列表)...
# 在vars配置文件中,可以调整证书的有效期
set_var EASYRSA_CA_EXPIRE 36500
# 设置证书的有效天数
set_var EASYRSA_CERT_EXPIRE 8250
配置文件准备与环境设置
# 生成服务器的主配置文件
[root@openvpn-server ~]#cp /usr/share/doc/openvpn/sample/sample-config-files/server.conf /etc/openvpn/
# 准备证书签发所需的相关文件
[root@openvpn-server ~]#cp -r /usr/share/easy-rsa/ /etc/openvpn/easy-rsa-server
# 准备证书签发变量的配置文件
[root@openvpn-server ~]#cp /usr/share/doc/easy-rsa/vars.example /etc/openvpn/easy-rsaserver/3/vars
# 建议修改CA和OpenVPN服务器证书的有效期,可适当延长
[root@openvpn-server ~]#vim /etc/openvpn/easy-rsa-server/3/vars
# CA证书默认有效期10年,可延长至36500天
set_var EASYRSA_CA_EXPIRE 36500
# 服务器证书默认825天,可延长至3650天
set_var EASYRSA_CERT_EXPIRE 3650
# 查看目录结构
[root@openvpn-server ~]#tree /etc/openvpn/
/etc/openvpn/
├── client
├── easy-rsa-server
│ ├── 3 -> 3.0.7
│ └── 3.0.7
│ ├── easyrsa
│ ├── openssl-easyrsa.cnf
│ ├── vars
│ └── x509-types
│ ├── ca
│ ├── client
│ ├── code-signing
│ ├── COMMON
│ ├── email
│ ├── kdc
│ ├── server
│ └── serverClient
├── server
└── server.conf
证书体系构建详解
3.1 初始化PKI与CA签发机构环境
3.1.1 掌握easyrsa脚本的基本用法
[root@openvpn-server ~]#cd /etc/openvpn/easy-rsa-server/3/
[root@openvpn-server 3]#pwd
/etc/openvpn/easy-rsa-server/3
[root@openvpn-server 3]#file ./easyrsa
./easyrsa: POSIX shell script, ASCII text executable
# 获取easy-rsa工具的使用帮助
[root@openvpn-server 3]#./easyrsa
Note: using Easy-RSA configuration from: /etc/openvpn/easy-rsa-server/3.0.8/vars
Easy-RSA 3 usage and overview
USAGE: easyrsa [options] COMMAND [command-options]
...(此处显示完整的命令列表和目录状态)...
3.1.2 执行PKI初始化操作
[root@openvpn-server ~]#cd /etc/openvpn/easy-rsa-server/3/
[root@openvpn-server 3]#pwd
/etc/openvpn/easy-rsa-server/3
[root@openvpn-server 3]#ls
easyrsa openssl-easyrsa.cnf vars x509-types
# 执行初始化命令,在当前目录下创建pki及相关文件
[root@openvpn-server 3]#./easyrsa init-pki
Note: using Easy-RSA configuration from: /etc/openvpn/easy-rsa-server/3.0.8/vars
init-pki complete; you may now create a CA or requests.
Your newly created PKI dir is: /etc/openvpn/easy-rsa-server/3/pki
[root@openvpn-server 3]#ls
easyrsa openssl-easyrsa.cnf pki vars x509-types
[root@openvpn-server 3]#tree
.
├── easyrsa
├── openssl-easyrsa.cnf
├── pki
│ ├── openssl-easyrsa.cnf
│ ├── private
│ ├── reqs
│ └── safessl-easyrsa.cnf
├── vars
└── x509-types
├── ca
├── client
└── ... (其他类型)
3.2 创建根证书颁发机构(CA)
[root@openvpn-server ~]#cd /etc/openvpn/easy-rsa-server/3
[root@openvpn-server 3]#tree pki
pki
├── openssl-easyrsa.cnf
├── private
├── reqs
└── safessl-easyrsa.cnf
# 创建CA,并不设置密码(nopass)
[root@openvpn-server 3]#./easyrsa build-ca nopass
Note: using Easy-RSA configuration from: /etc/openvpn/easy-rsa-server/3.0.8/vars
Using SSL: openssl OpenSSL 1.1.1k FIPS 25 Mar 2021
Generating RSA private key, 2048 bit long modulus (2 primes)
...(密钥生成过程)...
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/3/pki/ca.crt # 生成的自签名证书文件
# 查看生成的文件结构
[root@openvpn-server 3]#tree pki
pki
├── ca.crt # 自签名的根证书文件
├── certs_by_serial
├── index.txt
├── index.txt.attr
├── issued
├── openssl-easyrsa.cnf
├── private
│ └── ca.key # CA的私钥文件
└── ... (其他目录)
# 验证生成的关键文件
[root@openvpn-server 3]#ll pki/ca.crt pki/private/ca.key
-rw------- 1 root root 1204 Aug 2 16:42 pki/ca.crt
-rw------- 1 root root 1675 Aug 2 16:42 pki/private/ca.key
3.3 生成服务器端证书签名请求(CSR)
[root@openvpn-server ~]#cd /etc/openvpn/easy-rsa-server/3
[root@openvpn-server 3]#pwd
/etc/openvpn/easy-rsa-server/3
# 创建服务器证书请求,'server'为文件名前缀
[root@openvpn-server 3]#./easyrsa gen-req server nopass
Note: using Easy-RSA configuration from: /etc/openvpn/easy-rsa-server/3.0.8/vars
Using SSL: openssl OpenSSL 1.1.1k FIPS 25 Mar 2021
Generating a RSA private key
...(密钥生成过程)...
Common Name (eg: your user, host, or server name) [server]: # 接受Common Name的默认值,直接回车
Keypair and certificate request completed. Your files are:
req: /etc/openvpn/easy-rsa-server/3/pki/reqs/server.req # 证书请求文件
key: /etc/openvpn/easy-rsa-server/3/pki/private/server.key # 服务器私钥文件
3.4 使用CA签发服务器端证书
3.4.1 查看证书签发命令的帮助信息
[root@openvpn-serve ~]#cd /etc/openvpn/easy-rsa-server/3
[root@openvpn-server 3]#./easyrsa help sign
Note: using Easy-RSA configuration from: /etc/openvpn/easy-rsa-server/3.0.8/vars
sign-req <type> <filename_base>
Sign a certificate request of the defined type. <type> must be a known
type such as 'client', 'server', 'serverClient', or 'ca' (or a user-added type.)
This request file must exist in the reqs/ dir and have a .req file
extension. See import-req below for importing reqs from other sources.
3.4.2 执行服务器证书签发操作
# 对名为server的请求文件,签发服务器类型的证书
[root@openvpn-server ~]#cd /etc/openvpn/easy-rsa-server/3
[root@openvpn-server 3]#./easyrsa sign server server
Note: using Easy-RSA configuration from: /etc/openvpn/easy-rsa-server/3.0.8/vars
Using SSL: openssl OpenSSL 1.1.1k FIPS 25 Mar 2021
You are about to sign the following certificate.
Please check over the details shown below for accuracy.
Request subject, to be signed as a server certificate for 8250 days: # 显示vars文件中指定的有效期
subject=
commonName = server
Type the word 'yes' to continue, or any other input to abort.
Confirm request details: yes # 输入yes并回车确认
...(证书签发过程)...
Certificate created at: /etc/openvpn/easy-rsa-server/3/pki/issued/server.crt # 生成的服务器证书
3.4.3 验证证书签发结果
[root@openvpn-server 3]#cd /etc/openvpn/easy-rsa-server/3
[root@openvpn-server 3]#tree pki/
pki/
├── ca.crt
├── certs_by_serial
│ └── B8A307DDCAD2E3B9A473A2CB590C0460.pem # 服务器证书文件
├── index.txt
├── issued
│ └── server.crt # 服务器证书文件
├── private
│ ├── ca.key
│ └── server.key
└── ... (其他目录和文件)
# 查看证书序列号等信息
[root@openvpn-server 3]#cat pki/serial
B8A307DDCAD2E3B9A473A2CB590C0461
[root@openvpn-server 3]#cat pki/index.txt
V 460114064240Z B8A307DDCAD2E3B9A473A2CB590C0460 unknown /CN=server
3.5 创建Diffie-Hellman密钥交换参数
3.5.1 Diffie-Hellman算法简介
Diffie-Hellman密钥交换方法由惠特菲尔德·迪菲与马丁·赫尔曼于1976年共同发表。作为一种安全协议,它允许通信双方在不安全的信道上协商出一个共享密钥,该密钥通常用作后续数据传输的对称加密密钥。其数学原理基于离散对数难题。具备类似功能的还有RSA等非对称加密算法。该算法应用极为广泛,常见于SSH、VPN及HTTPS等协议中。
3.5.2 生成Diffie-Hellman参数文件
[root@openvpn-server 3]# cd /etc/openvpn/easy-rsa-server/3
[root@openvpn-server 3]#pwd
/etc/openvpn/easy-rsa-server/3
# 方法一:使用easyrsa工具生成
[root@centos8 3]#./easyrsa gen-dh
Note: using Easy-RSA configuration from: /etc/openvpn/easy-rsa-server/3.0.8/vars
Using SSL: openssl OpenSSL 1.1.1k FIPS 25 Mar 2021
Generating DH parameters, 2048 bit long safe prime, generator 2
This is going to take a long time
...(生成过程,需要等待)...
DH parameters of size 2048 created at /etc/openvpn/easy-rsa-sever/3/pki/dh.pem
# 查看生成的文件
[root@openvpn-server 3]#ll pki/dh.pem
-rw------- 1 root root 424 Jun 14 14:53 pki/dh.pem
# 方法二:直接使用openssl命令生成
[root@centos8 ~]#openssl dhparam -out /etc/openvpn/dh2048.pem 2048
[root@centos8 ~]#ll /etc/openvpn/dh2048.pem
-rw-r--r-- 1 root root 424 Aug 3 20:50 /etc/openvpn/dh2048.pem
3.6 准备客户端证书环境(本示例将服务器与客户端的文件置于同一环境)
[root@openvpn-server 3]# cd /etc/openvpn/easy-rsa-server/3
[root@openvpn-server 3]#pwd
/etc/openvpn/easy-rsa-server/3
[root@openvpn-server 3]#tree
.
├── easyrsa
├── openssl-easyrsa.cnf
├── pki
│ ├── ca.crt
│ ├── certs_by_serial
│ │ └── B8A307DDCAD2E3B9A473A2CB590C0460.pem
│ ├── dh.pem
│ ├── index.txt
│ ├── issued
│ │ └── server.crt
│ ├── private
│ │ ├── ca.key
│ │ └── server.key
│ └── ... (其他目录)
├── vars
└── x509-types
├── ca
├── client
└── ... (其他类型)
# 证书申请所需的pki目录及文件已随服务器配置一同生成
3.7 生成客户端证书请求
[root@centos8 ~]#cd /etc/openvpn/easy-rsa-client/3
[root@centos8 3]#pwd
/etc/openvpn/easy-rsa-client/3
# 为客户端用户生成证书请求
[root@openvpn-server 3]#./easyrsa gen-req dingbaohang nopass
Note: using Easy-RSA configuration from: /etc/openvpn/easy-rsa-server/3.0.8/vars
Using SSL: openssl OpenSSL 1.1.1k FIPS 25 Mar 2021
Generating a RSA private key
...(密钥生成过程)...
Common Name (eg: your user, host, or server name) [dingbaohang]: # 接受默认值,直接回车
Keypair and certificate request completed. Your files are:
req: /etc/openvpn/easy-rsa-server/3/pki/reqs/dingbaohang.req # 证书请求文件
key: /etc/openvpn/easy-rsa-server/3/pki/private/dingbaohang.key # 客户端私钥文件
3.8 使用CA签发客户端证书
[root@openvpn-server 3]#pwd
/etc/openvpn/easy-rsa-server/3
# 修改客户端证书的有效期(建议短于服务器证书)
[root@openvpn-server 3]#vim vars
# 建议修改给客户端颁发证书的有效期,可适当减少,例如90天
# 将原设置修改为:
set_var EASYRSA_CERT_EXPIRE 90
# 签发客户端证书
[root@openvpn-server 3]#./easyrsa sign client dingbaohang
Note: using Easy-RSA configuration from: /etc/openvpn/easy-rsa-server/3.0.8/vars
Using SSL: openssl OpenSSL 1.1.1k FIPS 25 Mar 2021
You are about to sign the following certificate.
Request subject, to be signed as a client certificate for 90 days:
subject=
commonName = dingbaohang
Type the word 'yes' to continue, or any other input to abort.
Confirm request details: yes
...(签发过程)...
Certificate created at: /etc/openvpn/easy-rsa-server/3/pki/issued/dingbaohang.crt # 客户端证书文件
[root@openvpn-server 3]#tree pki/
pki/
├── ca.crt
├── certs_by_serial
│ ├── B8A307DDCAD2E3B9A473A2CB590C0460.pem
│ └── BBD575D412666BE1B578CACB25323067.pem
├── issued
│ ├── dingbaohang.crt # 新生成的客户端证书
│ └── server.crt
└── ... (其他目录)
# 查看证书数据库
[root@openvpn-server 3]#cat pki/index.txt
V 460114064240Z B8A307DDCAD2E3B9A473A2CB590C0460 unknown /CN=server
V 230912072326Z BBD575D412666BE1B578CACB25323067 unknown /CN=dingbaohang
如果需要为大量客户端颁发证书,可以使用以下脚本实现自动化操作。
客户端证书自动颁发脚本示例
[root@centos8 ~]#cat openvpn-user-crt.sh
#!/bin/bash
read -p "请输入用户的姓名拼音(如:${NAME}): " NAME
cd /etc/openvpn/easy-rsa-client/3
./easyrsa gen-req ${NAME} nopass <<EOF
EOF
cd /etc/openvpn/easy-rsa-server/3
./easyrsa import-req /etc/openvpn/easy-rsa-client/3/pki/reqs/${NAME}.req ${NAME}
./easyrsa sign client ${NAME} <<EOF
yes
EOF
3.9 将CA及服务器证书文件部署到服务器指定目录
[root@openvpn-server 3]#mkdir /etc/openvpn/certs
[root@openvpn-server 3]#cp /etc/openvpn/easy-rsa-server/3/pki/ca.crt /etc/openvpn/certs/
[root@openvpn-server 3]#cp /etc/openvpn/easy-rsa-server/3/pki/issued/server.crt /etc/openvpn/certs/
[root@openvpn-server 3]#cp /etc/openvpn/easy-rsa-server/3/pki/reqs/server.req /etc/openvpn/certs/
[root@openvpn-server 3]#cp /etc/openvpn/easy-rsa-server/3/pki/dh.pem /etc/openvpn/certs/
[root@openvpn-server 3]#ll /etc/openvpn/certs/
total 20
-rw------- 1 root root 1204 Jun 14 15:30 ca.crt
-rw------- 1 root root 424 Jun 14 15:32 dh.pem
-rw------- 1 root root 4608 Jun 14 15:31 server.crt
-rw------- 1 root root 887 Jun 14 15:32 server.req
3.10 将客户端私钥与证书文件集中存放
# 查找客户端相关的证书和密钥文件
[root@openvpn-server 3]#find /etc/openvpn/ -name "dingbaohang.key" -o -name "dingbaohang.crt" -o -name ca.crt
/etc/openvpn/easy-rsa-server/3.0.8/pki/private/dingbaohang.key
/etc/openvpn/easy-rsa-server/3.0.8/pki/issued/dingbaohang.crt
/etc/openvpn/easy-rsa-server/3.0.8/pki/ca.crt
/etc/openvpn/certs/ca.crt
# 将找到的文件复制到为客户端创建的专用目录
[root@openvpn-server 3]#find /etc/openvpn/ \( -name "dingbaohang.key" -o -name "dingbaohang.crt" -o -name ca.crt \) -exec cp {} /etc/openvpn/client/dingbaohang \;
[root@openvpn-server 3]#ll /etc/openvpn/client/dingbaohang/
total 16
-rw------- 1 root root 1204 Jun 14 15:45 ca.crt
-rw------- 1 root root 4505 Jun 14 15:45 dingbaohang.crt
-rw------- 1 root root 1704 Jun 14 15:45 dingbaohang.key
4、OpenVPN服务器主配置文件详解
4.1 服务器端核心配置选项说明
# server.conf文件中以#或;开头的均为注释行
[root@openvpn-server ~]#grep '^[a-Z].*' /etc/openvpn/server.conf
port 1194
proto tcp
dev tun
ca /etc/openvpn/certs/ca.crt
cert /etc/openvpn/certs/server.crt
key /etc/openvpn/certs/server.key # This file should be kept secret
dh /etc/openvpn/certs/dh.pem
server 10.8.0.0 255.255.255.0
push "route 172.30.0.0 255.255.255.0"
keepalive 10 120
cipher AES-256-CBC
compress lz4-v2
push "compress lz4-v2"
max-clients 2048
user openvpn
group openvpn
status /var/log/openvpn/openvpn-status.log
log-append /var/log/openvpn/openvpn.log
verb 3
mute 20
# 创建日志文件目录并设置权限
[root@openvpn-server ~]#vim /etc/openvpn/server.conf
[root@openvpn-server ~]#getent passwd openvpn
openvpn:x:220:988:OpenVPN:/etc/openvpn:/sbin/nologin
[root@openvpn-server ~]#mkdir /var/log/openvpn
[root@openvpn-server ~]#chown openvpn.openvpn /var/log/openvpn
[root@openvpn-server ~]#ll -d /var/log/openvpn
drwxr-xr-x 2 openvpn openvpn 6 Jun 14 16:31 /var/log/openvpn
5、配置系统网络转发与防火墙规则
# 在服务器上启用IPv4数据包转发功能
[root@centos8 ~]#echo net.ipv4.ip_forward = 1 >> /etc/sysctl.conf
[root@centos8 ~]#sysctl -p
net.ipv4.ip_forward = 1
# 添加SNAT规则以实现VPN客户端访问外部网络
[root@openvpn-server sysctl.d]#vim /etc/rc.local
# 添加以下内容
[root@openvpn-server sysctl.d]#cat /etc/rc.local |grep '^[a-z].*'
touch /var/lock/subsys/local
iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -j MASQUERADE
[root@openvpn-server sysctl.d]#/etc/rc.d/rc.local
[root@openvpn-server sysctl.d]#iptables -vnL
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
[root@openvpn-server sysctl.d]#iptables -vnL -t nat
Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
0 0 MASQUERADE all -- * * 10.8.0.0/24 0.0.0.0/0
Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
6、启动并验证OpenVPN服务
# 检查系统服务单元文件
[root@openvpn-server sysctl.d]#rpm -ql openvpn|grep systemd
/usr/lib/systemd/system/openvpn-client@.service
/usr/lib/systemd/system/openvpn-server@.service
/usr/lib/systemd/system/openvpn@.service
/usr/share/doc/openvpn/README.systemd
# 查看并确认服务单元文件内容
[root@openvpn-server sysctl.d]#vim /usr/lib/systemd/system/openvpn@.service
[root@openvpn-server sysctl.d]#cat /usr/lib/systemd/system/openvpn@.service
[Unit]
Description=OpenVPN Robust And Highly Flexible Tunneling Application On %I
After=network.target
[Service]
Type=notify
PrivateTmp=true
ExecStart=/usr/sbin/openvpn --cd /etc/openvpn/ --config %i.conf
[Install]
WantedBy=multi-user.target
# 重新加载systemd配置并启动服务
[root@openvpn-server sysctl.d]#systemctl daemon-reload
[root@openvpn-server sysctl.d]#systemctl enable --now openvpn@server
# 可选:在配置文件中添加用户密码认证相关行(如需要)
22 script-security 3
23 auth-user-pass-verify /etc/openvpn/checkpsw.sh via-env
24 username-as-common-name
7、配置额外的用户密码认证(可选增强安全)
# 在服务器端配置文件中启用脚本认证
[root@openvpn-server openvpn]#vim /etc/openvpn/server.conf
22 script-security 3 # 允许运行自定义脚本
23 auth-user-pass-verify /etc/openvpn/checkpsw.sh via-env # 指定认证脚本路径
24 username-as-common-name # 使用用户名作为证书通用名
# 创建用户密码认证脚本
[root@openvpn-server ]#vim /etc/openvpn/checkpsw.sh
#!/bin/sh
###########################################################
# checkpsw.sh (C) 2004 Mathias Sundman <mathias@openvpn.se>
#
# 此脚本将根据纯文本文件对OpenVPN用户进行认证。
# 密码文件应每行包含一个用户,先是用户名,接着是一个或多个空格或制表符,然后是密码。
PASSFILE="/etc/openvpn/psw-file"
LOG_FILE="/var/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
# 为脚本添加可执行权限
[root@openvpn-server openvpn]#chmod +x checkpsw.sh
# 在客户端配置文件client.ovpn中添加以下指令以启用密码认证
auth-user-pass