strongSwan 配置实践

strongSwan,是一个完整的开源 IPsec VPN 解决方案,可以运行在 Linux、Windows 和 Mac OS X 上,此外,它还兼容基于 Android 和 iOS 的产品所支持的 IPsec 功能。

要注意的是,strongSwan 并不对 VPN 上的数据进行加密,而是由运行 strongSwan 的底层操作系统(比如 Linux)进行加密。strongSwan 负责使用网络密钥交换(internet key exchange, IKE)协议帮助同级协商用于对称加密的密钥。strongSwan 支持 IKEv1 和 IKEv2。IKE 允许对等体就加密所使用的加密参数达成一致,然后通过 Diffie-Hellman 密钥交换算法共同商定加密密钥。

IKEv1 于 1998 年发布,因为协议规格含糊不清,前后不一致,可能导致实施时容易受到各种安全攻击而最终受到批评。七年后,IKEv2 的规范发布,其中提出了一个有些简化和更可靠的协议版本。IKEv2 对 NAT 遍历、可扩展认证协议(extensible authentication protocol, EAP)、移动性和多宿主(mobility and multi-homing, MOBIKE)等功能也有更好的支持。

strongSwan 在一个叫 pluto 的服务中实现了 IKEv1,而 IKEv2 则由一个叫 charon 的服务提供。

Linux IPsec 支持

在 Linux 中有多个竞争性的 IPsec 协议栈,我们应该看看历史来了解它们之间的关系。1998年开始的 KAME 项目是第一个努力实现 IPv4 和 IPv6 的免费 IPsec 协议栈的项目。该项目的实施是为了 BSD UNIX 系统,但很快就作为 Linux 内核的补丁。大约在同一时间,John Gilmore(电子前沿基金会的创始人之一,因破解 DES 加密而闻名)启动了 FreeS/WAN 项目,旨在将 IPsec 引入 Linux。这也需要给 Linux 内核打补丁。FreeS/WAN 补丁被称为 Kernel IP Security(KLIPS)。

在这两种情况下(KAME 和 KLIPS),都需要打补丁来允许在用户空间运行的 IKE 服务与内核空间的对应服务进行通信,以安装加密密钥和配置各种加密参数。

IETF 在 RFC 2367 中定义了 PF_KEYv2,作为标准 API,允许用户空间进程在操作系统内核中配置 IPsec。KAME 和 KLIPS 都使用了 PF_KEYv2。为每个内核版本维护 KAME 和 KLIPS 的补丁是很有挑战性的,因此决定在 Linux 2.6 内核系列中把 KAME 作为默认的 IPsec 协议栈。2.6 内核下的 KAME 实现被称为 NETKEY。然而,2.6 内核的特点是修改了协议栈,其中一个新特性是一个叫做 NETLINK 的协议。与 PF_KEYv2 不同,NETLINK 是一个通用的协议,它允许用户空间实体与内核的各个部分交换消息。例如,RT_NETLINK 是一个 NETLINK 扩展,它可以让动态路由进程在内核中安装或删除路由。同样,XFRM_NETLINK 9 允许 IKE 或其他用户进程管理内核中维护的 IPsec 安全关联数据库(Security Association Database, SAD)和安全策略数据库(Security Policy Database, SPD)。

策略路由

strongSwan 使用了一种叫做策略路由的网络功能。这与 SPD 和 IPsec 使用的安全策略不同。正常的 IP 路由是基于 IP 头中的目标 IP 地址字段。启用 IP 转发的主机将为所有前往特定目的地的数据包选择相同的路径。这种情况并不总是可取的。策略路由的一个早期案例是支持不同客户类别的不同 QoS 级别。金牌客户为获得高吞吐量和低延迟服务而付费,而青铜客户则为更便宜的尽力服务而解决。为这些客户选择路由时,除其他外,还需要将源 IP 地址和目的地址作为 IP 路由过程的一部分。

Linux 策略路由的实现需要多个路由表和一个路由策略数据库(RPDB)。你可以通过输入以下命令列出 RPDB 中的策略:

1
ip rule show

输出应该显示这样的内容:

