支持Android、iOS 9内置IPSec客户端的strongSwan 5.3.5配置

服务器

Arch Linux可以安装aur/strongswan,Debian可以安装unstable仓库的strongswanlibcharon-extra-plugins。Ubuntu等发行版,软件仓库中strongswan较旧,建议编译安装最新版本。

在服务器上执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# CA key & certificate
ipsec pki --gen > caKey.der
ipsec pki --self --in caKey.der --dn 'C=JP, O=mynet, CN=CA' --ca > caCert.der
openssl x509 -inform der -in caCert.der -out caCert.pem

# server key & certificate
ipsec pki --gen > serverKey.der
ipsec pki --pub --in serverKey.der | ipsec pki --issue --cacert caCert.der --cakey caKey.der --dn 'C=JP, O=mynet, CN=your.ip' --san 'your.domain' > serverCert.der
openssl x509 -inform der -in serverCert.der -out serverCert.pem
openssl rsa -inform der -in serverKey.der -out serverKey.pem

# 本文未用到(iOS 9公钥认证未试验成功,Linux strongSwan客户端公钥认证产生1580字节packet超过MTU,分片未试验成功)。若需生成client key & certificate
#ipsec pki --gen > clientKey.der
#ipsec pki --pub --in clientKey.der | ipsec pki --issue --cacert caCert.der --cakey caKey.der --dn "C=JP, O=azurejp, CN=client" --san 'client@your.domain' > clientCert.der
#openssl x509 -inform der -in clientCert.der -out clientCert.pem
#openssl rsa -inform der -in clientKey.der -out clientKey.pem

mkdir -p /etc/ipsec.d/{certs,cacerts,private}
cp caCert.pem /etc/ipsec.d/cacerts/
cp serverCert.pem /etc/ipsec.d/certs/
cp serverKey.pem /etc/ipsec.d/private/

编辑/etc/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
28
29
30
31
32
config setup
uniqueids = never

conn ikev2ios
keyexchange = ikev2
ike = aes256-sha256-modp1024,3des-sha1-modp1024,aes256-sha1-modp1024! # for iOS 9
esp = aes256-sha256,3des-sha1,aes256-sha1! # for iOS 9

left = %any
leftid = your.domain # iOS 9似乎要求此处为域名或IP
leftsendcert = always # iOS 9会验证证书
leftcert = serverCert.pem
leftsubnet = 0.0.0.0/0 # 给iOS 9推送的路由

right = %any
rightauth = eap-mschapv2
rightsourceip = 10.99.1.0/24
eap_identity = %any
auto = add
fragmentation = yes

conn ikev1android
keyexchange = ikev1
left = %any
leftid = your.ip
leftsendcert = always
leftcert = serverCert.pem
leftsubnet = 0.0.0.0/0
right = %any
rightauth = xauth
rightsourceip = 10.99.1.0/24
auto = add

注意修改your.ipyour.domain

iOS 9内置的IKEv2 VPN客户端会验证服务端证书是否为本地某信任CA签署的,且“远程ID”匹配证书的CN(生成serverCert.der时的--dn选项中)或subject alternative name (生成serverCert.der时的--san选项)。CN、subject alternative name可以填域名或IP地址,域名可以乱写,不会检查域名是否和服务器IP对应。填写服务器的域名或IP地址可行,其他可能项如邮箱地址等不知是否可行。

编辑/etc/ipsec.secrets

1
2
3
: RSA serverKey.pem
client : EAP "your.password" # iOS及Linux的EAP-MSCHAPv2
client : XAUTH "your.password" # Android IPSec hybrid RSA

注意修改用于EAP-MSCHAPv2的密码your.password

服务器需要允许外界访问500/udp和4500/udp。客户端连接后,可以把服务端作为路由的下一跳,服务端需要配置转发:

1
2
sysctl -w net.ipv4.ip_forward=1
iptables -t nat -A POSTROUTING -s 10.99.1.0/24 -j MASQUERADE

某些使用环境可能会设定默认DROP、禁止FORWARD等,需要更复杂的配置,此处不赘述。

若要推送nameserver给客户端,可以编辑/etc/strongswan.conf

