相关原理:
1,虚拟网卡
在Linux2.4版本以上,操作系统支持一个名为tun的设备,tun设备的驱动程序中包含两个部分,一部分是字符设备驱动,一部分是网卡驱动。网卡的驱动把从TCP/IP协议栈收到的数据包结构skb放于tun设备的读取队列,用户进程通过调用字符设备接口read获得完整的IP数据包,字符驱动read函数的功能是从设备的读取队列读取数据,将核心态的skb传递给用户;反过来字符驱动write函数给用户提供了把用户态的数据写入核心态的接口,write函数把用户数据写入核心空间并穿入TCP/IP协议栈。该设备既能以字符设备的方式被读写,作为系统的虚拟网卡,也具有和物理网卡相同的特点:能够配置IP地址和路由。对虚拟网卡的使用是OpenVPN实现其SSLVPN功能的关键。
2,地址池以及路由
OpenVPN服务器一般需要配置一个虚拟IP地址池和一个自用的静态虚拟IP地址(静态地址和地址池必须在同一个子网中),然后为每一个成功建立SSL连接的客户端动态分配一个虚拟IP地址池中未分配的地址。这样,物理网络中的客户端和OpenVPN服务器就连接成一个虚拟网络上的星型结构局域网,OpenVPN服务器成为每个客户端在虚拟网络上的网关。OpenVPN服务器同时提供对客户端虚拟网卡的路由管理。当客户端对OpenVPN服务器后端的应用服务器的任何访问时,数据包都会经过路由流经虚拟网卡,OpenVPN程序在虚拟网卡上截获数据IP报文,然后使用SSL协议将这些IP报文封装起来,再经过物理网卡发送出去。OpenVPN的服务器和客户端在虚拟网卡之上建立起一个虚拟的局域网络,这个虚拟的局域网对系统的用户来说是透明的。
3,客户端与服务端安全连接的建立
OpenVPN的服务器和客户端支持tcp和udp两种连接方式,只需在服务端和客户端预先定义好使用的连接方式(tcp或udp)和端口号,客户端和服务端在这个连接的基础上进行SSL握手。连接过程包括SSL的握手以及虚拟网络上的管理信息,OpenVPN将虚拟网上的网段、地址、路由发送给客户端。连接成功后,客户端和服务端建立起SSL安全连接,客户端和服务端的数据都流入虚拟网卡做SSL的处理,再在tcp或udp的连接上从物理网卡发送出去。
4,数据包的处理过程
4.1发送数据流程
应用层的外出数据,经过系统调用接口传入核心TCP/IP层做处理,在TCP/IP经过路由到虚拟网卡,虚拟网卡的网卡驱动发送处理程序hard_start_xmit()将数据包加入skb表并完成数据包从核心区到用户区的复制,OpenVPN调用虚拟网卡的字符处理程序tun_read(),读取到设备上的数据包,对读取的数据包使用SSL协议做封装处理后,通过socket系统调用发送出去。
4.2接收数据流程
物理网卡接收数据包,经过核心TCP/IP上传到OpenVPN,OpenVPN通过link_socket_read()接收数据包,使用SSL协议进行解包处理,经过处理的数据包OpenVPN调用虚拟网卡的字符处理程序tun_write()写入虚拟网卡的字符设备,设备驱动程序完成数据从用户区到核心区的复制,并将数据写入skb链表,然后调用网卡netif_rx()接收程序,数据包再次进入系统TCP/IP协议栈,传到上层应用程序。
5,数据包的封装
OpenVPN提供tun和tap两种工作模式。在tun模式下,从虚拟网卡上收到的是不含物理帧头IP数据包,SSL处理模块对IP包进行SSL封装;在tap模式下,从虚拟网卡上收到的是包含物理帧头的数据包,SSL处理模块对整个物理帧进行SSL封装。Tap模式称为网桥模式,整个虚拟的网络就像网桥方式连接的物理网络。这种模式可以传输以太网帧、IPX、NETBIOS等数据包,应用范围更广。
6,OpenVPN与Openssl
OpenVPN软件包需要和openssl软件一起安装,因为OpenVPN调用了Openssl函数库,OpenVPN的客户端和服务端建立SSL链接的过程是通过调用Openssl来实现的。通过bio_write()/函数把数据写入Openssl的状态机通道,bio_read()从Openssl读取结果。OpenVPN还调用Openssl的加解密函数处理转发的数据包。
本次搭建环境基于CentOS 7.1
OpenVPN的源码地址:https://github.com/OpenVPN/openvpn.git
easy-rsa的源码地址:https://github.com/OpenVPN/easy-rsa.git
源码编译的环境先准备好(Development Tools, Server Platform Development, Openssl-devel,Openssl,lzo,lzo-devel,pam-devel)
还有LZO(LZO 是致力于解压速度的一种数据压缩算法,LZO 是 Lempel-Ziv-Oberhumer 的缩写。这个算法是无损算法,参考实现程序是线程安全的。 实现它的一个自由软件工具是lzop。最初的库是用 ANSI C 编写、并且遵从 GNU通用公共许可证发布的。)
接下来,开始编译安装:
在下载好的源码包中,有个INSTALL文件,里面有具体的安装步骤和注意事项,可按需进行查阅。
进入openvpn目录,并没有发现惯常的编译脚本configure,后来才知道,需要运行一个命令才能生成:
autoreconf -i -v -f //BUILD COMMANDS FROM SRC REPOSITORY CHECKOUT:
接下来可以执行configure脚本了:
./configure --prefix=/usr/local/OpenVPN --disable-lzo //如需禁用lzo,加入此参数 make make install
默认安装在/usr/local目录下,如需自定义安装路径,在执行configure脚本时,加入参数–prefix=PREFIX即可。
安装完成后,如图所示:
![图片[1]-Linux OpenVPN源码搭建-第五维](https://images2015.cnblogs.com/blog/1041742/201707/1041742-20170712105841806-998393247.png)
ln -s /usr/local/OpenVPN/sbin/openvpn /usr/sbin/openvpn
接着,我们来安装配置easy-rsa(easy-rsa is a CLI utility to build and manage a PKI CA. In laymen’s terms,this means to create a root certificate authority, and request and sign certificates, including sub-CAs and certificate revokation lists (CRL).)且基于easy-rsa3来进行配置
easy-rsa我clone到了/opt/目录下,先将其拷贝一份至openv的安装目录(/usr/local/OpenVPN)中
cp -r /opt/openvpn/easy-rsa/ /usr/local/OpenVPN/ cd /usr/local/OpenVPN/ cp vars.example vars
还有一种部署方式:
mkdir /etc/openvpn mkdir /usr/local/OpenVPN/etc ln -s /usr/local/OpenVPN/etc /etc/openvpn //之后就可以在/etc/openvpn目录中放置配置文件了
编辑vars文件,修改这几处:
![图片[2]-Linux OpenVPN源码搭建-第五维](https://images2015.cnblogs.com/blog/1041742/201707/1041742-20170714094639900-1003175379.png)
接下来,开始创建服务器端证书和Key:
![图片[3]-Linux OpenVPN源码搭建-第五维](https://images2015.cnblogs.com/blog/1041742/201707/1041742-20170714095002431-857455064.png)
创建根证书(CA):

[root@vm172-26-0-5 easyrsa3]# ./easyrsa build-ca Note: using Easy-RSA configuration from: ./vars Generating a 2048 bit RSA private key .....+++ ......................+++ writing new private key to '/usr/local/OpenVPN/easy-rsa/easyrsa3/pki/private/ca.key.EUNv9gSqxP' Enter PEM pass phrase: #输入密码,用来证书签名 Verifying - Enter PEM pass phrase: ----- 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) [Easy-RSA CA]:myhost #输入一个common name CA creation complete and you may now import and sign cert requests. Your new CA certificate file for publishing is at: /usr/local/OpenVPN/easy-rsa/easyrsa3/pki/ca.crt

创建服务器端证书:

[root@vm172-26-0-5 easyrsa3]# ./easyrsa gen-req server nopass Note: using Easy-RSA configuration from: ./vars Generating a 2048 bit RSA private key .....+++ .+++ writing new private key to '/usr/local/OpenVPN/easy-rsa/easyrsa3/pki/private/server.key.WCjfTjvd25' ----- 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]:host-server #此处的common name不要同于CA的common name Keypair and certificate request completed. Your files are: req: /usr/local/OpenVPN/easy-rsa/easyrsa3/pki/reqs/server.req key: /usr/local/OpenVPN/easy-rsa/easyrsa3/pki/private/server.key

签约服务器端证书

[root@vm172-26-0-5 easyrsa3]# ./easyrsa sign server server Note: using Easy-RSA configuration from: ./vars 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 3650 days: subject= commonName = host-server Type the word 'yes' to continue, or any other input to abort. Confirm request details: yes #在此输入yes Using configuration from ./openssl-1.0.cnf Enter pass phrase for /usr/local/OpenVPN/easy-rsa/easyrsa3/pki/private/ca.key: #输入CA的数字签名密码 Check that the request matches the signature Signature ok The Subject's Distinguished Name is as follows commonName :PRINTABLE:'host-server' Certificate is to be certified until Jul 12 02:07:19 2027 GMT (3650 days) Write out database with 1 new entries Data Base Updated Certificate created at: /usr/local/OpenVPN/easy-rsa/easyrsa3/pki/issued/server.crt

创建Diffie-Hellman,确保key穿越不安全网络的命令:

[root@vm172-26-0-5 easyrsa3]# ./easyrsa gen-dh Note: using Easy-RSA configuration from: ./vars 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 /usr/local/OpenVPN/easy-rsa/easyrsa3/pki/dh.pem

紧接着,创建客户端证书:
自己额外找个路径存放client端的相关信息,此处演示我在/root目录下创建了client目录,并将easy-rsa复制一份至此目录,然后执行初始化操作:

[root@vm172-26-0-5 easyrsa3]# ./easyrsa init-pki Note: using Easy-RSA configuration from: ./vars #由于此处我复制的服务器端改好的easy-rsa,所以此处提示我是否进行初始化;确定无误后,输入yes进行初始化即可。 WARNING!!! You are about to remove the EASYRSA_PKI at: /root/client/easy-rsa/easyrsa3/pki and initialize a fresh PKI here. Type the word 'yes' to continue, or any other input to abort. Confirm removal: yes init-pki complete; you may now create a CA or requests. Your newly created PKI dir is: /root/client/easy-rsa/easyrsa3/pki

生成客户端证书生成请求和key:

[root@vm172-26-0-5 easyrsa3]# ./easyrsa gen-req Ops Note: using Easy-RSA configuration from: ./vars Generating a 2048 bit RSA private key .+++ ...............................+++ writing new private key to '/root/client/easy-rsa/easyrsa3/pki/private/Ops.key.8uUfDoiUPb' Enter PEM pass phrase: #输入密码 Verifying - Enter PEM pass phrase: ----- 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) [client_one]:Ops #输入common name Keypair and certificate request completed. Your files are: req: /root/client/easy-rsa/easyrsa3/pki/reqs/Ops.req key: /root/client/easy-rsa/easyrsa3/pki/private/Ops.key

将生成的客户端请求导入并签约:

cd /usr/local/OpenVPN/easy-rsa/easyrsa3 ./easyrsa import-req /root/client/easy-rsa/easyrsa3/pki/reqs/Ops.req Ops #显示如下: Note: using Easy-RSA configuration from: ./vars The request has been successfully imported with a short name of: Ops You may now use this name to perform signing operations on this request.

签约客户端证书:

[root@vm172-26-0-5 easyrsa3]# ./easyrsa sign client Ops Note: using Easy-RSA configuration from: ./vars 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 client certificate for 3650 days: subject= commonName = Ops Type the word 'yes' to continue, or any other input to abort. Confirm request details: yes #输入yes Using configuration from ./openssl-1.0.cnf Enter pass phrase for /usr/local/OpenVPN/easy-rsa/easyrsa3/pki/private/ca.key: #输入CA的数字签名密码 Check that the request matches the signature Signature ok The Subject's Distinguished Name is as follows commonName :PRINTABLE:'Ops' Certificate is to be certified until Jul 12 03:15:23 2027 GMT (3650 days) Write out database with 1 new entries Data Base Updated Certificate created at: /usr/local/OpenVPN/easy-rsa/easyrsa3/pki/issued/Ops.crt #客户端证书生成

至此,客户端和服务端的证书已经配置完毕,接下来将相关文件拷贝至openvpn安装目录下:
拷贝服务器密钥及证书等到openvpn目录:都在pki目录下
cp ca.crt /usr/local/OpenVPN/ cp private/server.key /usr/local/OpenVPN/ cp issued/server.crt /usr/local/OpenVPN/ cp dh.pem /usr/local/OpenVPN/
拷贝客户端证书和秘钥至客户端client目录:
cp ca.crt /root/client/ cp issued/Ops.crt /root/client/ cp /root/client/easy-rsa/easyrsa3/pki/private/Ops.key /root/client/
为服务端编写openvpn的配置文件:
复制样例配置文件至openvpn的安装目录:
cp /opt/openvpn/openvpn/sample-config-files/server.conf ./
核心的几个配置:

[root@vm172-26-0-5 openvpn]# grep -v ^# server.conf | grep -v ^$ | grep -v ^\; port 1194 proto udp dev tun ca /etc/openvpn/ca.crt cert /etc/openvpn/server.crt key /etc/openvpn/server.key # This file should be kept secret dh /etc/openvpn/dh.pem server 10.8.0.0 255.255.255.0 ifconfig-pool-persist ipp.txt keepalive 10 120 comp-lzo persist-key persist-tun status /logs/openvpn/openvpn-status.log verb 5

然后在/etc/openvpn目录下运行:
# 生成ta.key文件(防DDos攻击、UDP淹没等恶意攻击) openvpn --genkey --secret ta.key
之后就可以启动服务了:
openvpn /etc/openvpn/server.conf
服务器端配置基本完成
客户端配置:
将服务器端的Client目录下的ca.crt,Ops.crt,Opt.key和/etc/openvpn/ta.key放到客户端的openvpn的config目录下,并修改client的配置文件:

client dev tun proto udp remote 120.X2.1X5.XXX 1194 resolv-retry infinite nobind persist-key persist-tun ca ca.crt cert Ops.crt key Ops.key comp-lzo verb 5

然后就可以通过密码进行连接了,如果图标变绿则证明连接成功,否则根据对应的报错信息进行排查。
如果出现TLS相关报错,大体有两类,一类是TLS handshake失败,造成此原因大概是配置过程的证书出错或配置文件错误。
另一类错误是有三行报错,本人没有及时截图,此错误把客户端的配置文件中的ns-cert-type server 注释掉即可。