1
2
3
4
0:     from all lookup local
220: from all lookup 220
32766: from all lookup main
32767: from all lookup default

最左边的一列显示了分配给同一行的策略路由规则的优先级。可用的规则从最高优先级(0)到最低优先级(32767)进行扫描,第一个匹配的规则会选择路由过程将使用的路由表来查找路由。一旦 RPDB 策略匹配,就会使用指定的表来查找下一跳。如果路由过程无法从规则所指示的路由表计算出数据包的下一跳,则继续处理到 RPDB 中的下一条规则。from 关键字表示对源 IP 地址的选择符,但由于也使用了 all ,所以表示所有数据包都会匹配。默认情况下,strongSwan 会插入一个优先级为 220 的策略规则。当 Ubuntu Linux 启动时,它会设置三个表:本地(id 255)、主(254)和默认(253)。本地路由表包含本地和广播地址的高优先级控制路由。这个表是供内核内部使用的,不应该从用户空间修改。主路由表是在没有指定表时使用的普通表。最后,默认表通常是空的,当没有其他表匹配时使用。

要在表中列出可用的路由(例如,local),使用以下命令之一:

1
2
ip route list table local
ip route list table 255

id 为 220 的表是由 strongSwan 创建的。它没有像上面三张表那样友好的名字。id 号的选择与 RPDB 中相应规则的优先级相匹配,便于识别策略规则与表之间的联系。所有由 strongSwan 创建的路由都会被插入到表 220 中。

安装

1
apt-get install strongswan

strongSwan 的主要配置文件是 /etc/ipsec.conf/etc/ipsec.secrets

新版配置范例:https://wiki.strongswan.org/projects/strongswan/wiki/IKEv2Examples

老版配置范例:https://wiki.strongswan.org/projects/strongswan/wiki/IKEv2StrokeExamples

实验环境

服务器 A 和客户机 A 各有一个只连接到 192.168.60.0/24 网络的主机接口。服务器 A 上的接口被分配的 IP 地址为 192.168.60.100 ,客户端 A 上的接口被分配的 IP 地址为 192.168.60.111 。为了使这个例子类似于现实中的例子,我们说服务器 A 和客户端 A 是站点 A 的一部分,站点 A “拥有” 网络192.168.60.0/24 。服务器 A 通过其 NAT 接口为网站 A 提供互联网访问。

相似的,服务器 B 上的网络接口被分配了 IP 地址 192.168.80.100 ,客户端 B 上的接口被分配了 IP 地址 192.168.80.111 。服务器 B 通过其 NAT 接口为 B 站点提供 Internet 访问。

服务器 A 和服务器 B 都有一个只连接到网络的二级主机接口 192.168.70.0/24 。服务器 A 的接口被分配的 IP 地址是 192.168.70.5 ,而服务器 B 的 IP 地址是 192.168.70.6 。您将配置服务器 A 和服务器 B 在 192.168.70.0/24 网络上建立 VPN 服务。

192.168.60.0/24192.168.80.0/24 是私有网络,它们实际上将通过 “公共 “网络 192.168.70.0/24 连接。

带 PSK 认证的 Host-to-Host Transport Mode 配置

Server A 的 ipsec.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# server A
# ipsec.conf - strongSwan IPsec configuration file

# basic configuration

config setup
# strictcrlpolicy=yes
# uniqueids = no

conn %default
ikelifetime=60m
keylife=20m
rekeymargin=3m
keyingtries=1
keyexchange=ikev2
mobike=no

conn serverA-to-serverB
type=transport
authby=secret
left=192.168.70.5
leftsubnet=192.168.70.0/24
leftfirewall=yes
right=192.168.70.6
rightsubnet=192.168.70.0/24
auto=route

Server A 的 ipsec.secret

1
2
3
4
5
6
7
# server A
# This file holds shared secrets or RSA private keys for authentication.

# RSA private key for this host, authenticating it to any other host
# which knows the public part.

192.168.70.5 192.168.70.6 : PSK "helloworld"

Server B 的 ipsec.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# server B
# ipsec.conf - strongSwan IPsec configuration file

