带你读《Linux系统安全:纵深防御、安全扫描与入侵检测》之三:虚拟专用网络-阿里云开发者社区

开发者社区> 华章出版社> 正文
登录阅读全文

带你读《Linux系统安全:纵深防御、安全扫描与入侵检测》之三:虚拟专用网络

简介: 这是一部从技术原理、工程实践两个方面系统、深入讲解Linux系统安全的著作,从纵深防御、安全扫描、入侵检测3个维度细致讲解了如何构建一个如铜墙铁壁般的Linux防护体系。作者是资深的Linux系统安全专家、运维技术专家,有13年的从业经验,厚积薄发。本书得到了来自腾讯、阿里等知名企业的多位行业专家的高度评价。全书不仅包含大量工程实践案例,而且为各种核心知识点绘制了方便记忆的思维导图。

点击查看第一章
点击查看第二章

第3章 虚拟专用网络

虚拟专用网络(Virtual Private Network,VPN)架设在公共共享的互联网基础设施上,在非受信任的网络上建立私有的和安全的连接,把分布在不同地域的信息基础设施、办公场所、用户或者商业伙伴互联起来。
虚拟专用网络使用加密技术为通信提供安全保护,以对抗对通信内容的窃听和主动的攻击。虚拟专用网络在今天被广泛地使用到远程互联中。在虚拟专用网络技术出现之前,不同办公场所互联组建专用私有网络时,企业往往需要投入较大的成本用于租赁专用线路。虚拟专用网络的出发点是建立虚拟的专用链路,在互联网上进行传输并使用加密技术进行通信安全防护。
虚拟专用网络的使用场景如下。

  • 安全互联:把多个分布在不同地域的服务器或者网络安全地连接起来。
  • 指定网络流量路由:把多个点之间的网络流量通过虚拟专用网络的隧道进行连接后,可以使用动态路由等优化内部网络通信。
  • 匿名访问:通过虚拟专用网络通道,可以隐藏客户端的来源IP地址,在某些场景下可以起到保护用户的作用。

本章将首先概要描述目前使用比较多的各种虚拟专用网络构建技术、方案和原理,然后重点讲解使用OpenVPN构建企业级虚拟专用网络的最佳方案,深入研究其中的核心配置参数,最后对OpenVPN的排错思路和方法进行指导。

3.1 常见虚拟专用网络构建技术

目前我们在实践中,经常遇到的虚拟专用网络构建技术,大致上分以下3类:

  • 点到点的隧道协议(Point-to-Point Tunneling Protocol,PPTP)虚拟专用网络
  • 互联网协议安全(Internet Protocol Security,IPSec)虚拟专用网络
  • 安全接口层/安全传输层协议(Secure Sockets Layer/Transport Layer Security,SSL/TLS)虚拟专用网络

3.1.1 PPTP虚拟专用网络的原理

PPTP使用建立于TCP之上的通道来进行控制,使用通用路由封装协议(Generic Routing Encapsulation,GRE)隧道技术来封装点到点协议(Point to Point Protocol,PPP)包。PPTP规范里面没有描述加密和认证的特性,它依赖于底层的PPP协议来实现数据安全的功能。
PPTP的第1个隧道首先通过和对端服务器的TCP 1723端口进行通信来建立。在该TCP连接建立后,再创建第2个隧道GRE来进行数据传输。在RFC 2673中详细描述了PPTP协议的控制和数据通信过程。
在Linux环境中,我们可以使用pptpd进行PPTP虚拟专用网络的架设。

3.1.2 IPSec虚拟专用网络的原理

IPSec是一组基于IP协议的协议组。它使得两台或者多台主机之间通过认证和加密每个IP包以一个安全的方式进行通信。IPSec由以下协议组成。

  • 封装的安全负荷(Encapsulated Security Payload,ESP):通过使用对称加密算法(比较常用的,如Blowfish和3DES)来加密通信内容,以防止被第三方窃听和干扰通信。
  • 认证头部(Authentication Header,AH):通过计算校验和的方式来对通信双方的数据进行认证,防止被第三方篡改。
  • IP负荷压缩协议(IP Payload Compression Protocol,IPComp):通过压缩IP的负荷来减少数据通信量,提高性能。

