使用 IPsec 和 SSL/TLS 创建 VPN
VPN(虚拟专用网络)是一种通过不安全和不受信任的网络(如互联网)提供安全通信的技术。通常,它通过身份验证、加密、压缩和隧道来实现这一点。隧道是一种将一种协议的数据包头和数据封装在另一种协议的有效负载字段中的技术。这样,封装的数据包就可以穿过原本无法穿过的网络。
目前,创建 VPN 的两种最常见技术是 IPsec 和 SSL/TLS。在本文中,我描述了这两种技术的特性和特征,并提供了两个简短的示例,说明如何在 Linux 中创建 IPsec 和 SSL/TLS 隧道,并验证隧道是否正确启动。我还对这两种技术进行了简要比较。
IPsec(IP 安全)在网络层提供加密、身份验证和压缩。IPsec 实际上是一套协议,由 IETF(互联网工程任务组)开发,已经存在很长时间了。第一个 IPsec 协议于 1995 年定义(RFC 1825–1829)。后来,在 1998 年,这些 RFC 被 RFC 2401–2412 取代。2.6 Linux 内核中的 IPsec 实现由 Dave Miller 和 Alexey Kuznetsov 编写。它同时处理 IPv4 和 IPv6。IPsec 在 OSI 七层网络模型中的第 3 层,即网络层运行。IPsec 在 IPv6 中是强制性的,在 IPv4 中是可选的。为了实现 IPsec,添加了两个新协议:身份验证头 (AH) 和封装安全有效负载 (ESP)。握手和交换会话密钥使用互联网密钥交换 (IKE) 协议完成。
AH 协议(RFC 2404)的协议号为 51,它验证数据包头和有效负载。AH 协议不使用加密,因此几乎从未使用过。
ESP 的协议号为 50。它使我们能够向数据包添加安全策略并对其进行加密,尽管加密不是强制性的。加密由内核使用内核 CryptoAPI 完成。当两台机器使用 ESP 协议连接时,一个唯一的数字标识此连接;此数字称为 SPI(安全参数索引)。在这些机器之间流动的每个数据包都有一个序列号 (SN),从 0 开始。对于每个发送的数据包,SN 递增 1。每个数据包还有一个校验和,称为数据包的 ICV(完整性检查值)。此校验和使用只有这两台机器知道的密钥计算。
IPsec 有两种模式:传输模式和隧道模式。创建 VPN 时,我们使用隧道模式。这意味着每个 IP 数据包都完全封装在新建的 IPsec 数据包中。这个新建的 IPsec 数据包的有效负载是原始 IP 数据包。

图 2. IPsec 隧道 ESP 数据包
图 2 显示,由于使用隧道,在右侧添加了一个新的 IP 报头,并且还添加了一个 ESP 报头。
当隧道的端点(有时称为对等方)位于 NAT(网络地址转换)设备之后时,会出现一个问题。使用 NAT 是一种连接多台具有“内部地址”的机器的方法,这些机器无法直接从外部世界访问。这些机器通过一台具有互联网地址的机器访问外部世界;NAT 在这台机器上执行——通常是网关。
当隧道的端点位于 NAT 之后时,NAT 会修改 IP 数据包的内容。结果,此数据包将被对等方拒绝,因为签名错误。因此,IETF 发布了一些 RFC,试图找到解决此问题的方法。此解决方案通常称为 NAT-T 或 NAT 穿越。NAT-T 的工作原理是将 IPsec 数据包封装在 UDP 数据包中,以便这些数据包能够穿过 NAT 路由器而不会被丢弃。RFC 3948,《IPsec ESP 数据包的 UDP 封装》,讨论了 NAT-T(请参阅资源)。
Openswan 是一个开源项目,为 Linux IPsec 提供用户工具的实现。您可以使用 Openswan 工具创建 VPN(如下面的简短示例所示)。Openswan 项目于 2003 年由前 FreeS/WAN 开发人员启动。FreeS/WAN 是 Openswan 的前身。S/WAN 代表安全广域网,实际上是 RSA 的商标。Openswan 在许多不同的平台上运行,包括 x86、x86_64、ia64、MIPS 和 ARM。它支持内核 2.0、2.2、2.4 和 2.6。
目前有两种可用的 IPsec 内核堆栈:KLIPS 和 NETKEY。Linux 内核 NETKEY 代码是对 KAME IPsec 代码的从头重写。KAME 项目是日本六家公司的共同努力,旨在为 BSD UNIX 计算机操作系统变体提供免费的 IPv6 和 IPsec(适用于 IPv4 和 IPv6)协议栈实现。
KLIPS 不是 Linux 内核的一部分。使用 KLIPS 时,您必须将补丁应用于内核以支持 NAT-T。使用 NETKEY 时,NAT-T 支持已在内核内部,无需修补内核。
当您应用防火墙 (iptables) 规则时,KLIPS 是更简单的情况,因为使用 KLIPS,您可以识别 IPsec 流量,因为此流量通过 ipsecX 接口。您可以将 iptables 规则应用于这些接口,就像将规则应用于其他网络接口(例如 eth0)一样。
当使用 NETKEY 时,应用防火墙 (iptables) 规则要复杂得多,因为流量不流经 ipsecX 接口;一种解决方案是在 Linux 内核中使用 iptables 标记数据包(使用 setmark iptables 规则)。此标记是内核套接字缓冲区结构(来自 Linux 内核网络代码的 struct sk_buff)的成员;数据包的解密不会修改该标记。
Openswan 支持机会加密 (OE),它允许通过从 DNS 服务器广告和获取公钥来创建基于 IPsec 的 VPN。
OpenVPN 是由 James Yonan 创立的开源项目。它提供基于 SSL/TLS 的 VPN 解决方案。传输层安全 (TLS) 及其前身安全套接字层 (SSL) 是加密协议,可在互联网上提供安全通信数据传输。SSL 自 90 年代初以来一直存在。
OpenVPN 网络模型基于 TUN/TAP 虚拟设备;TUN/TAP 是 Linux 内核的一部分。Linux 中的第一个 TUN 驱动程序由 Maxim Krasnyansky 开发。
与 IPsec 相比,OpenVPN 的安装和配置更简单。OpenVPN 支持 RSA 身份验证、Diffie-Hellman 密钥协商、HMAC-SHA1 完整性检查等。在服务器模式下运行时,它支持多个客户端(最多 128 个)通过同一端口连接到 VPN 服务器。您可以设置自己的证书颁发机构 (CA),并为 OpenVPN 服务器和多个客户端生成证书和密钥。
OpenVPN 在用户空间模式下运行;这使得将 OpenVPN 移植到其他操作系统变得容易。
首先,下载并安装 ipsec-tools 包和 Openswan 包(大多数发行版都有这些包)。
VPN 隧道在其两端有两个参与者,称为 left 和 right,哪个参与者被认为是 left 或 right 是任意的。您必须在 /etc/ipsec.conf 中配置这两个端点的各种参数(请参阅 man 5 ipsec.conf)。/etc/ipsec.conf 文件分为多个部分。conn 部分包含连接规范,定义要使用 IPsec 建立的网络连接。
/etc/ipsec.conf 中 conn 部分的示例,它定义了同一 LAN 上两个节点之间的隧道,左侧为 192.168.0.89,右侧为 192.168.0.92,如下所示
... conn linux-to-linux # # Simply use raw RSA keys # After starting openswan, run: # ipsec showhostkey --left (or --right) # and fill in the connection similarly # to the example below. left=192.168.0.89 leftrsasigkey=0sAQPP... # The remote user. # right=192.168.0.92 rightrsasigkey=0sAQON... type=tunnel auto=start ...
您可以通过运行以下命令在两个参与者上生成 leftrsasigkey 和 rightrsasigkey
ipsec rsasigkey --verbose 2048 > rsa.key
然后,将 rsa.key 的内容复制并粘贴到 /etc/ipsec.secrets 中。
在某些情况下,IPsec 客户端是漫游客户端(具有随机 IP 地址)。这种情况通常发生在客户端是从远程位置使用的笔记本电脑时(此类客户端称为 Roadwarriors)。在这种情况下,在 ipsec.conf 中使用以下内容
right=%any
%any 关键字用于指定未知 IP 地址。
此示例中连接的 type 参数是 tunnel(这是默认值)。其他类型可以是 transport,表示主机到主机的传输模式;passthrough,表示根本不应进行 IPsec 处理;drop,表示应丢弃数据包;以及 reject,表示应丢弃数据包并返回诊断 ICMP。
连接的 auto 参数告诉 IPsec 启动时应自动执行哪些操作。例如,auto=start 告诉它加载并启动连接;而 auto=ignore(这是默认值)表示没有自动启动操作。auto 参数的其他值可以是 add、manual 或 route。
配置 /etc/ipsec.conf 后,使用以下命令启动服务
service ipsec start
您可以执行一系列检查,通过键入以下命令来获取有关机器上 IPsec 的信息ipsec verify。并且,输出ipsec verify可能看起来像这样
Checking your system to see if IPsec has installed and started correctly: Version check and ipsec on-path [OK] Linux Openswan U2.4.7/K2.6.21-rc7 (netkey) Checking for IPsec support in kernel [OK] NETKEY detected, testing for disabled ICMP send_redirects [OK] NETKEY detected, testing for disabled ICMP accept_redirects [OK] Checking for RSA private key (/etc/ipsec.d/hostkey.secrets) [OK] Checking that pluto is running [OK] Checking for 'ip' command [OK] Checking for 'iptables' command [OK] Opportunistic Encryption Support [DISABLED]
您可以通过运行以下命令获取有关您创建的隧道的信息
ipsec auto --status
您还可以在内核 syslog 中查看各种低级 IPSec 消息。
您可以通过在两个参与者之间打开 FTP 连接(例如),并在两个参与者之间运行以下命令来测试和验证在两个参与者之间流动的数据包确实是 esp 帧
tcpdump -f esp tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on eth0, link-type EN10MB (Ethernet), capture size 96 bytes
您应该看到类似这样的内容
IP 192.168.0.92 > 192.168.0.89: ESP(spi=0xd514eed9,seq=0x7) IP 192.168.0.89 > 192.168.0.92: ESP(spi=0x3a1563b9,seq=0x6) IP 192.168.0.89 > 192.168.0.92: ESP(spi=0x3a1563b9,seq=0x7) IP 192.168.0.92 > 192.168.0.89: ESP(spi=0xd514eed9,seq=0x8)
请注意,spi(安全参数索引)标头对于所有数据包都是相同的;这是连接的标识符。
如果您需要支持 NAT 穿越,请添加nat_traversal=yes在 ipsec.conf 中;nat_traversal=no是默认值。
Linux IPsec 堆栈可以与 Openswan 的 pluto、ipsec-tools 中包含的 KAME 项目的 racoon 或 OpenBSD 的 isakmpd 一起工作。
首先,下载并安装 OpenVPN 包(大多数发行版都有此包)。
然后,通过执行以下操作创建共享密钥
openvpn --genkey --secret static.key
您可以在服务器端或客户端创建此密钥,但您应该在安全通道(例如 SSH)中将此密钥复制到另一端。创建隧道时,客户端和服务器之间会交换此密钥。
这种类型的共享密钥是最简单的密钥;您也可以使用基于 CA 的密钥。CA 可以与 OpenVPN 服务器位于不同的机器上。《OpenVPN HOWTO》提供了有关此内容的更多详细信息(请参阅资源)。
然后,创建一个名为 server.conf 的服务器配置文件
dev tun ifconfig 10.0.0.1 10.0.0.2 secret static.key comp-lzo
在客户端,创建以下名为 client.conf 的配置文件
remote serverIpAddressOrHostName dev tun ifconfig 10.0.0.2 10.0.0.1 secret static.key comp-lzo
请注意,client.conf 配置文件中 IP 地址的顺序已更改。
comp-lzo 指令启用 VPN 链接上的压缩。
您可以通过添加 tun-mtu 指令来设置隧道的 mtu。当使用以太网桥接时,您应该使用 dev tap 而不是 dev tun。
隧道的默认端口是 UDP 端口 1194(您可以通过键入以下命令进行验证netstat -nl | grep 1194在启动隧道之后)。
在启动 VPN 之前,请确保 TUN 接口(或 TAP 接口,如果您使用以太网桥接)未被防火墙阻止。
通过运行以下命令在服务器上启动 vpnopenvpn server.conf并运行openvpn client.conf在客户端上。
您将在客户端上获得如下输出
OpenVPN 2.1_rc2 x86_64-redhat-linux-gnu [SSL] [LZO2] [EPOLL] built on Mar 3 2007 IMPORTANT: OpenVPN's default port number is now 1194, based on an official port number assignment by IANA. OpenVPN 2.0-beta16 and earlier used 5000 as the default port. LZO compression initialized TUN/TAP device tun0 opened /sbin/ip link set dev tun0 up mtu 1500 /sbin/ip addr add dev tun0 local 10.0.0.2 peer 10.0.0.1 UDPv4 link local (bound): [undef]:1194 UDPv4 link remote: 192.168.0.89:1194 Peer Connection Initiated with 192.168.0.89:1194 Initialization Sequence Completed
您可以通过从客户端 ping 服务器(从客户端 ping 10.0.0.1)来验证隧道是否已启动。
TUN 接口模拟 PPP(点对点)网络设备,TAP 模拟以太网设备。用户空间程序可以打开 TUN 设备并可以对其进行读取或写入。您可以将 iptables 规则应用于 TUN/TAP 虚拟设备,就像您对以太网设备(例如 eth0)所做的那样。
IPsec 被认为是 VPN 的标准;许多供应商(包括 Cisco、Nortel、CheckPoint 等)制造具有内置 IPsec 功能的设备,这使它们能够连接到其他 IPsec 客户端。
但是,我们应该在这里谨慎一点:不同的制造商可能在其设备上以不兼容的方式实现 IPsec,这可能会造成问题。
目前大多数供应商不支持 OpenVPN。
IPsec 比 OpenVPN 复杂得多,并且涉及内核代码;这使得将 IPsec 移植到其他操作系统的任务更加繁重。将 OpenVPN 移植到其他操作系统比 IPsec 容易得多,因为 OpenVPN 完全在用户空间中运行,并且不涉及内核代码。
IPsec 和 OpenVPN 都使用 HMAC(哈希消息身份验证码)来验证数据包。
OpenVPN 基于使用 OpenSSL 库;它可以运行在 UDP(默认和首选协议)或 TCP 上。与在内核中运行的 IPsec 相比,它在用户空间中运行,因此在性能方面比 IPsec 更重。
在 OpenVPN 中配置和应用防火墙 (iptables) 规则通常比在基于 IPsec 的隧道中使用 Openswan 配置此类规则更容易。
资源
OpenVPN: openvpn.net
OpenVPN 2.0 HOWTO: openvpn.net/howto.html
RFC 3948, UDP 封装 IPsec ESP 数据包: tools.ietf.org/html/rfc3948
Openswan: www.openswan.org
KAME 项目: www.kame.net
Rami Rosen 是位于海法的以色列理工学院计算机科学专业毕业生。他是一家网络初创公司的 Linux 和 Open Solaris 内核程序员,可以通过 ramirose@gmail.com 联系他。在业余时间,他喜欢跑步、解决密码难题,并帮助他认识的每个人转移到这个出色的操作系统 Linux。