基于 strongSwan 配置 IPsec IKEv2 VPN
涛叔IPsec 是标准的 VPN 技术,主流系统都支持,不需要单独安装客户端。但是 IPsec 概念非常多,配置起来很繁杂。经过一番折腾,终于摸索出了一套基于 strongSwan 的简便配置方法,支持 iOS 和 macOS 拨入。
服务器是 ubuntu 22.04,首先安装 strongSwan 相关组件:
sudo aptitude install strongswan strongswan-swanctl
启动 strongSwan 服务的命令是:
sudo systemctl start strongswan-starter.service
服务名是 strongswan-starter
,跟老版本的系统不一样。
然后需要添加配置。跟网上其他资料不同,这次使用最新的 swanctl 配置 strongSwan。它的配置文件在 /etc/swanctl/
。
首先创建 /etc/swanctl/conf.d/foo.conf
,内容结构如下:
connections {
foo {
local_addrs = X.X.X.X
local {}
remote {}
children {}
version = 2
}
}
secrets {}
pools {}
connections 下面可以添加多条配置,每个配置指定一个名字。对于每一个 connection,我们需要指定以下配置:
- local_addrs 是服务器的公网 IP 地址
- local 是服务器的认证信息
- remote 是客户端的认证信息
- children 配置客户端跟服务端的网络信息
- version 指定 IKE 版本号
网上的资料多使用证书来认证双端身份,这种方式最安全,但在配置和使用上也最繁琐。我们可以使用 PSK 模式来简化配置。
所谓 PSK 就是 Pre-Share Key,双端预先共享一个密钥。跟证书方式相比,PSK 更简单,但安全性要差一些。所以不要使用简单的 PSK 密钥。建议使用 openssl 自动生成随机密钥:
$ openssl rand -hex 32
9817df17e110d120b7470026cfdb700b91b375cc33cbee66260125f23f3cbe8b
除了需要生成共享密钥,PSK 还要为每个密钥指定一个 ID。所以 local 和 remote 的配置为:
local {
auth = psk
id = bob
}
remote {
auth = psk
}
这里服务器的 PSK 标识指定为 bob,大家可以随便选。客户端也指定使用 PSK 认证,但是用户信息需要在 secrets 中添加:
secrets {
ike-tom {
id = tom
secret = "..."
}
}
配置中的secret
也有多种格式。带双引号的表示不需要额外处理,“foo”的意思就是foo
。不带双引号的分两种:以0x
开头的表示后面为十六进制字符串,以0l
开头的表示后面为 base64
字符串。但我测试下来发现 iOS/macOS 不支持后两种。
以上就完成了认证部分的配置,下面配置网络部分。
首先,我们要为客户端自动分配IP地址。为此要先在 pools 中添加客户端网段:
pools {
rw_pool {
addrs = 10.9.8.0/24
}
}
rw_pool
是配置名,可以随便取。有了网段之后,需要在connection
配置中指定要使用哪个网段:
connections {
foo {
pools = rw_pool
# ...
}
}
最后就是允许客户端流量传入,这需要配置children
参数。
children {
bar {
local_ts = 0.0.0.0/0
remote_ts = 10.9.8.0/24
}
}
我们可以把children
简单理解为路由表或者防火墙规则。从客户端角度看,local_ts
表示目标网段,remote_ts
表示来源网段。bar
这段配置的意思是允许10.9.8.0/24
网段的设备访问任意网络地址。也就是说 VPN 服务器为客户端中转所有网络流量。我们也可以根据实际情况设置local_ts
来限制客户端访问的资源。
到这里所有的配置就结束了😄是不是很简单。完整配置如下:
connections {
foo {
version = 2
local_addrs = X.X.X.X
local {
auth = psk
id = bob
}
remote {
auth = psk
}
children {
bar {
local_ts = 0.0.0.0/0
remote_ts = 10.9.8.0/24
}
}
pools = rw_pool
}
}
secrets {
ike-tom {
id = tom
secret = "..."
}
}
pools {
rw_pool {
addrs = 10.9.8.0/24
}
}
我们需要使用 swanctl 加载配置:
swanctl --load-all
在 iOS 上添加 VPN 配置,类型选 IKEv2。服务器填服务端IP地址。远程ID填在local
中指定的 ID,本地ID填在secrets
指定的 ID。用户认证选无,不使用证书,然后填入 secrets
中的secret
。
这样就可以远程接入 VPN 了。拨入成功后会发现无法正常访问网络。经过调试,发现有两个问题。
第一,服务器没有推送 DNS 配置。
iOS 不支持指定 DNS,macOS 虽然可以指定,但不起作用。最简单的办法就是让 strongSwan 自动下发。这需要修改 /etc/strongswan.conf
charon {
dns1 = 8.8.8.8
# ...
}
修改配置后需要重启 strongswan-starter 然后调用 swanctl 加载配置。
第二,服务器需要开启 NAT 功能。
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
这里的 eth0 是服务器能访问外网的网卡名,请按需调整。
解决这两个问题后客户端就可以通过服务端转发访问外网了🎉
为了方便后续维护,我们可以把上面的两条命令写成一个脚本,比如/etc/ipsec-load.sh
#!/usr/bin/sh
/usr/sbin/iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
/usr/sbin/swanctl --load-all
再给脚本开启可执行权限
chmod a+x /etc/ipsec-load.sh
最后添加 systemd 描述文件(/etc/systemd/system/ipsec-load.service
)
[Unit]
Description=load ipsec rules
After=network.target
Requires=strongswan-starter.service
[Service]
Type=oneshot
ExecStart=/etc/ipsec-load.sh
RemainAfterExit=true
[Install]
WantedBy=multi-user.target
关键点之一是 Type 需要设置成 oneshot,表示一次性任务。关键点之二是 RemainAfterExit 需要认为 true,告诉 systemd 不要重复执行 ExecStart 指定的脚本。
另外值得一提的是 Requires=strongswan-starter.service,这个配置是让 systemd 在 strongswan-starter 启动之后再运行 ipsec-load.sh 脚本,不然 swanctl 会报错。
以上就是本文的全部内容。IPsec VPN 的好处是标准化,主流系统都支持。但缺点也很明显,除了配置复杂之外,因为应用太广泛,也很容易被网络审查系统干扰,但在国内使用则完全没有问题。