前言
IKEv2/IPsec 是一套网络层数据加密方案,它可以配置为点到点、站到站、网状网(Cisco DMVPN)、远程访问。IKEv2/IPsec 远程访问 VPN 十分适合于远程办公的场景。
IKEv2/IPsec 有两个组成部分,IKEv2 负责对通信双方进行鉴权并协商临时密钥,IPsec 负责匹配给定的流量规则并加密数据。
IPsec 有两种模式,AH 和 ESP。AH 只进行真实性校验,不加密;ESP 既做真实性校验又加密。实际应用中,AH 因为不支持 NAT 穿隧而并没有什么用处。如果真的有不加密的需求,可以将 ESP 的加密模式设置成 null。
其中,ESP 也有两种模式,transport 和 tunnel。Transport 模式下,加密载荷里直接就是传输层段或者报文,因而只能加密两个端点之间的连接;tunnel 模式下,加密载荷里有第二层 IP 报头,不仅可以进行路由,还可以给 NAT 后的客户端分配虚拟 IP 地址。
综上所述,我们要配置的 IPsec 模式应为 ESP tunnel。
关于认证阶段
IKEv2 的认证分为两个阶段。
Phase 1:双方鉴权和协商 IKE_SA,使用 UDP 500 端口。对于远程访问 VPN 来说,客户端验证服务端的 PKI 证书,服务端验证客户端的用户名密码。
Phase 2:建立隧道和协商 CHILD_SA,使用 IP 50 协议或 UDP 4500 端口(若启用 NAT 穿隧)。双方根据配置好的规则来决定哪些流量需要加密。对于远程访问 VPN 来说,客户端会请求加密全部流量(0.0.0.0/0 和 ::/0)。
随后,双方配置操作系统的 IPsec Policy 来把流量加密转发到 IP 50 协议或 UDP 4500 端口上。
为了保证同一个临时密钥不使用太长时间,IKEv2 会每隔一段时间 rekey 一次。在本例,我们关闭服务端的 rekey,将 rekey 的任务交给客户端,否则会因为双方同时 rekey 而掉线。
软件环境
服务端:Debian testing (10.0pre),strongSwan 5.7.1,systemd 239,AppArmor 2.12。
客户端:Windows 10 Pro 1803、macOS High Sierra 10.13.6、iOS 11.4.1。
如果你在用旧版的 Debian 或者 Ubuntu,强烈建议你打开 updates 和 backports 源。其中 updates 包含版本内功能更新,backports 包含跨版本功能更新。
步骤一:安装依赖
sudo apt update
sudo apt install certbot charon-systemd iptables-persistent libcharon-extra-plugins libstrongswan-extra-plugins ndppd
本文使用了新版 strongSwan 的部分新功能,所以请务必确认 strongSwan 的版本高于 5.7.1。
如果你曾经安装过 strongSwan,强烈建议你使用以下命令清空 strongSwan 的全部配置:
sudo apt --reinstall -o Dpkg::Options::="--force-confask,confmiss" install charon-systemd iptables-persistent libcharon-extra-plugins libstrongswan-extra-plugins
步骤二:申请 Let's Encrypt 证书
如果你已经有在开 HTTPS 业务,那么直接把 TLS 证书拿来用就好。
没有的话用 certbot 申请一个 Let's Encrypt 的证书:
sudo certbot certonly --standalone -d example.com
sudo ln -s /etc/letsencrypt/live/example.com/privkey.pem /etc/swanctl/private/example.com.key
一切顺利的话证书已经躺在 /etc/letsencrypt/live 里了。证书需要每隔 90 天续期一次,幸运的是 systemd 的 certbot.timer 会自动启动 certbot 来续期。不过续期之后 strongSwan 并不会自动重启,你可以在 /etc/letsencrypt/renewal-hooks 里写个脚本。
步骤三:配置 AppArmor
Debian 会默认给 strongSwan 打开 AppArmor。这样的话 strongSwan 就读取不到我们的证书文件了。在 /etc/apparmor.d/local/usr.sbin.swanctl 里加入:
/etc/letsencrypt/ r,
/etc/letsencrypt/** r,
步骤四:配置日志
在遇到问题的时候有日志看是很重要的,因此我们要先打开必要的日志。在 /etc/strongswan.d/charon-systemd.conf 里写上:
charon-systemd {
# Section to configure native systemd journal logger, very similar to the
# syslog logger as described in LOGGER CONFIGURATION in strongswan.conf(5).
journal {
# Loglevel for a specific subsystem.
# <subsystem> = <default>
# Default loglevel.
# default = 1
cfg = 2
ike = 2
}
}
步骤五:配置 swanctl
strongSwan 有两套配置系统:旧的一套是 ipsec.conf,使用 stroke 插件来启动;新的一套是 swanctl.conf,使用 vici 插件来启动。我们将使用新的 swanctl.conf 系统。虽然网络上关于 swantl.conf 的文档和资料还比较少,但是配置起来更加灵活。
在 /etc/swanctl/swanctl.conf 里加入:
authorities {
lets-encrypt {
file = /etc/letsencrypt/live/example.com/chain.pem
}
}
connections {
ikev2 {
version = 2
local_addrs = %any
remote_addrs = %any
# 这是 Windows 10 和 macOS High Sierra 兼容的最优 Phase 1 加密套件。如果你的客户端支持更好的加密套件,可以在这里写上。
proposals = chacha20poly1305-aes256gcm16-prfsha384-curve25519-ecp384-modp3072-ecp256,aes256-sha384-curve25519-ecp384-modp3072-ecp256,aes256gcm16-prfsha384-modp1024,aes256-sha256-ecp256
dpd_delay = 30s
send_certreq = no
send_cert = always
rekey_time = 0s
pools = pool-ipv4,pool-ipv6
local {
certs = /etc/letsencrypt/live/example.com/cert.pem
id = example.com
}
remote {
eap_id = %any
auth = eap-mschapv2
}
children {
ikev2 {
# 这是 Windows 10 和 macOS High Sierra 兼容的最优 Phase 2 加密套件。如果你的客户端支持更好的加密套件,可以在这里写上。
esp_proposals = chacha20poly1305-aes256gcm16-prfsha384-curve25519-ecp384-modp3072-ecp256-modpnone-esn-noesn,aes256-sha384-curve25519-ecp384-modp3072-ecp256-modpnone-esn-noesn,aes256-sha256-sha1
local_ts = 0.0.0.0/0,::/0
rekey_time = 0s
}
}
}
}
secrets {
eap-1 {
# 两个客户端不可以共用同一个用户名,否则会发生 IP 地址冲突。如果有多个客户端,请分配多个用户名。
secret = 用户1和用户2共用的密码
id-user1 = 用户名1
id-user2 = 用户名2
}
eap-2 {
secret = 用户3的密码
id-user3 = 用户名3
}
private-1 {
file = example.com.key
}
}
pools {
pool-ipv4 {
# 这里也可以写 100.100.100.129-100.100.100.254 的格式,或者换成你想要的 IPv4 地址段。
addrs = 100.100.100.128/25
dns = 8.8.8.8,8.8.4.4
}
pool-ipv6 {
# 改成上游 ISP 分配的 IPv6 子网的一小段。
addrs = 2001:db8::/97
dns = 2001:4860:4860::8888,2001:4860:4860::8844
}
}
步骤六:配置 IPv4 NAT
在 /etc/iptables/rules.v4 加入:
*nat
:PREROUTING ACCEPT [31:5739]
:INPUT ACCEPT [31:5739]
:OUTPUT ACCEPT [28:3794]
:POSTROUTING ACCEPT [11:1486]
-A POSTROUTING -m policy --pol ipsec --dir out -j ACCEPT
-A POSTROUTING -s 100.100.100.128/25 -o eth0 -j MASQUERADE
COMMIT
步骤七:配置 IPv6 NDP Proxy
在 /etc/ndppd.conf 加入:
# 改成你的出站设备名
proxy eth0 {
# 改成上游 ISP 分配的 IPv6 子网的一小段。
rule 2001:db8::/97 {
static
}
}
步骤八:启用 IP 转发
在 /etc/sysctl.d/50-ip-forwarding.conf 里写上:
net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1
注意当开启 IPv6 转发的时候,SLAAC 会被默认关闭。如果你通过 SLAAC 从上游 ISP 收取 IPv6 地址,那么需要将其手动打开:(可选)
net.ipv6.conf.eth0.accept_ra = 2
或者,如果你使用 /etc/network/interfaces、NetworkManager、systemd-networkd 来配置网络,只需要打开 SLAAC 开关即可。
步骤九:启动服务
注意:StrongSwan 5.8.0 变更了服务名称。如果你的 strongSwan 版本高于 5.8.0,请将以下所有的 strongswan-swanctl 替换成 strongswan。
sudo systemctl restart apparmor
# 禁用旧版 ipsec.conf 配置服务
sudo systemctl disable --now strongswan
sudo systemctl restart netfilter-persistent ndppd strongswan-swanctl systemd-sysctl
sudo systemctl enable certbot.timer netfilter-persistent ndppd strongswan-swanctl
步骤十:连接客户端
在开始之前,我强烈建议在一台空闲的电脑上连接 VPN 网关,输入:
sudo journalctl -u strongswan-swanctl -f
来查看实时滚动日志。
Windows
在 Windows 设置里,进入 Network & Internet → VPN → Add a VPN connection。
VPN provider: | Windows (built-in) |
Connection name: | 随意 |
Server name or address: | 服务器的域名 |
VPN type: | IKEv2 |
User name: | 用户名 |
Password: | 密码 |
如果提示 Error 13801: IKE authentication credentials are unacceptable,那么请用 certmgr.msc 导入 Let's Encrypt Authority X3 中间证书。
如果还是不行,就导入这条注册表项:
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\RasMan\Parameters]
"DisableIKENameEkuCheck"=dword:00000001
macOS
在 macOS 系统偏好设置里,进入 Network,点击左边栏底部的「+」。
Interface: | VPN |
VPN Type: | IKEv2 |
Service Name: | 随意 |
Server Address: | 服务器的域名或 IP 地址(如果填域名连接超时就填 IP 地址) |
Remote ID: | 服务器的域名,不能填 IP 地址 |
Local ID: | 留空 |
Authentication Settings: | Username |
Username: | 用户名 |
Password: | 密码 |
iOS
在 iOS 的设置里,进入 VPN → Add VPN Configuration。
Type: | IKEv2 |
Description: | 随意 |
Server: | 服务器的域名或 IP 地址(如果填域名连接超时就填 IP 地址) |
Remote ID: | 服务器的域名,不能填 IP 地址 |
Local ID: | 留空 |
Uesr Authentication: | Username |
Username: | 用户名 |
Password: | 密码 |
Linux 和 Android
Linux 用户只需要将 swanctl.conf 的内容反过来填就成了客户端配置。
如何配置默认网关
Windows 默认不把所有流量都发送到 VPN,macOS 和 iOS 则相反。如果要改变这个设置,可以根据以下步骤来操作。
Windows
IPv4:在 Windows 设置里,进入 Network & Internet → Status → Network and Sharing Center → Change adapter settings → VPN (IKEv2) → 右键属性 → Networking → TCP/IPv4 → Properties → Advanced → IP Settings → Use default gateway on remote network。
IPv6:虽然 TCP/IPv6 里也有这个名字的选项,但是
实际上无效。要想设置 IPv6 的默认网关,请打开 PowerShell,输入:
Add-VpnConnectionRoute "连接名" ::/1
Add-VpnConnectionRoute "连接名" 8000::/1
macOS
VPN 已断开的状态下,在 macOS 系统偏好设置里,进入 Network,选择 VPN (IKEv2),点击 Advanced,在 Options 选项卡内,勾选 Send all traffic over VPN connection。
如何打开 AEAD(仅 Windows 10 支持)
AEAD 可以在同一个算法内实现加密和校验,能够提高连接的安全性。
请打开 PowerShell,输入:
Set-VpnConnectionIPsecConfiguration -ConnectionName "连接名" -AuthenticationTransformConstants GCMAES256 -CipherTransformConstants GCMAES256 -EncryptionMethod GCMAES256 -IntegrityCheckMethod SHA384 -PfsGroup ECP384 -DHGroup ECP384 -Force
备用:旧版 ipsec.conf 配置
这是过时的 ipsec.conf 配置文件,仅供参考,不要使用。
config setup
charondebug = "cfg 2, ike 2"
ca lets-encrypt
cacert = /etc/letsencrypt/live/example.com/chain.pem
auto = add
conn ikev2
left = %any
leftauth = pubkey
leftcert = /etc/letsencrypt/live/example.com/cert.pem
leftid = @example.com
leftsendcert = always
leftsubnet = 0.0.0.0/0,::/0
right = %any
rightauth = eap-mschapv2
rightdns = 8.8.8.8,8.8.4.4,2001:4860:4860::8888,2001:4860:4860::8844
rightsendcert = never
rightsourceip = 100.100.100.128/25,2001:db8::/97
auto = add
dpdaction = clear
eap_identity = %any
esp = chacha20poly1305-aes256gcm16-prfsha384-curve25519-ecp384-modp3072-ecp256-modpnone-esn-noesn,aes256-sha384-curve25519-ecp384-modp3072-ecp256-modpnone-esn-noesn,aes256-sha256-sha1!
ike = chacha20poly1305-aes256gcm16-prfsha384-curve25519-ecp384-modp3072-ecp256,aes256-sha384-curve25519-ecp384-modp3072-ecp256,aes256gcm16-prfsha384-modp1024,aes256-sha256-ecp256!
keyexchange = ikev2
rekey = no
/etc/ipsec.secrets
# ln -s /etc/letsencrypt/live/example.com/privkey.pem /etc/ipsec.d/private/example.com.key
: RSA "example.com.key"
用户名1 : EAP "用户1的密码"
用户名2 : EAP "用户2的密码"
用户名3 : EAP "用户3的密码"