# basic configuration

config setup
# strictcrlpolicy=yes
# uniqueids = no

conn %default
ikelifetime=60m
keylife=20m
rekeymargin=3m
keyingtries=1
keyexchange=ikev2
mobike=no

conn serverB-to-serverA
type=transport
authby=secret
left=192.168.70.6
leftsubnet=192.168.70.0/24
leftfirewall=yes
right=192.168.70.5
rightsubnet=192.168.70.0/24
auto=route

Server B 的 ipsec.secret

1
2
3
4
5
6
7
# server B
# This file holds shared secrets or RSA private keys for authentication.

# RSA private key for this host, authenticating it to any other host
# which knows the public part.

192.168.70.5 192.168.70.6 : PSK "helloworld"

您也可以在服务器 A 或服务器 B 上输入以下命令,要求 strongSwan 报告建立的连接:

1
sudo ipsec statusall

带证书认证的 Host-to-Host Transport Mode 配置

此处省略创建证书的过程。

教程可以参考:https://wiki.strongswan.org/projects/strongswan/wiki/SimpleCA

注意:strongSwan 不知道如何处理单个 PEM 文件中包含的证书链。相反,你需要将根 CA 和中间 CA 的单个证书复制到 strongSwan 的 CA 证书目录中。

Server A 的 ipsec.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# server A
# ipsec.conf - strongSwan IPsec configuration file

# basic configuration

config setup
# strictcrlpolicy=yes
# uniqueids = no

conn %default
ikelifetime=60m
keylife=20m
rekeymargin=3m
keyingtries=1
keyexchange=ikev2
mobike=no

conn serverA-to-serverB
type=transport
left=192.168.70.5
leftcert=192.168.70.5.cert.pem
leftid=%fromcert
leftfirewall=yes
right=192.168.70.6
rightid="C=CN, ST=Beijing, L=Chaoyang, O=the masses, CN=192.168.70.6"
auto=route

Server A 的 ipsec.secret

1
2
3
4
5
6
7
# server A
# This file holds shared secrets or RSA private keys for authentication.

# RSA private key for this host, authenticating it to any other host
# which knows the public part.

: RSA 192.168.70.5.key.pem

Server B 的 ipsec.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# server B
# ipsec.conf - strongSwan IPsec configuration file

# basic configuration

config setup
# strictcrlpolicy=yes
# uniqueids = no

conn %default
ikelifetime=60m
keylife=20m
rekeymargin=3m
keyingtries=1
keyexchange=ikev2
mobike=no

conn serverB-to-serverA
type=transport
left=192.168.70.6
leftcert=192.168.70.6.cert.pem
leftid=%fromcert
leftfirewall=yes
right=192.168.70.5
rightid="C=CN, ST=Beijing, L=Chaoyang, O=the masses, CN=192.168.70.5"
auto=route

Server B 的 ipsec.secret

1
2
3
4
5
6
7
# server B
# This file holds shared secrets or RSA private keys for authentication.

# RSA private key for this host, authenticating it to any other host
# which knows the public part.

: RSA 192.168.70.6.key.pem

配置完后需要重启强制 strongSwan 读取 CA 证书。

要列出已存在的 CA 证书,请输入命令:

1
sudo ipsec listcacerts

带证书认证的 Site-to-Site Tunnel Mode 配置

Server A 的 ipsec.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# server A
# ipsec.conf - strongSwan IPsec configuration file

# basic configuration

config setup
# strictcrlpolicy=yes
# uniqueids = no

conn %default
ikelifetime=60m
keylife=20m
rekeymargin=3m
keyingtries=1
keyexchange=ikev2

conn serverA-to-serverB
left=192.168.70.5
leftcert=192.168.70.5.cert.pem
leftid=%fromcert
leftfirewall=yes
right=192.168.70.6
rightid="C=CN, ST=Beijing, L=Chaoyang, O=the masses, CN=192.168.70.6"
auto=route

Server A 的 ipsec.secret

1
2
3
4
5
6
7
# server A
# This file holds shared secrets or RSA private keys for authentication.

# RSA private key for this host, authenticating it to any other host
# which knows the public part.

: RSA 192.168.70.5.key.pem

Server B 的 ipsec.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# server B
# ipsec.conf - strongSwan IPsec configuration file

# basic configuration

config setup
# strictcrlpolicy=yes
# uniqueids = no

conn %default
ikelifetime=60m
keylife=20m
rekeymargin=3m
keyingtries=1
keyexchange=ikev2

conn serverB-to-serverA
left=192.168.70.6
leftcert=192.168.70.6.cert.pem
leftid=%fromcert
leftfirewall=yes
right=192.168.70.5
rightid="C=CN, ST=Beijing, L=Chaoyang, O=the masses, CN=192.168.70.5"
auto=route

Server B 的 ipsec.secret

1
2
3
4
5
6
7
# server B
# This file holds shared secrets or RSA private keys for authentication.

# RSA private key for this host, authenticating it to any other host
# which knows the public part.

: RSA 192.168.70.6.key.pem

带 IP 转发的 Site-to-Site Tunnel Mode 配置

在 Client A 上编辑 /etc/network/interfaces 并添加 gateway 192.168.60.100 。编辑 /etc/resolv.conf 并验证 nameserver 10.0.98.3 是 DNS 服务器。

在 Server A 上输入:

1
2
3
sudo sysctl -w net.ipv4.ip_forward=1
sudo sysctl -p
sudo iptables -t nat -A POSTROUTING -j SNAT -o <interface> --to 10.0.98.100

在 Client B 上编辑 /etc/network/interfaces 并添加 gateway 192.168.80.100 。编辑 /etc/resolv.conf 并验证 nameserver 10.0.99.3 是 DNS 服务器。

在 Server B 上输入:

1
2
3
sudo sysctl -w net.ipv4.ip_forward=1
sudo sysctl -p
sudo iptables -t nat -A POSTROUTING -j SNAT -o <interface> --to 10.0.99.100

Server A 的 ipsec.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# server A
# ipsec.conf - strongSwan IPsec configuration file

# basic configuration

config setup
# strictcrlpolicy=yes
# uniqueids = no

conn %default
ikelifetime=60m
keylife=20m
rekeymargin=3m
keyingtries=1
keyexchange=ikev2
mobike=no

conn serverA-to-serverB
left=192.168.70.5
leftcert=192.168.70.5.cert.pem
leftsubnet=192.168.60.0/24
leftid=%fromcert
leftfirewall=yes
right=192.168.70.6
rightsubnet=192.168.80.0/24
rightid="C=CN, ST=Beijing, L=Chaoyang, O=the masses, CN=192.168.70.6"
auto=route

Server A 的 ipsec.secret

1
2
3
4
5
6
7
# server A
# This file holds shared secrets or RSA private keys for authentication.

# RSA private key for this host, authenticating it to any other host
# which knows the public part.

: RSA 192.168.70.5.key.pem

Server B 的 ipsec.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# server B
# ipsec.conf - strongSwan IPsec configuration file

# basic configuration

config setup
# strictcrlpolicy=yes
# uniqueids = no

conn %default
ikelifetime=60m
keylife=20m
rekeymargin=3m
keyingtries=1
keyexchange=ikev2
mobike=no

conn serverB-to-serverA
left=192.168.70.6
leftcert=192.168.70.6.cert.pem
leftsubnet=192.168.80.0/24
leftid=%fromcert
leftfirewall=yes
right=192.168.70.5
rightsubnet=192.168.60.0/24
rightid="C=CN, ST=Beijing, L=Chaoyang, O=the masses, CN=192.168.70.5"
auto=route

iptables 中关于 IPSec 的设置

在默认为 DROP 的规则中,添加:

1
2
3
4
5
6
# IKE negotiations
iptables -A INPUT -p udp --sport 500 --dport 500 -j ACCEPT
iptables -A OUTPUT -p udp --sport 500 --dport 500 -j ACCEPT
# ESP encrypton and authentication
iptables -A INPUT -p 50 -j ACCEPT
iptables -A OUTPUT -p 50 -j ACCEPT

参考