IPSec虚拟专用网络有以下两种工作模式。

  • 传输模式:仅IP数据负荷被加密,IP和路由信息不做修改。这个模式主要在两个服务器之间进行Host-to-Host加密通信时使用。
  • 隧道模式:整个IP包都被加密。这个模式主要在不同网络之间构建Network-to-Network的虚拟专用网络时使用。
    在Linux环境中,使用范围比较多的IPSec虚拟专用网络实现方案是strongSwan(官方网站:https://www.strongswan.org )和FreeS/WAN(官方网站:https://www.freeswan.org )。

IPSec也是大部分商业硬件防火墙或者路由器所支持的VPN构建协议。

3.1.3 SSL/TLS虚拟专用网络的原理

SSL/TLS虚拟专用网络的工作过程如下。

  • 认证过程:在SSL/TLS的握手过程中,客户端和服务器端分别使用对方的证书来进行认证。
  • 加密过程:在SSL/TLS的握手过程中,客户端和服务器端使用非对称算法计算出对称密钥进行数据加密。

SSL/TLS虚拟专用网络主要使用了以下的虚拟设备。
tun/tap设备:Linux中提供了两种虚拟网络设备tun/tap设备。通过对这两种设备的读写操作,实现内核与用户态程序的交互。
在Linux环境中,SSL/TLS虚拟专用网络的典型代表是OpenVPN(OpenVPN项目的官方网站是https://openvpn.net )。
对于3.1.1节、3.1.2节和本节中讲到的3种虚拟专用网络技术来说,其各有特点。总的来说:

  • PPTP需要建立两个隧道进行通信,控制和数据传输分离,其中传输数据使用GRE。在同一个局域网里面的多个内网主机需要建立多条GRE通道连接到同一台虚拟专用网络服务器时,需要在防火墙或者网络地址转换设备上进行特殊设置,以增加对Call ID的支持,否则会导致隧道建立失败。
  • IPSec虚拟专用网络是一个成熟的方案,但其配置较复杂,学习成本较高。IPSec虚拟专用网络在商业硬件设备上实现得较多。
  • SSL/TLS虚拟专用网络工作在用户态,不需要对内核做特殊的修改,可移植性较高;且配置简单,学习成本低。接下来的章节将重点介绍该开源虚拟专用网络软件的最佳配置。

3.2 深入理解OpenVPN的特性

使用OpenVPN,我们可以实现以下功能:

  • 对任何IP子网或者虚拟以太网通过一个UDP或者TCP端口来建立隧道。
  • 架构一个可扩展的、负载均衡的虚拟专用网络集群系统,同时支持来自上千用户的连接。
  • 使用任意的加密算法、密钥长度或者HMAC摘要。这些功能是使用OpenSSL库来实现的。
  • 可以选择最简单的静态密码的传统加密算法或者基于证书的公钥私钥加密算法。
  • 对数据流进行实时压缩。
  • 支持对端节点通过动态方法获取IP地址,例如DHCP等。
  • 对于面向连接的有状态防火墙,不需要使用特殊的设置。
  • 支持网络地址转换。
  • 在Windows或者mac OS上提供GUI工具,方便配置。

3.3 使用OpenVPN创建点到点的虚拟专用网络

在某些运维场景中,我们会遇到只需要把两台处于Internet上的服务器使用虚拟专用网络互联起来的需求,比如远程的SNMP信息抓取、远程数据库备份等。
在这种情况下,我们可以使用OpenVPN来创建点到点(Peer-to-Peer)的虚拟专用网络的物理架构,如图3-1所示。

image.png

创建点到点模式的虚拟专用网络的操作步骤如下。
1)在两台需要互联的服务器x.y.z.28和a.b.c.239上都执行如下安装操作。

#下载epel的扩展仓库,其中提供了OpenVPN的rpm包
wget https://dl.fedoraproject.org/pub/epel/epel-release-latest-6.noarch.rpm
#安装epel的rpm包
rpm -ivh epel-release-latest-6.noarch.rpm
#安装OpenVPN前,需要安装OpenVPN的依赖库(lzo库用于压缩;openssl库用于支持加密和证书认证)
yum -y install lzo lzo-devel openssl openssl-devel
#安装OpenVPN
yum -y install openvpn

2)在服务器x.y.z.28上生成静态密码。使用的命令如下。

openvpn --genkey --secret key

key的内容如下:

#
# 2048 bit OpenVPN static key
#
-----BEGIN OpenVPN Static key V1-----
8acc8d8feae2fc13ec66fac4eabc72b8
10fa75f239e8cd77d0cec0361dd77046
c6e757c9ed392410b6671899229983cc
6c85f9a3449ae6847fb569559bdebd93
bfecdf00bee63453e2cac80e4429e98d
3162eae826837836fe37959fd96040c4
445b568028e8cc251e557d3ce39b88e2
385af0b64bcb7860bc133859bcd9a8da
63f2729b1f5ebf003cb26005249dcf03
9fd37cba370af73be523ad549a3df6b5
b53f441e674f8e05201f051ce66f2f87
83c3c33fd29cf7bfb85be3370ee00c07
a8e7227e78557155fb365c812570d8bf
c0bf845a7c24abc262de77a68567d1b2
afc96447fcfc1e3286f18a22512abfa3
f68bcd0bfe892fa14848166bc1b36bac
-----END OpenVPN Static key V1-----

3)使用scp把该key文件传到对端a.b.c.239服务器上。
4)创建隧道。
在服务器x.y.z.28上执行以下命令。

openvpn --remote a.b.c.239--dev tun0 --ifconfig 10.6.0.1 10.6.0.2 --secret key --daemon

在对端服务器a.b.c.239上执行以下命令。

openvpn --remote x.y.z.28 --dev tun0 --ifconfig 10.6.0.2 10.6.0.1 --secret key --daemon

其中的关键配置项解释如下:

  • --remote,指定点到点架构中对端的公网IP。
  • --dev,指定使用tun设备。
  • --ifconfig,指定虚拟隧道的本端和远端IP地址。
  • --secret,指定包含静态密码的文件。
  • --daemon,指定使用后台驻守进程的模式。

执行步骤4后,两台服务器之间的虚拟专用网络如图3-2所示。

image.png

5)验证隧道功能。
在服务器x.y.z.28上执行以下命令。

ping 10.6.0.2 -c 2

在a.b.c.239使用tcpdump可以看到以下输出。

tcpdump -vvv -nnn -i tun0 icmp 
tcpdump: listening on tun0, link-type RAW (Raw IP), capture size 65535 bytes
10:07:04.031236 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto ICMP (1), length 84)
    10.6.0.1 > 10.6.0.2: ICMP echo request, id 26451, seq 1, length 64
10:07:04.031272 IP (tos 0x0, ttl 64, id 42617, offset 0, flags [none], proto ICMP (1), length 84)
    10.6.0.2 > 10.6.0.1: ICMP echo reply, id 26451, seq 1, length 64
10:07:05.032546 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto ICMP (1), length 84)
    10.6.0.1 > 10.6.0.2: ICMP echo request, id 26451, seq 2, length 64
10:07:05.032565 IP (tos 0x0, ttl 64, id 42618, offset 0, flags [none], proto ICMP (1), length 84)
    10.6.0.2 > 10.6.0.1: ICMP echo reply, id 26451, seq 2, length 64
10:07:06.033775 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto ICMP (1), length 84)

image.png

tun和tap是Linux等操作系统中提供的一种虚拟网络设备。tun设备可以理解为Point-to-Point的设备;tap设备可以理解为Ethernet设备。
需要注意的是:tun/tap设备不是从物理网卡设备中读取包,而是从用户空间的程序中读取包;向该设备写入时,并不实际从物理网卡设备上发出包,而是由内核提交到应用程序。
讲起来比较难以理解,那么我们以本案例中的ping 10.6.0.2为例,对OpenVPN使用到的关键技术tun设备进行详细说明。
在服务器x.y.z.28上由用户使用BASH输入ping 10.6.0.2后,tun设备和内核、OpenVPN及物理网卡之间的工作流程如图3-3所示。

image.png

详细说明如下:
1)用户使用BASH进程输入ping 10.6.0.2。此时,内核收到的IP包地址信息为:源地址10.6.0.1,目的地址10.6.0.2。
2)内核经过路由判断,把该IP包写入tun0设备(tun0的IP地址是10.6.0.1)。
3)OpenVPN进程读取该IP包。
4)OpenVPN对该包进行封装、加密后,向内核写入,此时IP包地址信息为:源地址x.y.z.28,目的地址a.b.c.239。1)中的包信息,含IP头部,被封装到该IP包内。
5)内核经过路由判断,把该包写入物理网卡(Physical NIC)。
6)物理网卡经过封装成帧(Frame)通过物理链路,经过互联网发送到a.b.c.239上。
服务器a.b.c.239收到经过互联网传输过来的数据时,它的工作流程如图3-4所示。

image.png

详细说明如下:
1)物理网卡收到帧(Frame)。
2)物理网卡将帧提交到内核。
3)OpenVPN读取该IP包后,经过解封装、解密,获得内容是ICMP的ping包,目的地址是tun0。
4)OpenVPN向tun0写入经过步骤3解封的ICMP包。
5)内核模块处理。
内核模块处理完成后,会发回ICMP请求响应。回包的流程与图3-3中所示的流程相同。

3.4 使用OpenVPN创建远程访问的虚拟专用网络

在上个实践中,我们创建了两台具有公网IP的服务器之间的虚拟专用网络,进行安全的数据传输。在本案例中,我们将创建远程访问(Remote Access)模式的虚拟专用网络。
在某些文档中,远程访问被称为Road Warrior(可以翻译为“移动办公”),是指为经常不在办公室的驻场人员或者远程办公的人员提供访问服务器资源或者办公网络资源的通道。在这些场景中,远程访问者一般没有公网IP,他们使用内网地址通过防火墙设备进行网络地址转换后连接互联网。
在本例中,我们使用的物理网络结构图如图3-5所示。

image.png

创建远程访问模式的虚拟专用网络的操作步骤如下。
1)在服务器a.b.c.239上生成CA证书、服务器证书、客户端证书。
在OpenVPN 2.0.9的源码包中有相关的脚本可以辅助我们进行证书的生成和管理。
我们首先从http://build.openvpn.net/downloads/releases/openvpn-2.0.9.tar.gz 下载该代码。使用如下命令:

wget http://build.openvpn.net/downloads/releases/openvpn-2.0.9.tar.gz

解压缩后,进入以下目录:

[root@localhost easy-rsa]# cd openvpn-2.0.9/easy-rsa
[root@localhost easy-rsa]# ls
2.0       build-dh     build-key       build-key-pkcs12  build-req       clean-all  make-crl     README      revoke-full  vars
build-ca  build-inter  build-key-pass  build-key-server  build-req-pass  list-crl   openssl.cnf  revoke-crt  sign-req     Windows

生成如下CA证书:

[root@localhost easy-rsa]# . vars #初始化环境变量
NOTE: when you run ./clean-all, I will be doing a rm -rf on /root/openvpn/openvpn-2.0.9/easy-rsa/keys
[root@localhost easy-rsa]# ./clean-all #删除旧的文件
[root@localhost easy-rsa]# ./build-ca #创建root CA
Generating a 1024 bit 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) [KG]:CN #填写国家代码
State or Province Name (full name) [NA]:SH #填写省份
Locality Name (eg, city) [BISHKEK]:SH #填写城市
Organization Name (eg, company) [OpenVPN-TEST]:XUFENG-INFO #填写组织名
Organizational Unit Name (eg, section) []:DEVOPS #填写部门名称
Common Name (eg, your name or your server's hostname) []:cert.xufeng.info
Email Address [me@myhost.mydomain]:xufengnju@163.com #填写管理员邮箱地址

image.png

生成OpenVPN服务器证书和私钥如下:

[root@localhost easy-rsa]# ./build-key-server vpnserver #extension = server
Generating a 1024 bit RSA private key
...........++++++
..........................................++++++
writing new private key to 'vpnserver.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) [KG]:CN
State or Province Name (full name) [NA]:SH
Locality Name (eg, city) [BISHKEK]:SH
Organization Name (eg, company) [OpenVPN-TEST]:XUFENG-INFO
Organizational Unit Name (eg, section) []:VPN
Common Name (eg, your name or your server's hostname) []:vpnserver.xufeng.info
Email Address [me@myhost.mydomain]:xufengnju@163.com
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
Using configuration from /root/openvpn/openvpn-2.0.9/easy-rsa/openssl.cnf
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
countryName           :PRINTABLE:'CN'
stateOrProvinceName   :PRINTABLE:'SH'
localityName          :PRINTABLE:'SH'
organizationName      :PRINTABLE:'XUFENG-INFO'
organizationalUnitName:PRINTABLE:'VPN'
commonName            :PRINTABLE:'vpnserver.xufeng.info'
emailAddress          :IA5STRING:'xufengnju@163.com'
Certificate is to be certified until Dec  8 06:56:36 2025 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

image.png

生成客户端需要的证书和私钥如下:

[root@localhost easy-rsa]# ./build-key vpnclient2
Generating a 1024 bit RSA private key
........................++++++
......++++++
writing new private key to 'vpnclient1.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) [KG]:CN
State or Province Name (full name) [NA]:SH
Locality Name (eg, city) [BISHKEK]:SH
Organization Name (eg, company) [OpenVPN-TEST]:XUFENG-INFO
Organizational Unit Name (eg, section) []:VPN
Common Name (eg, your name or your server's hostname) []:vpnclient2.xufeng.info
Email Address [me@myhost.mydomain]:xufengnju@163.com
 
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
Using configuration from /root/openvpn/openvpn-2.0.9/easy-rsa/openssl.cnf
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
countryName           :PRINTABLE:'CN'
stateOrProvinceName   :PRINTABLE:'SH'
localityName          :PRINTABLE:'SH'
organizationName      :PRINTABLE:'XUFENG-INFO'
organizationalUnitName:PRINTABLE:'VPN'
commonName            :PRINTABLE:'vpnclient2.xufeng.info'
emailAddress          :IA5STRING:'xufengnju@163.com'
Certificate is to be certified until Dec  8 06:57:53 2025 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

image.png

2)在服务器a.b.c.239配置OpenVPN,配置文件是server.conf。配置文件的内容如下:

port 1194 #使用1194端口进行监听
proto udp #使用UDP协议
dev tun #使用IP路由模式
ca      /etc/openvpn/ca.crt #指定CA证书位置
cert    /etc/openvpn/vpnserver.crt #指定服务器端证书位置
key     /etc/openvpn/vpnserver.key #指定服务器端私钥位置
dh      /etc/openvpn/dh1024.pem #使用Diffie-Hellman算法进行加密密钥计算
server 172.16.100.0 255.255.255.0 #客户端连接上虚拟专用网络后从此网段分配隧道IP
client-config-dir /etc/openvpn/ccd #使用此目录对各个虚拟专用网络客户端进行细粒度控制
route 192.168.20.0 255.255.255.0 #配置服务器增加一条到客户端网络的路由
client-to-client #允许不同的客户端进行互相访问,使用OpenVPN内部路由
keepalive 10 120 #每10s发送保活,120s内未收到保活信息时向OpenVPN进程发送SIGUSR1信号
#在TLS控制通道的通信协议上增加一层HMAC(Hash-based Message Authentication Code)防止dos攻击
tls-auth    /etc/openvpn/ta.key 0 
comp-lzo #启用压缩
max-clients 100 #最大用户数
user nobody #执行OpenVPN进程的用户
group nobody #执行OpenVPN进程的组
persist-key #收到信号SIGUSR1时不重新读取key文件
persist-tun #收到信号SIGUSR1时不关闭tun虚拟网口和重新打开
#创建并修改权限,使nobody可以读写 /var/log/openvpn
status /var/log/openvpn/status.log  #指定状态日志位置
log-append  /var/log/openvpn/openvpn.log #指定运行日志位置
verb 4 #设置日志级别为一般级别,会记录正常连接信息和报错

我们来看看/etc/openvpn/ccd下文件vpnclient2.xufeng.info中的内容:

ifconfig-push 172.16.100.9 172.16.100.10 #指定客户端的IP为172.16.100.9
iroute 10.192.168.20.0 255.255.255.0 #加一条内部路由
push "route 10.168.103.0 255.255.255.0" #把该路由推送到客户端执行

image.png

启动OpenVPN服务器进程。使用如下的命令:

openvpn --daemon --config /etc/openvpn/server.conf

3)在192.168.20.96上安装OpenVPN GUI,并部署配置文件。
https://openvpn.net/index.php/download/community-downloads.html 页面进行下载。
在32位Windows 7 系统上,我们通过以下链接进行下载并安装:
https://swupdate.openvpn.org/community/releases/openvpn-install-2.3.9-I601-i686.exe
在安装过程中,可能会出现确认界面,如图3-6所示。

image.png

请勾选“始终信任来自‘OpenVPN Technologies,Inc.’的软件(A)”。
安装完成后,在目录C:Program FilesOpenVPNconfig下面部署如下文件,如图3-7所示。

image.png

vpnclient.ovpn内容如下:

client #指定角色为客户端
dev tun #和服务器端一致
proto udp #和服务器端一致
remote a.b.c.239 1194 #指定服务器端IP和端口
resolv-retry infinite #连接失败时重复尝试
nobind #不指定本地端口
persist-key #收到信号SIGUSR1时不重新读取key文件
persist-tun #收到信号SIGUSR1时不关闭tun虚拟网口和重新打开
ca ca.crt #指定CA证书位置
cert    vpnclient2.crt #指定客户端证书位置
key     vpnclient2.key #指定客户端私钥位置
ns-cert-type server #要求服务器端的证书的扩展属性为server
#在TLS控制通道的通信协议上增加一层HMAC(Hash-based Message Authentication Code)防止dos攻击
tls-auth ta.key 1 
comp-lzo #启用压缩
verb 4 #设置日志级别为一般级别,会记录正常连接信息和报错
keepalive 10 120 #每10s发送保活,120s内未收到保活信息时向OpenVPN进程发送SIGUSR1信号
log-append openvpn.log #指定log位置

经过以上3个步骤后,客户端192.168.20.96可以使用虚拟隧道和虚拟专用网络服务器进行通信。但此时无法与10.168.103.171通信。为了实现客户端192.168.20.96可以与10.168.103.171通信,必须在a.b.c.239这个虚拟专用网络服务器上执行以下的操作:

#启用ip_forward
sed -e 's/net.ipv4.ip_forward = 0/net.ipv4.ip_forward = 1/g' /etc/sysctl.conf
sysctl -p
#增加iptables对tun0的转发支持
iptables -A FORWARD -i tun0 -j ACCEPT
#加入网络地址转换的转发
iptables -t nat -A POSTROUTING -o eth1 -j MASQUERADE #eth1为服务器内网端口
iptables -t nat -A POSTROUTING -o tun0
-j MASQUERADE #tun0为虚拟隧道端口

同时在10.168.103.171服务器上执行以下的操作:

route add -net 192.168.20.0/24 gw 10.168.103.239

4)在192.168.20.96上,连接OpenVPN服务器并进行网络测试。
连接后,我们在192.168.20.96上可以看到它获得的隧道IP地址,如图3-8所示。

image.png

由此可见,它获得的隧道IP地址和服务器端配置文件/etc/openvpn/ccd/vpnclient2.xufeng.info中使用ifconfig-push指令配置的完全一致。
它获得的路由如图3-9所示。

image.png

在远程访问模式下,从虚拟专用网络客户端192.168.20.96使用ICMP ping服务器Host:a.b.c.239所在局域网中的一台服务器10.168.103.171的虚拟网络数据流图如图3-10所示。

image.png

可以看到,OpenVPN起到虚拟路由器的作用,使用net30的模式,建立起远程访问者和虚拟专用网络服务器之间的虚拟专用网络。方框中的IP包标示出了在虚拟专用网络客户端发出的包到达虚拟专用网络服务器时经过网络地址转换的情况。此时,在服务器10.168.103.171上看到的ICMP的来源IP地址是虚拟专用网络服务器(Host:a.b.c.239)的内网IP地址10.168.103.239。
在服务器10.168.103.171使用tcpdump抓取ICMP网络通信的结果如下:

# tcpdump -vvv -nnn -i em1 -c 3 icmp
tcpdump: listening on em1, link-type EN10MB (Ethernet), capture size 65535 bytes
10:38:35.015495 IP (tos 0x0, ttl 127, id 654, offset 0, flags [none], proto ICMP (1), length 60)
10.168.103.239 > 10.168.103.171: ICMP echo request, id 1, seq 9923, length 40 #源地址已经被转换成VPN服务器的内网地址
10:38:35.016139 IP (tos 0x0, ttl 64, id 64964, offset 0, flags [none], proto ICMP (1), length 60)
10.168.103.171 > 10.168.103.239: ICMP echo reply, id 1, seq 9923, length 40
10:38:36.017624 IP (tos 0x0, ttl 127, id 655, offset 0, flags [none], proto ICMP (1), length 60)
10.168.103.239 > 10.168.103.171: ICMP echo request, id 1, seq 9924, length 40 #源地址已经被转换成虚拟专用网络服务器的内网地址
3 packets captured
4 packets received by filter
0 packets dropped by kernel

3.5 使用OpenVPN创建站点到站点虚拟专用网络

站点到站点(Site-to-Site)虚拟专用网络,用于连接两个或者多个地域上不同的局域网LAN,每个LAN有一台OpenVPN服务器作为接入点,组成虚拟专用网络,使得不同LAN里面的主机和服务器能够互相通信。
一个典型的站点到站点的虚拟专用网络物理架构如图3-11所示。

image.png

