OPENVPN 实践
VPN(全称Virtual Private Network)虚拟专用网络,是依靠ISP和其他的NSP,在公共网络中建立专用的数据通信网络的技术,可以为企业之间或者个人与企业之间提供安全的数据传输隧道服务。在VPN中任意两点之间的连接并没有传统专网所需的端到端的物理链路,而是利用公共网络资源动态组成的,可以理解为通过私有的隧道技术在公共数据网络上模拟出来的和专网有同样功能的点到点的专线技术。
操作均在 Ubuntu 18.04 server 下。
安装
安全前需要先安装 openssl 和 lzo。
$ sudo apt install openssl # oepnssl
$ sudo apt install libssl-dev
$ sudo apt install liblzo2-2 # lzo
$ sudo apt install liblzo2-dev
$ sudo apt install libpam0g-dev # pam
$ apt install openvpn
$ apt install easy-rsa
依靠 openvpn 实现 vpn 服务,依靠 easy-rsa 实现加密认证。
当通过 apt 安装完成后,主要的目录有:
- /usr/sbin/openvpn 执行的二进制程序
- /etc/openvpn openvpn 需要的相关配置文件
- /usr/share/doc/openvpn openvpn 的帮助文档以及示例配置文件
- /usr/share/easy-rsa easy-rsa 生成密钥对的位置
生成密钥
配置 vars
首先需要修改 vars 文件。
$ cd /usr/share/easy-rsa
$ vi vars
vars 文件不改也能用,但是缺省的信息后期很难管理,生成的证书也没有和自身相关的部分,所以推荐修改以下参数。
# /usr/share/easy-rsa/vars
export KEY_COUNTRY="CN"
export KEY_PROVINCE="ZJ"
export KEY_CITY="hangzhou"
export KEY_ORG="xxxx"
export KEY_EMAIL="aaa@xxx.com"
根据自己的需求来修改,注意的是,当改完了这些内容后直接进行后续操作时,可能会遇到:
**************************************************************
No /usr/share/easy-rsa/openssl.cnf file could be found
Further invocations will fail
**************************************************************
我们也可以看到当前目录下确实没有 openssl.cnf,但是有 openssl-0.9.6.cnf,openssl-0.9.8.cnf,openssl-1.0.0.cnf。一般情况下,可以理解为 openssl-1.0.0.cnf 就是 openssl.cnf 文件,但是我们需要通过环境变量来指定。
# /usr/share/easy-rsa/vars
# 注释一行,新增一行
# export KEY_CONFIG=`$EASY_RSA/whichopensslcnf $EASY_RSA`
export KEY_CONFIG=$EASY_RSA/openssl-1.0.0.cnf
当 vars 配置完成后,通过下面的指令导入环境以及清理环境。
$ source vars
$ ./clean-all
生成 ca 证书
root@localhost:/usr/share/easy-rsa# ./build-ca
Can't load /root/.rnd into RNG
140106201145792:error:2406F079:random number generator:RAND_load_file:Cannot open file:../crypto/rand/randfile.c:88:Filename=/root/.rnd
Generating a RSA private key
..............................................+++++
......................+++++
writing new private key to 'ca.key'
-----
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.
-----
Country Name (2 letter code) [US]:
State or Province Name (full name) [CA]:
Locality Name (eg, city) [SanFrancisco]:
Organization Name (eg, company) [Fort-Funston]:
Organizational Unit Name (eg, section) [MyOrganizationalUnit]:
Common Name (eg, your name or your server's hostname) [Fort-Funston CA]:
Name [EasyRSA]:
Email Address [me@myhost.mydomain]:
root@localhost:/usr/share/easy-rsa# ll keys/
total 20
drwx------ 2 root root 4096 Apr 7 02:35 ./
drwxr-xr-x 3 root root 4096 Apr 7 02:33 ../
-rw-r--r-- 1 root root 1850 Apr 7 02:35 ca.crt
-rw------- 1 root root 1704 Apr 7 02:35 ca.key
-rw-r--r-- 1 root root 0 Apr 7 02:33 index.txt
-rw-r--r-- 1 root root 3 Apr 7 02:33 serial
在这个过程中,会有几次交互,让你确认或输入相关信息,如果已经在 vars 里配置好了,那么这里就直接回车好了,否则需要根据每一项单独输入。
生成服务器端证书和密钥 key 文件
root@xxx:/usr/share/easy-rsa# ./build-key-server server
Can't load /root/.rnd into RNG
140287103832512:error:2406F079:random number generator:RAND_load_file:Cannot open file:../crypto/rand/randfile.c:88:Filename=/root/.rnd
Generating a RSA private key
.....................................................................................................................................................................................................................+++++
...............+++++
writing new private key to 'server.key'
-----
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.
-----
Country Name (2 letter code) [US]:
State or Province Name (full name) [CA]:
Locality Name (eg, city) [SanFrancisco]:
Organization Name (eg, company) [Fort-Funston]:
Organizational Unit Name (eg, section) [MyOrganizationalUnit]:
Common Name (eg, your name or your server's hostname) [server]:
Name [EasyRSA]:
Email Address [me@myhost.mydomain]:
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
Using configuration from /usr/share/easy-rsa/openssl-1.0.0.cnf
Can't load /root/.rnd into RNG
140162557669824:error:2406F079:random number generator:RAND_load_file:Cannot open file:../crypto/rand/randfile.c:88:Filename=/root/.rnd
Can't open /usr/share/easy-rsa/keys/index.txt.attr for reading, No such file or directory
140162557669824:error:02001002:system library:fopen:No such file or directory:../crypto/bio/bss_file.c:72:fopen('/usr/share/easy-rsa/keys/index.txt.attr','r')
140162557669824:error:2006D080:BIO routines:BIO_new_file:no such file:../crypto/bio/bss_file.c:79:
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
countryName :PRINTABLE:'US'
stateOrProvinceName :PRINTABLE:'CA'
localityName :PRINTABLE:'SanFrancisco'
organizationName :PRINTABLE:'Fort-Funston'
organizationalUnitName:PRINTABLE:'MyOrganizationalUnit'
commonName :PRINTABLE:'server'
name :PRINTABLE:'EasyRSA'
emailAddress :IA5STRING:'me@myhost.mydomain'
Certificate is to be certified until Apr 5 06:44:37 2030 GMT (3650 days)
Sign the certificate? [y/n]:y
1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated
root@xxx:/usr/share/easy-rsa# ll keys/
total 56
drwx------ 2 root root 4096 Apr 7 02:44 ./
drwxr-xr-x 3 root root 4096 Apr 7 02:39 ../
-rw-r--r-- 1 root root 5784 Apr 7 02:44 01.pem
-rw-r--r-- 1 root root 1850 Apr 7 02:42 ca.crt
-rw------- 1 root root 1704 Apr 7 02:42 ca.key
-rw-r--r-- 1 root root 149 Apr 7 02:44 index.txt
-rw-r--r-- 1 root root 21 Apr 7 02:44 index.txt.attr
-rw-r--r-- 1 root root 0 Apr 7 02:39 index.txt.old
-rw-r--r-- 1 root root 3 Apr 7 02:44 serial
-rw-r--r-- 1 root root 3 Apr 7 02:39 serial.old
-rw-r--r-- 1 root root 5784 Apr 7 02:44 server.crt
-rw-r--r-- 1 root root 1098 Apr 7 02:44 server.csr
-rw------- 1 root root 1704 Apr 7 02:44 server.key
中间的很多信息也是可以回车掠过,注意在最后需要手动确认两次。当执行完成后,会在 keys 目录下出现,server.crt,server.csr,server.key 和 01.pem 等文件。
生成客户端证书和 key 文件
可以通过 easy-rsa/build-key 生成单个用户专属的证书和 key,每个证书在同一时刻只能供一个客户端连接。下面会生成一个不需要密码的账户 test。
root@localhost:/usr/share/easy-rsa# ./build-key test
Can't load /root/.rnd into RNG
139841275879872:error:2406F079:random number generator:RAND_load_file:Cannot open file:../crypto/rand/randfile.c:88:Filename=/root/.rnd
Generating a RSA private key
...+++++
.......................+++++
writing new private key to 'test.key'
-----
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.
-----
Country Name (2 letter code) [US]:
State or Province Name (full name) [CA]:
Locality Name (eg, city) [SanFrancisco]:
Organization Name (eg, company) [Fort-Funston]:
Organizational Unit Name (eg, section) [MyOrganizationalUnit]:
Common Name (eg, your name or your server's hostname) [test]:
Name [EasyRSA]:
Email Address [me@myhost.mydomain]:
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
Using configuration from /usr/share/easy-rsa/openssl-1.0.0.cnf
Can't load /root/.rnd into RNG
139913049067968:error:2406F079:random number generator:RAND_load_file:Cannot open file:../crypto/rand/randfile.c:88:Filename=/root/.rnd
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
countryName :PRINTABLE:'US'
stateOrProvinceName :PRINTABLE:'CA'
localityName :PRINTABLE:'SanFrancisco'
organizationName :PRINTABLE:'Fort-Funston'
organizationalUnitName:PRINTABLE:'MyOrganizationalUnit'
commonName :PRINTABLE:'test'
name :PRINTABLE:'EasyRSA'
emailAddress :IA5STRING:'me@myhost.mydomain'
Certificate is to be certified until Apr 5 06:52:46 2030 GMT (3650 days)
Sign the certificate? [y/n]:y
1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated
root@localhost:/usr/share/easy-rsa# ll keys/
total 88
drwx------ 2 root root 4096 Apr 7 02:52 ./
drwxr-xr-x 3 root root 4096 Apr 7 02:39 ../
-rw-r--r-- 1 root root 5784 Apr 7 02:44 01.pem
-rw-r--r-- 1 root root 5654 Apr 7 02:52 02.pem
-rw-r--r-- 1 root root 1850 Apr 7 02:42 ca.crt
-rw------- 1 root root 1704 Apr 7 02:42 ca.key
-rw-r--r-- 1 root root 296 Apr 7 02:52 index.txt
-rw-r--r-- 1 root root 21 Apr 7 02:52 index.txt.attr
-rw-r--r-- 1 root root 21 Apr 7 02:44 index.txt.attr.old
-rw-r--r-- 1 root root 149 Apr 7 02:44 index.txt.old
-rw-r--r-- 1 root root 3 Apr 7 02:52 serial
-rw-r--r-- 1 root root 3 Apr 7 02:44 serial.old
-rw-r--r-- 1 root root 5784 Apr 7 02:44 server.crt
-rw-r--r-- 1 root root 1098 Apr 7 02:44 server.csr
-rw------- 1 root root 1704 Apr 7 02:44 server.key
-rw-r--r-- 1 root root 5654 Apr 7 02:52 test.crt
-rw-r--r-- 1 root root 1098 Apr 7 02:52 test.csr
-rw------- 1 root root 1704 Apr 7 02:52 test.key
在这中间的提示输入项,可以根据需求填写。和 server key 类似,最后手动确认。会生成 02.pem,test.crt,test.csr,test.key 等文件。
生成 vpn 密钥协议交换文件
root@localhost:/usr/share/easy-rsa# ./build-dh
Generating DH parameters, 2048 bit long safe prime, generator 2
This is going to take a long time
......++++++.......
root@localhost:/usr/share/easy-rsa# ll keys/
total 92
drwx------ 2 root root 4096 Apr 7 02:56 ./
drwxr-xr-x 3 root root 4096 Apr 7 02:39 ../
-rw-r--r-- 1 root root 5784 Apr 7 02:44 01.pem
-rw-r--r-- 1 root root 5654 Apr 7 02:52 02.pem
-rw-r--r-- 1 root root 1850 Apr 7 02:42 ca.crt
-rw------- 1 root root 1704 Apr 7 02:42 ca.key
-rw-r--r-- 1 root root 424 Apr 7 02:58 dh2048.pem
-rw-r--r-- 1 root root 296 Apr 7 02:52 index.txt
-rw-r--r-- 1 root root 21 Apr 7 02:52 index.txt.attr
-rw-r--r-- 1 root root 21 Apr 7 02:44 index.txt.attr.old
-rw-r--r-- 1 root root 149 Apr 7 02:44 index.txt.old
-rw-r--r-- 1 root root 3 Apr 7 02:52 serial
-rw-r--r-- 1 root root 3 Apr 7 02:44 serial.old
-rw-r--r-- 1 root root 5784 Apr 7 02:44 server.crt
-rw-r--r-- 1 root root 1098 Apr 7 02:44 server.csr
-rw------- 1 root root 1704 Apr 7 02:44 server.key
-rw-r--r-- 1 root root 5654 Apr 7 02:52 test.crt
-rw-r--r-- 1 root root 1098 Apr 7 02:52 test.csr
-rw------- 1 root root 1704 Apr 7 02:52 test.key
会等待一段时间,生成 keys/dh2048.pem 文件。
生成 HMAC firewall
$ openvpn --genkey --secret keys/ta.key
至此,密钥和证书生成工作已完成。
证书及密钥说明表
Filename | Needed By | Purpose | Secret |
---|---|---|---|
ca.crt | server+all client | Root CA ertificate | No |
ca.key | key signing machine only | Root CA key | YES |
dh{n}.pem | server only | Diffie Hellman parameters | NO |
server.crt | server only | Server Certificate | NO |
server.key | server only | Server Key | YES |
clientl.crt | clientl only | Clientl Certificate | NO |
clientl.key | clientl only | Clientl Key | YES |
配置 server 端
我们进入到 /etc/openvpn 目录,开始操作。先拷贝一些文件到该目录下。
$ cp -r /usr/share/easy-rsa/keys /etc/openvpn
$ cp /usr/share/doc/openvpn/examples/sample-config-files/server.conf /etc/openvpn
然后修改 /etc/openvpn/server.conf 文件。
# /etc/openvpn/server.conf 修改后的结果
local aaa.bbb.ccc.ddd
port 1194
proto tcp
dev tun
ca /etc/openvpn/keys/ca.crt
cert /etc/openvpn/keys/server.crt
dh /etc/openvpn/keys/dh2048.pem
server 10.8.0.0 255.255.255.0
ifconfig-pool-persist ipp.txt
push "route 192.168.1.0 255.255.255.0"
push "redirect-gateway def1 bypass-dhcp"
push "dhcp-option DNS 114.114.114.114"
client-to-client
duplicate-cn
keepalive 20 120
cipher AES-256-CBC
comp-lzo
user xxx
group xxx
persist-key
persist-tun
status openvpn-status.log
log openvpn.log
verb 3
配置参数 | 参数说明 |
---|---|
local 10.0.0.28(外网卡地址) | 哪一个本地地址要被 openvpn 进行监听 |
port 1194 | 监听的端口,默认是 1194 |
proto tcp | 指定监听的协议,当并发访问多时,推荐tcp |
dev tun | vpn server 的模式采用路由模式,可选 tap 或 tun |
ca /etc/openvpn/keys/ca.crt | ca 证书 |
cert /etc/openvpn/keys/server.crt | |
key /etc/openvpn/keys/server.key | this file should be kept secret |
dh /etc/openvpn/keys/dh2048.pem | vpn 密钥协议交换文件 |
server 10.8.0.0 | 这个是 vpn server 动态分配给 vpn client 的地址池,一般不需要更改。这个段不要和任何网络地址段冲突或者重复。 |
ifconfig-pool-persist ipp.txt | |
push “route 172.16.1.0 255.255.255.0” | 这个是 vpn server 所在的内网网段,如果有多个可以写多个 push,注意,此命令实际作用是在 vpn client 本地生成 vpn sever 所在的内网网段路由,确保能够和 vpn server 所在的内网网段通信 |
client-to-client | 允许拨号的多个 vpn client 互相通信 |
duplicate-cn | 允许多个客户端使用同一个帐号连接 |
keepalive 10 20 | 每 10 秒 ping 一次,若是 120 秒未收到包,即认定客户端断线 |
comp-lzo | 开启压缩功能 |
persist-key | 当 vpn 超时后,当重新启动 vpn 后,保持上一次使用的私钥,而不重新读取私钥 |
persist-tun | 通过 keepalive 检测 vpn 超时后,当重新启动 vpn 后,保持 tun 或者 tap 设备自动连接状态 |
status openvpn-status.log | openvpn日志状态信息 |
log /var/log/openvpn.log | 日志文件 |
verb 3 | 指定日志文件冗余 |
server.cnf 文件配置完毕后,进行启动服务等操作。
# 关闭防火墙
root@xxx:/etc/openvpn# systemctl stop ufw.service
root@xxx:/etc/openvpn# systemctl status ufw.service
● ufw.service - Uncomplicated firewall
Loaded: loaded (/lib/systemd/system/ufw.service; enabled; vendor preset: enabled)
Active: inactive (dead) since Fri 2020-04-03 18:02:19 CST; 3 days ago
Docs: man:ufw(8)
Process: 14664 ExecStop=/lib/ufw/ufw-init stop (code=exited, status=0/SUCCESS)
Main PID: 351 (code=exited, status=0/SUCCESS)
Apr 03 18:02:19 xxx systemd[1]: Stopping Uncomplicated firewall...
Apr 03 18:02:19 xxx ufw-init[14664]: Skip stopping firewall: ufw (not enabled)
Apr 03 18:02:19 xxx systemd[1]: Stopped Uncomplicated firewall.
Warning: Journal has been rotated since unit was started. Log output is incomplete or unavailable.
# 开启内核转发
root@xxx:/etc/openvpn# sed -i 's#net.ipv4.ip_forward = 0#net.ipv4.ip_forward = 1#' /etc/sysctl.conf
root@xxx:/etc/openvpn# sysctl -p
# 启动服务
root@xxx:/etc/openvpn# /usr/sbin/openvpn --config /etc/openvpn/server.conf &
# 查看虚拟网卡
root@xxx:/etc/openvpn# ip a|grep tun
11: tun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UNKNOWN group default qlen 100
link/none
inet 10.8.0.1 peer 10.8.0.2/32 scope global tun0
valid_lft forever preferred_lft forever
inet6 fe80::ce49:7fb3:9eed:9e40/64 scope link stable-privacy
valid_lft forever preferred_lft forever
# 盯着日志,准备数据接入
root@xxx:/etc/openvpn# tail -f /var/log/openvpn.log
配置 client 端
客户端需要安装软件。根据 2x HOW TO 提到的,不同的平台有不同的 GUI 程序,即便是 Linux 也可以通过编译源码的方式来安装。这里以 macOS 为基准,主要解释如何配置。
下载 OpenVPN Connect,启动。
从安装好 openvpn 服务的机器上拉取 client.cfg 和必要的key。
$ scp ca.crt test.crt test.key user@yourip:~/openvpn/keys
$ vi ~/openvpn/client.ovpn
client # 代表客户端
dev tun # 和 server 端一致
proto tcp # 和 server 端一致
remote xxx.xxx.xxx.xxx 1194 # server.conf 里的 local
resolv-retry infinite
nobind # 不绑定
persist-key # 保持 key
persist-tun # 保持 tun
ca ~/keys/ca.crt
cert ~/keys/test.crt
key ~/keys/test.key
ns-cert-type server
comp-lzo
verb 3
双击这个 client.ovpn 文件或者在软件 OpenVPN Connect 中选择导入,点击连接即可。此时可以观察多了一块虚拟网卡,默认分配 ip 为 10.8.0.6。
通过 ping 做测试。
$ ping -I tun0 10.8.0.1
PING 10.8.0.1 (10.8.0.1) from 10.8.0.6 tun0: 56(84) bytes of data.
64 bytes from 10.8.0.1: icmp_seq=1 ttl=64 time=1.00 ms
64 bytes from 10.8.0.1: icmp_seq=2 ttl=64 time=1.37 ms
^C
--- 10.8.0.1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 1.009/1.193/1.377/0.184 ms
可见 VPN 确实连接上了,但是尝试通过 tun0 口访问 baidu.com 是失败的。一开始以为是防火墙设置的问题,再以为是路由表配置的问题,三以为不转发的问题。查了很多资料,不得其解,又是一个未解之谜,等一个询问专业人士。
参考
- 企业VPN办公环境搭建(阿里云ECS上搭建OpenVPN服务器)
- vpn简介及openvpn搭建
- [Solved] openssl.cnf not found in easy-rsa/2.0
- Error in setting up CA during OpenVPN configuration on Ubuntu Server 12.04 x64
- OpenVPN客户端(Windows/Linux/MacOS)连接OpenVPN服务器
关于连接 VPN 但无法连接互联网的问题: