SSL VPN --- (C-S)OpenVPN搭建过程

学习完SSL,我们来看一下如何构建一个基于SSL的OpenVPN。OpenVPN一般提供客户端/服务器模式,也就是客户端连接到服务器端,可以访问服务器端,但服务器端无法主动访问客户端,这是通过隧道(tunnel)来实现的。大致流程是客户端对服务器发起SSL连接,连接建立后,所有流量通过SSL传输,OpenVPN还会加上特定的报头表示这是OpenVPN报文。
有关OpenVPN的下载网站在https://www.techspot.com/downloads/5182-openvpn.html
我这里使用的是OpenVPN2.4.7,下载地址:https://files02.tchspt.com/storage2/temp/openvpn-2.4.7.tar.gz
我使用一台Linux服务器作为服务端,客户端准备了一台Linux和一台Windows。

先决条件

在Linux服务端和Linux客户端先安装一些软件,并配置服务端支持转发功能。

yum install lzo lzo-devel pam pam-devel vim wget -y
yum install openssl-devel openssl gcc gcc-c++ cmake -y

查看tun模块是否支持

modinfo tun # 查看模块信息

开启路由转发功能

vim /etc/sysctl.conf

net.ipv4.ip_forward = 1  
sysctl -p  

服务端安装过程

在服务端安装OpenVPN。

wget https://files02.tchspt.com/storage2/temp/openvpn-2.4.7.tar.gz  
tar -zxvf openvpn-2.4.7.tar.gz  
cd openvpn-2.4.7  
./configure  
make && make install 

有了软件,根据SSL握手的流程,我们还需要证书。如果不了解可以先看一下之前介绍SSL的文章,如何获取一个证书?如果只是为了测试,不值得去买一个,所以我们可以自己生成证书,具体步骤请看如何生成自签名证书

现在我们有证书了,具体要有些什么证书呢?OpenVPN如果要选证书验证的话,还必须要使用双向认证,所以这就需要把很多证书,证书链,私钥分别放到客户端和服务器。以下面这个证书链为例:

      Root.key    Root.crt
               |
    Second.key    Second.crt
               |
    Server.key    Server.crt

Root证书签发了Second证书,Second证书签发了Server证书,我们的Server证书是服务器使用的,为了方便起见,双向认证的客户端也使用Server证书。所以,两边需要放的材料如下:

  • 服务端:证书链(Second.crt+Root.crt) 证书(Server.crt) 私钥(Server.key) Diffie-Hellman参数(dh.pem)
  • 客户端:证书链(Second.crt+Root.crt) 证书(Server.crt) 私钥(Server.key)

这里两边放的内容是一样的,为什么呢,因为不管是签发流程还是证书都是一样的。应该是一种设定,使用OpenVPN的话客户端和服务端证书的签发者必须是一样的,因为没有专门的配置选项来区分客户端和服务端的CA。😐 这个dh.pem也是一个强制选项,应该是需要DH算法参与进密钥交换,但是也应该是两边都有啊,光服务端设置这么一个是有什么说法,于是抓包看一下,抓包看了之后server hello之后的报文都没有被wireshark解析出内容,这。。。

之前的博客里只有生成证书的步骤,在这里把生成dh.pem的步骤加上:

openssl dhparam -out dh.pem 1024

最后的1024是DH参数集大小,1024可以马上生成完毕,如果是2048的话就需要几分钟了。

准备好这些材料后开始编写配置文件server.conf:

local 10.0.0.45
port 8850
proto udp
dev tun
ca ./secondCA.crt
cert ./server.crt
key ./server.key
dh ./dh.pem
#push "redirect-gateway def1 bypass-dhcp"
push "route 10.0.0.0 255.255.0.0"
client-to-client
log /var/log/openvpn/openvpn.log
server 10.11.0.0 255.255.255.0
push "dhcp-option DNS 114.114.114.114"
#compress lzo
duplicate-cn
keepalive 10 120
comp-lzo
persist-key
persist-tun
log-append /var/log/openvpn/server.log
status /var/log/openvpn/status.log
verb 3
explicit-exit-notify 1

具体的含义可以看一下https://www.bbsmax.com/A/B0zqenGGzv/
explicit-exit-notify只用在UDP协议,作用是在服务端重启让客户端自动重连。
comp-lzo两端配置必须一致。

配置完成,可以启动服务端了:

/usr/local/sbin/openvpn --config server.conf

启动后,可以换一个终端查看日志:

tail -f /var/log/openvpn/openvpn.log

日志输出如下说明服务端正常启动:

Sun Apr 19 03:11:22 2020 TUN/TAP device tun0 opened
Sun Apr 19 03:11:22 2020 TUN/TAP TX queue length set to 100
Sun Apr 19 03:11:22 2020 /sbin/ifconfig tun0 10.11.0.6 pointopoint 10.11.0.5 mtu 1500
Sun Apr 19 03:11:22 2020 /sbin/route add -net 10.0.0.0 netmask 255.255.0.0 gw 10.11.0.5
Sun Apr 19 03:11:22 2020 /sbin/route add -net 10.11.0.0 netmask 255.255.255.0 gw 10.11.0.5
Sun Apr 19 03:11:22 2020 WARNING: this configuration may cache passwords in memory -- use the auth-nocache option to prevent this
Sun Apr 19 03:11:22 2020 Initialization Sequence Completed

客户端安装过程

在客户端安装OpenVPN。

wget https://files02.tchspt.com/storage2/temp/openvpn-2.4.7.tar.gz  
tar -zxvf openvpn-2.4.7.tar.gz  
cd openvpn-2.4.7  
./configure  
make && make install 

证书什么的就不赘述了,直接上配置文件client.ovpn:

client
dev tun
proto udp
remote 1.1.1.1 8850
#user nobody
#group nobody
nobind
ca ./secondCA.crt
cert server.crt
key server.key
#remote-cert-tls server
#comp-lzo
verb 3
#route nopull

具体含义请参考https://www.bbsmax.com/A/pRdBBRwPdn/
route nopull的意思是不接受服务端发来的路由,所以可以自己配。。
有些老哥可能会使用ns-cert-type server这个参数,目前ns-cert-type应该被淘汰了,应当使用remote-cert-tls。那么使用不使用这个参数是取决于证书,如果你的证书里面有extend Key usage这个extension,那可以用,如果没有,那就不要用。
keyusage.JPG

然后就可以运行客户端了,我这里以Linux作为客户端。

/usr/local/sbin/openvpn --config client.ovpn

连接的过程中会有各种报错,例如我遇到了这个错误

Sat Apr 18 21:53:44 2020 TLS Error: TLS key negotiation failed to occur within 60 seconds (check your network connectivity)
Sat Apr 18 21:53:44 2020 TLS Error: TLS handshake failed

这个错误在客户端和服务器都会出现,从字面上来说是本地判定TLS握手失败,但不代表对端也这么判定。例如,服务端校验客户端证书失败,服务端日志里会有校验失败的错误,但是客户端这里不会有什么提示,只有一个这个60s错误。所以遇到问题最好看一下双方的日志。如果是网络问题,延迟比较大,丢包严重,也会导致这个错误出现。

或者这个

Bad LZO decompression header

这是两边的压缩选项不匹配导致的。

到这一步,VPN是连上了,下面来说通信的事情。

通信过程

客户端连接上VPN的时候,会新增一个网卡,例如tun0.

tun0: flags=4305<UP,POINTOPOINT,RUNNING,NOARP,MULTICAST>  mtu 1500
        inet 10.11.0.6  netmask 255.255.255.255  destination 10.11.0.5
        inet6 fe80::c9b3:da7c:184a:45b3  prefixlen 64  scopeid 0x20<link>
        unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00  txqueuelen 100  (UNSPEC)
        RX packets 3  bytes 252 (252.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 6  bytes 396 (396.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

这个网卡地址是tunnel的地址,是服务器给客户端分配的。如果查看操作系统路由表例如route -n

[root@ip-192-168-0-186 ec2-user]# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.0.1     0.0.0.0         UG    0      0        0 eth0
10.0.0.0        10.11.0.5       255.255.0.0     UG    0      0        0 tun0
10.11.0.0       10.11.0.5       255.255.255.0   UG    0      0        0 tun0
10.11.0.5       0.0.0.0         255.255.255.255 UH    0      0        0 tun0
169.254.169.254 0.0.0.0         255.255.255.255 UH    0      0        0 eth0
192.168.0.0     0.0.0.0         255.255.255.0   U     0      0        0 eth0

也可以看到有一部分相应的路由。按照路由交换的基本法,我客户端想去访问10.0.0.156,怎么办呢,查一下路由表,发现应当走tun0这个网卡。那我tun0这个网卡连接的是一个点对点网络,这个时候也就不需要考虑网关什么其他的了,直接发就完事了。所以数据包从tun0发出去,源地址肯定是tun0了,目的地址是10.0.0.156.下一步,OpenVPN会对流经这个网卡的数据包进行处理,该封装封装,该解封解封。服务端会收到一个数据包,源地址是10.11.0.6,目的地址是10.0.0.156。这个时候如何处理呢,这就需要看服务端的了。
之前我们说过让服务端开启了转发功能ip_forward。然而这还不够,因为这个转发只是表面意义的转发,就是把这个数据包原样发出去,这样会有一个问题,我服务端知道10.11.0.6是谁,但10.0.0.156可不知道10.11.0.6是谁,这包能到,但是无法回来。那么这种情况下我们需要给服务端加一个NAT功能,让客户端访问其他网络的数据包的源地址变为服务端的IP地址。可以执行以下命令:

iptables -t nat -A POSTROUTING -s 10.11.0.0/255.255.0.0 -o eth0 -j MASQUERADE

不得不说,iptables功能确实强大,这个回头再说。这样一来,客户端就可以顺利地和其他网络通信了。


发表评论

  • OωO
  • |´・ω・)ノ
  • ヾ(≧∇≦*)ゝ
  • (☆ω☆)
  • (╯‵□′)╯︵┴─┴
  •  ̄﹃ ̄
  • (/ω\)
  • ∠(ᐛ」∠)_
  • (๑•̀ㅁ•́ฅ)
  • →_→
  • ୧(๑•̀⌄•́๑)૭
  • ٩(ˊᗜˋ*)و
  • (ノ°ο°)ノ
  • (´இ皿இ`)
  • ⌇●﹏●⌇
  • (ฅ´ω`ฅ)
  • (╯°A°)╯︵○○○
  • φ( ̄∇ ̄o)
  • (งᵒ̌皿ᵒ̌)ง⁼³₌₃
  • (ó﹏ò。)
  • Σ(っ°Д°;)っ
  • ╮(╯▽╰)╭
  • o(*
  • >﹏<
  • (。•ˇ‸ˇ•。)
  • 泡泡
  • 颜文字

*