在部署这种站点到站点模式虚拟专用网络时,需要注意以下几点:

  • 在所有虚拟专用网络的接入点,把系统路由转发打开。
  • 在所有虚拟专用网络的接入点,在tun0端口和内网端口全部配置成网络地址转换模式,这样可以极大地简化虚拟专用网络路由设置。
  • 在所有虚拟专用网络的接入点,把iptables转发设置为允许。
  • 每个LAN的主机,通过设置静态路由或者默认路由,把到对端LAN的访问下一跳指向到本LAN的接入点服务器的内网IP。

本架构中的虚拟专用网络客户端x.y.z.28配置文件如下,供大家参考。

[root@localhost openvpn]# cat vpnclient.conf 
client
dev tun
proto udp
remote a.b.c.239 1194
resolv-retry infinite
nobind
persist-key
persist-tun
ca /etc/openvpn/ca.crt
cert    /etc/openvpn/vpnclient1.crt
key     /etc/openvpn/vpnclient1.key
ns-cert-type server
tls-auth /etc/openvpn/ta.key 1
comp-lzo
verb 4
route-delay 2
keepalive 10 120
log-append  /var/log/openvpn/openvpn.log

3.6 回收OpenVPN客户端的证书

如果我们分发给客户端的证书不慎被窃取了,或者相关员工离职了,那么我们必须确认它不能继续通过OpenVPN接入我们的虚拟专用网络。在此情况下,我们以收回vpnclient2的证书为例,需要使用如下的命令:

. ./vars 
./revoke-full vpnclient2

这样会在keys目录下产生一个文件crl.pem。我们把它复制到/etc/openvpn目录下。然后在server.conf中加入下面一行:

crl-verify crl.pem

这样,每次建立虚拟专用网络连接前,OpenVPN服务器会查看crl.pem,来确定客户端的证书是否在收回的列表里面。如果匹配到,则禁止客户端进行连接。

3.7 使用OpenVPN提供的各种script功能

在以上的各个实践中,我们分别使用了静态密码或者证书的方式来提供客户端的认证。那么,是不是还有其他方法呢?
答案是肯定的。
可以体现OpenVPN灵活性特点的一个重要方面是它提供了从客户端认证前、认证中、认证后、隧道建立后等各个阶段的script处理功能。我们可以用这些script来实现各种控制功能。
OpenVPN按照执行的顺序,提供了以下的一系列脚本功能:

  • --up,在TCP/UDP在socket上执行了bind、TUN/TAP打开后执行。
  • --tls-verify,远程开始进行tls认证时执行。
  • --ipchange,在客户端,OpenVPN连接认证后执行。
  • --client-connect,在服务器端,客户端认证后立即执行。
  • --route-up,连接认证后执行。
  • --route-pre-down,路由删除前执行。
  • --client-disconnect,在服务器上,客户端断开连接时执行。
  • --down,TCP/UDP和TUN/TAP关闭后执行。
  • --learn-address,在服务器端,任何路由或者IP地址对应的MAC地址学习时执行。
  • --auth-user-pass-verify,在服务器端,新的客户端连接开始建立的时候执行。

回归到前面提到的对客户端进行其他方式认证的问题,那么我们可以使用--auth-user-pass-verify这个指令实现。
在server.conf中增加如下配置项:

auth-user-pass-verify /etc/openvpn/myauth.pl via-file

myauth.pl脚本输出0(成功)或者1(失败)以通知OpenVPN是否认证通过。
通过如下的脚本,我们使用Windows Active Directory来进行用户的控制,只有合法的Active Directory账户才可以连接到我们的虚拟专用网络。脚本内容如下:

#!/usr/bin/perl
use strict;
use warnings;
use utf8;
use Net::LDAP;
my $tmpfile = $ARGV[0];#OpenVPN进程会把客户端提交过来的用户名和密码记录在临时文件中
my $line    = 1;
my $username;
my $password;
my $not_verified = 1;
 
open( TMP, '<', $tmpfile ) or exit(1); #打开临时文件
while (<TMP>) {
    chomp;
    if ( $line eq 1 ) {
        $username = $_; #获取用户名
    }
    else {
        $password = $_; #获取密码
    }
    $line++;
}
close(TMP);
if ( !( $username && $password ) ) {
    exit(1);
}
 
# verify via active directory
my $ldap = Net::LDAP->new('shrd.woyo.com', timeout =>3) or exit(1);
my $mesg =
  $ldap->bind( $username . "\@" . 'shrd.woyo.com', password => $password );
$mesg->code && exit(1); #使用用户名和密码到AD中进行认证
my $searchbase = 'dc=shrd,dc=woyo,dc=com';
#虚拟专用网络用户必须属于vpn组
my $filter     = "memberOf=CN=vpn,OU=Accounts,DC=shrd,DC=woyo,DC=com"; 
my $results    = $ldap->search( base => $searchbase, filter => $filter );
foreach my $entry ( $results->entries ) {
    if($entry->get_value('mailNickname') && ($entry->get_value('mailNickname') eq $username )) {
        $not_verified = 0;
        last;
    }
}
$ldap->unbind;
exit($not_verified);

3.8 OpenVPN的排错步骤

在实践中,运维工程师们经常需要搭建一套OpenVPN的系统或者运维一套已经在线上生产环境中使用的OpenVPN系统。在配置或者维护OpenVPN虚拟专用网络的过程中,根据不同的需求,我们可能会遇到各种各样不同的问题。
在此,我们总结了对于OpenVPN系统最佳的排错步骤。在遇到问题时,可以按照下面的步骤进行排查。
1)认真查看与分析服务器端和客户端的OpenVPN日志。
在服务器上,我们使用如下指令配置OpenVPN的日志:

log-append  /var/log/openvpn/openvpn.log
verb 4

那么在出现异常时,我们首先需要分析这个文件。
该文件分以下几个部分:

  • OpenVPN实际运行时读取的配置文件位置和配置项。

以如下的格式开始。

Fri Dec 18 13:25:44 2015 us=656293 Current Parameter Settings:
Fri Dec 18 13:25:44 2015 us=656383   config = '/etc/openvpn/server.conf'
  • OpenVPN的版本和OpenSSL版本。
Fri Dec 18 13:25:44 2015 us=660554 OpenVPN 2.3.8 x86_64-redhat-linux-gnu [SSL (OpenSSL)] [LZO] [EPOLL] [PKCS11] [MH] [IPv6] built on Aug  4 2015
Fri Dec 18 13:25:44 2015 us=660566 library versions: OpenSSL 1.0.1e-fips 11 Feb 2013, LZO 2.03
Fri Dec 18 13:25:44 2015 us=663615 Diffie-Hellman initialized with 1024 bit key
  • OpenVPN本地添加的路由信息。
Fri Dec 18 13:25:44 2015 us=665243 /sbin/ip link set dev tun0 up mtu 1500
Fri Dec 18 13:25:44 2015 us=668536 /sbin/ip addr add dev tun0 local 172.16.100.1 peer 172.16.100.2
Fri Dec 18 13:25:44 2015 us=670061 /sbin/ip route add 10.128.119.0/24 via 172.16.100.2
Fri Dec 18 13:25:44 2015 us=671212 /sbin/ip route add 192.168.20.0/24 via 172.16.100.2
Fri Dec 18 13:25:44 2015 us=672122 /sbin/ip route add 172.16.100.0/24 via 172.16.100.2

image.png

  • 客户端连接时的信息。
Fri Dec 18 13:25:54 2015 us=348333 x.y.z.28:58937 Re-using SSL/TLS context
#压缩启用成功
Fri Dec 18 13:25:54 2015 us=348369 x.y.z.28:58937 LZO compression initialized
Fri Dec 18 13:25:54 2015 us=348505 x.y.z.28:58937 Control Channel MTU parms [ L:1542 D:166 EF:66 EB:0 ET:0 EL:3 ]
Fri Dec 18 13:25:54 2015 us=348537 x.y.z.28:58937 Data Channel MTU parms [ L:1542 D:1450 EF:42 EB:143 ET:0 EL:3 AF:3/1 ]
#和客户端建立连接时,本地的配置项
Fri Dec 18 13:25:54 2015 us=348679 x.y.z.28:58937 Local Options String: 'V4,dev-type tun,link-mtu 1542,tun-mtu 1500,proto UDPv4,comp-lzo,keydir 0,cipher BF-CBC,auth SHA1,keysize 128,tls-auth,key-method 2,tls-server'
#和客户端建立连接时,对客户端配置项的要求
Fri Dec 18 13:25:54 2015 us=348706 x.y.z.28:58937 Expected Remote Options String: 'V4,dev-type tun,link-mtu 1542,tun-mtu 1500,proto UDPv4,comp-lzo,keydir 1,cipher BF-CBC,auth SHA1,keysize 128,tls-auth,key-method 2,tls-client'
Fri Dec 18 13:25:54 2015 us=348743 x.y.z.28:58937 Local Options hash (VER=V4): '14168603'
Fri Dec 18 13:25:54 2015 us=348766 x.y.z.28:58937 Expected Remote Options hash (VER=V4): '504e774e'
Fri Dec 18 13:25:54 2015 us=348824 x.y.z.28:58937 TLS: Initial packet from [AF_INET]x.y.z.28:58937, sid=5e66e4eb b8382cc8
#CA证书信息
Fri Dec 18 13:25:54 2015 us=652935 x.y.z.28:58937 VERIFY OK: depth=1, C=CN, ST=SH, L=SH, O=XUFENG-INFO, OU=DEVOPS, CN=cert.xufeng.info, emailAddress=xufengnju@163.com
#客户端证书,注意VERIFY的后面必须是OK
Fri Dec 18 13:25:54 2015 us=653140 x.y.z.28:58937 VERIFY OK: depth=0, C=CN, ST=SH, O=XUFENG-INFO, OU=VPN, CN=vpnclient1.xufeng.info, emailAddress=xufengnju@163.com
Fri Dec 18 13:25:54 2015 us=704318 x.y.z.28:58937 Data Channel Encrypt: Cipher 'BF-CBC' initialized with 128 bit key #加密算法
Fri Dec 18 13:25:54 2015 us=704352 x.y.z.28:58937 Data Channel Encrypt: Using 160 bit message hash 'SHA1' for HMAC authentication #HMAC算法
Fri Dec 18 13:25:54 2015 us=704436 x.y.z.28:58937 Data Channel Decrypt: Cipher 'BF-CBC' initialized with 128 bit key
Fri Dec 18 13:25:54 2015 us=704453 x.y.z.28:58937 Data Channel Decrypt: Using 160 bit message hash 'SHA1' for HMAC authentication
Fri Dec 18 13:25:54 2015 us=729243 x.y.z.28:58937 Control Channel: TLSv1.2, cipher TLSv1/SSLv3 DHE-RSA-AES256-GCM-SHA384, 1024 bit RSA
Fri Dec 18 13:25:54 2015 us=729287 x.y.z.28:58937 [vpnclient1.xufeng.info] Peer Connection Initiated with [AF_INET]x.y.z.28:58937
Fri Dec 18 13:25:54 2015 us=729344 vpnclient1.xufeng.info/x.y.z.28:58937 OPTIONS IMPORT: reading client specific options from: /etc/openvpn/ccd/vpnclient1.xufeng.info #确认服务器上读到了客户端的专用配置文件
Fri Dec 18 13:25:54 2015 us=729586 vpnclient1.xufeng.info/x.y.z.28:58937 MULTI: Learn: 172.16.100.5 -> vpnclient1.xufeng.info/x.y.z.28:58937
Fri Dec 18 13:25:54 2015 us=729610 vpnclient1.xufeng.info/x.y.z.28:58937 MULTI: primary virtual IP for vpnclient1.xufeng.info/x.y.z.28:58937: 172.16.100.5
Fri Dec 18 13:25:54 2015 us=729628 vpnclient1.xufeng.info/x.y.z.28:58937 MULTI: internal route 10.128.119.0/24 -> vpnclient1.xufeng.info/x.y.z.28:58937
Fri Dec 18 13:25:54 2015 us=729648 vpnclient1.xufeng.info/x.y.z.28:58937 MULTI: Learn: 10.128.119.0/24 -> vpnclient1.xufeng.info/x.y.z.28:58937
Fri Dec 18 13:25:56 2015 us=789781 vpnclient1.xufeng.info/x.y.z.28:58937 PUSH: Received control message: 'PUSH_REQUEST'
Fri Dec 18 13:25:56 2015 us=789819 vpnclient1.xufeng.info/x.y.z.28:58937 send_push_reply(): safe_cap=940
Fri Dec 18 13:25:56 2015 us=789862 vpnclient1.xufeng.info/x.y.z.28:58937 SENT CONTROL [vpnclient1.xufeng.info]: 'PUSH_REPLY,route 172.16.100.0 255.255.255.0,topology net30,ping 10,ping-restart 120,route 10.168.103.0 255.255.255.0,route 192.168.20.0 255.255.255.0,ifconfig 172.16.100.5 172.16.100.6' (status=1) #向客户端发送的PUSH内容