1
2
3
4
5
6
7
8
9
10
11
12
charon {
load_modular = yes
duplicheck.enable = no
compress = yes

dns1 = your.dns1
dns2 = your.dns2

plugins {
include strongswan.d/charon/*.conf
}
}

ipsec start启动服务端守护进程,如果使用systemd的话,可能是systecmtl start strongswan之类。

Linux strongSwan客户端

caCert.pem复制到本地,cp caCert.pem /etc/ipsec.d/cacerts/

编辑/etc/ipsec.conf,设定名为myikev2的连接:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
config setup
niqueids = never

conn myikev2
keyexchange = ikev2
right = your.ip
rightca = caCert.pem
rightid = your.domain
rightsubnet = 0.0.0.0/0

left = %any
leftsourceip = %config
leftauth = eap-mschapv2
eap_identity = client
auto = start

注意修改your.domain

编辑/etc/ipsec.secrets,需要服务端中指定的EAP密码:

1
client : EAP "your.password"

rightsubnet会影响到myikev2连接建立后的新建的路由。ip ru可以看到多了一个ID为220的路由表,若把rightsubnet改为8.0.0.0/8,会看到ip r s t 220输出:

1
2
# 我本地无线网接口wlp3s0 IP 192.168.0.3,网关为192.168.0.1。wlp3s0上多出了一个10.99.1.1/32的由服务端推送的[virtual IP
8.0.0.0/8 via 192.168.0.1 dev wlp3s0 table 220 proto static src 10.99.1.1

若要测试是否可以把服务端作为网关,可以用如下命令:

1
2
3
4
5
# 添加
ip r a 180.149.132.47 dev enp4s0f2 src 10.99.1.1
ping 180.149.132.47
# 删除
ip r d 180.149.132.47 dev enp4s0f2 src 10.99.1.1

Virtual IP。待补充。

iOS 9内置IKEv2客户端

在iOS 9中打开caCert.pem,会提示导入“描述文件”,之后可以在“设置->通用->描述文件”看到该证书。

iOS 9中亦可导入用于客户端认证的私钥及证书,但必须是PKCS12格式,且有四位passphrase,可以用如下命令创建:

1
openssl pkcs12 -export -inkey clientKey.pem -in clientCert.pem -name 'client' -certfile caCert.pem -caname 'CA' -passout 'pass:1234' -out client.p12

尚未弄明如何使用iOS 9内置客户端的公钥认证,但可以使用EAP-MSCHAPv2方式认证。“设置->通用->VPN->添加VPN配置”,填写如下字段:

  • 类型:IKEv2
  • 服务器:your.ip
  • 远程ID:服务端/etc/ipsec.conf中指定的leftid
  • 用户鉴定:用户名
  • 用户名:client (服务端/etc/ipsec.secrets中配置了名为client的EAP identity)
  • 密码:your.password

iOS调试还是挺麻烦的,VPN连接失败什么错误消息都没有,只能看服务端的日志……对iOS认识太少。

Android

  • 类型:IPSec Hybrid RSA
  • 服务器地址:your.ip
  • IPSec CA证书:caCert.pem
  • IPSec服务器证书:serverCert.pem。“设置->安全->从存储设备安装”中可以安装。

测试时,“IPSec CA证书”、“IPSec服务器证书”可留空。长按新建的VPN条目可以修改配置。

较新的strongSwan似乎关闭Aggressive Mode PSK,默认无法使用IPSec Xauth PSK:

1
Aggressive Mode PSK disabled for security reasons.

调试

journal -fb _SYSTEM_UNIT=strongswan.service看服务端日志,或者用ipsec start --nofork在前台开启。客户端修改后ipsec restart看日志,某些改动可以使用ipsec reloadipsec rereadsecrets等。

常见错误

服务端找不到匹配的conn配置:

1
2
looking for peer configs matching 10.0.0.4[server]...1.2.3.4[client]   # 未仔细研究,似乎对应配置文件中的:left[leftid]...right[rightid]
no matching peer config found

服务端strongswan不支持特定认证方式

journalctl -fb后重启ipsec服务,看到如下字样:

1
2
14[IKE] EAP-Identity request configured, but not supported
14[IKE] loading EAP_MSCHAPV2 method failed

服务端未推送nameserver给客户端

Android、iOS难以看到IPSec客户端错误信息,可以尝试用IP访问网页。

Path MTU discovery

假设服务器上外网接口为eth0,根据该接口的MTU与网站协商了一个MSS,但该MSS加上IP header很可能会大于IPSec客户端到服务器的MTU。服务器向网站发送ICMP Unreachable,但网站很可能屏蔽了该消息。

在服务器上使用命令tcpdump -ni eth0 'port 80 and tcp[13] & 2 == 2'观察80/tcp带有SYN的TCP packets发现协商的MSS。tcpdump -ni eth0 'net 180.97.33.0/24'发现网站发送的TCP packet与服务器发送的不被理会的ICMP Unreachable。

1
2
3
4
5
6
7
8
9
10
11
12
12:31:51.778576 IP (tos 0x0, ttl 44, id 42813, offset 0, flags [DF], proto TCP (6), length 1480)
180.97.33.107.80 > 10.0.0.4.49016: Flags [.], cksum 0xba48 (correct), seq 1026:2466, ack 78, win 193, length 1440
12:31:51.778633 IP (tos 0xc0, ttl 64, id 59986, offset 0, flags [none], proto ICMP (1), length 576)
10.0.0.4 > 180.97.33.107: ICMP 10.0.0.4 unreachable - need to frag (mtu 1438), length 556
IP (tos 0x0, ttl 43, id 42813, offset 0, flags [DF], proto TCP (6), length 1480)
180.97.33.107.80 > 10.0.0.4.49016: Flags [.], seq 1026:2466, ack 78, win 193, length 1440
12:31:52.194666 IP (tos 0x0, ttl 44, id 42814, offset 0, flags [DF], proto TCP (6), length 1480)
180.97.33.107.80 > 10.0.0.4.49016: Flags [.], cksum 0xba48 (correct), seq 1026:2466, ack 78, win 193, length 1440
12:31:52.194740 IP (tos 0xc0, ttl 64, id 59995, offset 0, flags [none], proto ICMP (1), length 576)
10.0.0.4 > 180.97.33.107: ICMP 10.0.0.4 unreachable - need to frag (mtu 1438), length 556
IP (tos 0x0, ttl 43, id 42814, offset 0, flags [DF], proto TCP (6), length 1480)
180.97.33.107.80 > 10.0.0.4.49016: Flags [.], seq 1026:2466, ack 78, win 193, length 1440

iOS客户端初始协商的MSS为1360,一般不会出问题。Linux strongswan客户端/Android客户端初始协商的MSS可能和MTU有关,对于MTU 1500,协商了MSS 1460,无法访问网页。解决办法是调低服务器向IPSec客户端通报的MSS:

1
iptables -t mangle -A FORWARD -s 10.99.1.0/24 -p tcp -m tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1361:1536 -j TCPMSS --set-mss 1360

参考