2)对比分析服务器端和客户端的配置文件,确保相关配置项一致。
这里提供一个简单有效的方法。首先把服务器配置文件和客户端配置文件都下载下来,对二者内容使用Linux中的diff或者Windows中的Beyond Compare进行对比。使用diff命令时操作如下:

sort server.conf > server.conf.1
sort vpnclient.conf > vpnclient.conf.1
diff server.conf.1 vpnclient.conf.1

image.png

3)检查服务器是否打开转发并被防火墙允许。
使用如下的命令,确认值是1。

# sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 1 

使用如下的命令,确认chain FORWARD为ACCEPT,或者显式地指定了tun0的FORWARD为ACCEPT。

iptables -L -n

4)检查服务器上网络地址转换的设置。
如下是一个正确使用iptables-save之后的网络地址转换配置内容:

*nat
:PREROUTING ACCEPT [176:15277]
:POSTROUTING ACCEPT [44:2480]
:OUTPUT ACCEPT [36:2160]
-A POSTROUTING -o eth1 -j MASQUERADE #虚拟专用网络服务器内网口启用网络地址转换
-A POSTROUTING -o tun0 -j MASQUERADE #虚拟专用网络服务器隧道口启用网络地址转换
COMMIT

5)检查主机的路由表。在所有参与网络通信的服务器上,按照网络数据流的路径,依次使用route或者traceroute命令检查下一跳是否正确。如指向不正确,则修正。
6)使用tcpdump进行分析。
如以上步骤依然无法排除问题,可以使用tcpdump进行抓包分析。

3.9 本章小结

虚拟专用网络通过使用软件来互联不同地域分布的分支机构、人员,为业务提供安全的加密通道,有效地扩展了局域网的范围。同时,借助开源方案,能够显著降低总拥有成本(Total Cost of Ownership,TCO)。
本章首先介绍了常见的虚拟专用网络构建技术和原理,并简要对比分析了它们的特点。然后我们实践了OpenVPN构建3种不同网络结构的虚拟专用网络,指出其中核心配置内容和证书管理等。通过对OpenVPN排错步骤的梳理,我们希望读者能够建立一套高效的问题排查思路,在遇到任何OpenVPN相关的故障时,都能从容不迫地去分析、处理、总结。
OpenVPN作为一款具有超过10年历史的开源SSL虚拟专用网络实现方案,具有良好的稳定性和性能,同时在国内外也有良好的技术生态圈,应用非常广泛,值得每个运维工程师去研究、学习、使用。

推荐阅读材料

https://openvpn.net/community-resources/how-to ,OpenVPN手册。
《Troubleshooting OpenVPN》,Eric F Crist著。该书专注于OpenVPN的调试和排错。

本章重点内容助记图

本章涉及的内容较多,因此,笔者特编制了图3-12以帮助读者理解和记忆重点内容。

image.png

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

分享:

华章出版社

官方博客
官网链接