本文不涉及完整的IKEv2配置教程,有需要的可参照Vultr的教程
纯证书登录,即服务器端、客户端认证均使用证书,而非PSK(预共享密钥)。
优点:比弱密码安全;不会被随口泄露;无需分享服务器密钥,服务器端很难被伪造。
缺点:各种配置相当繁琐。
网上大部分配置一般是服务器端PSK+客户端PSK(常见于IKEv1),或服务器端证书+客户端PSK(常见于IKEv2),对于个人或少数朋友之间共享来说完全够用(只要PSK不是弱密码),但是对于较多人或一个组织来说,使用证书认证会更加合适。以下在一般配置的基础上说一下证书认证的配置和注意点。
Strongswan配置
ipsec.conf
的示例配置:
config setup
uniqueids = never
#默认参数设置
conn %default
dpdaction=clear
dpddelay=3600s
dpdtimeout=300s
fragmentation=yes
rekey=no
ike=aes256gcm16-aes256gcm12-aes128gcm16-aes128gcm12-aesxcbc-sha256-sha1-modp4096-modp2048-modp1024,aes256-aes128-sha256-sha1-modp4096-modp2048-modp1024,3des-sha1-modp1024!
esp=aes128gcm12-aes128gcm16-aes256gcm12-aes256gcm16-modp4096-modp2048-modp1024,aes128-aes256-sha1-sha256-modp4096-modp2048-modp1024,aes128-sha1-modp4096,aes128-sha1-modp1024,aes128-sha1,3des-sha1!
left=%defaultroute
leftauth=pubkey
leftcert=vpnHostCert.pem
leftsendcert=always
leftsubnet=0.0.0.0/0
right=%any
rightauth=pubkey
rightsourceip=192.168.11.0/24
rightdns=8.8.8.8, 8.8.4.4
#IKEv1,注意这是个纯PSK配置,仅仅用作备用
conn ikev1-psk-xauth
keyexchange=ikev1
leftauth=psk
leftsendcert=never
rightauth=psk
rightauth2=xauth
auto=add
#IKEv2证书基本配置,注意写上你自己的服务器地址
conn ikev2-pubkey
keyexchange=ikev2
leftid=YOUR.VPN.SERVER.FQDN
rightid=%any
auto=add
#IKEv2 + EAP-TLS
conn ikev2-eap-tls
also="ikev2-pubkey"
rightauth=eap-tls
eap_identity=%identity
说明:
- fragmentation指的是将大块信息分成小块发送,对于证书认证特别重要,尤其遇上某些糟糕运营商的时候。但有的客户端不一定支持,例如Windows 7有可能会出问题,建议务必测试一下。
- ike及esp是指定加密算法,这里几个算法应该足够适用主流客户端了。
- 每个conn段落是一个配置,名称可以自己随便写(%default除外)。also可以在前一配置的基础上进行扩展。
- left和right简单理解的话,left是服务器端,right是客户端。
- leftid和rightid是iOS等少数客户端要求的参数。
- 这里配置了一个IKEv1,是因为安卓原生不支持IKEv2,为了避免“从Play安装梯子先得有个梯子”这种先有鸡还是先有蛋的问题,加上PPTP被封锁得厉害,所以还是留个IKEv1在这里。注意在ipsec.secrets里写入相应的PSK。
生成适用于iOS的mobileconfig文件
下面是自动生成mobileconfig文件的shell脚本,可以保存为 mobiconf.sh
。
#!/bin/sh
if [ $1 ]; then
localid="$1@SOME.DOMAIN"
else
echo "Error: missing parameter"
echo "Usage: mobiconf.sh <username> [passphrase]"
exit 1
fi
passphrase=$2
remoteid=${3:-'YOUR.VPN.SERVER.FQDN'}
remoteid_r=$(echo $remoteid | awk 'BEGIN{FS=".";OFS=".";ORS=""}{for (i=NF;i>0;i--) printf("%s%s",$i,(i>1?OFS:ORS))}')
ca_name=${4:-'MY CA COMMON NAME'}
conf_name=${5:-'MY CONF'}
vpn_name=${6:-'MY VPN'}
uuid_conf=$(uuidgen)
uuid_vpn=$(uuidgen)
uuid_p12cert=$(uuidgen)
uuid_cacert=$(uuidgen)
p12cert_file=$1.p12
cacert_file="cacerts/strongswanCert.pem"
output_file=$1.mobileconfig
cat >${output_file} <<EOF
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<!-- Set the name to whatever you like, it is used in the profile list on the device -->
<key>PayloadDisplayName</key>
<string>${conf_name}</string>
<!-- This is a reverse-DNS style unique identifier used to detect duplicate profiles -->
<key>PayloadIdentifier</key>
<string>${remoteid_r}</string>
<!-- A globally unique identifier, use uuidgen on Linux/Mac OS X to generate it -->
<key>PayloadUUID</key>
<string>${uuid_conf}</string>
<key>PayloadType</key>
<string>Configuration</string>
<key>PayloadVersion</key>
<integer>1</integer>
<key>PayloadContent</key>
<array>
<!-- It is possible to add multiple VPN payloads with different identifiers/UUIDs and names -->
<dict>
<!-- This is an extension of the identifier given above -->
<key>PayloadIdentifier</key>
<string>${remoteid_r}.instance</string>
<!-- A globally unique identifier for this payload -->
<key>PayloadUUID</key>
<string>${uuid_vpn}</string>
<key>PayloadType</key>
<string>com.apple.vpn.managed</string>
<key>PayloadVersion</key>
<integer>1</integer>
<!-- This is the name of the VPN connection as seen in the VPN application later -->
<key>UserDefinedName</key>
<string>${vpn_name}</string>
<key>VPNType</key>
<string>IKEv2</string>
<key>IKEv2</key>
<dict>
<!-- Hostname or IP address of the VPN server -->
<key>RemoteAddress</key>
<string>${remoteid}</string>
<!-- Remote identity, can be a FQDN, a userFQDN, an IP or (theoretically) a certificate's subject DN. Can't be empty.
IMPORTANT: DNs are currently not handled correctly, they are always sent as identities of type FQDN -->
<key>RemoteIdentifier</key>
<string>${remoteid}</string>
<!-- Local IKE identity, same restrictions as above. If it is empty the client's IP address will be used -->
<key>LocalIdentifier</key>
<string>${localid}</string>
<!-- Optional, if it matches the CN of the root CA certificate (not the full subject DN) a certificate request will be sent
NOTE: If this is not configured make sure to configure leftsendcert=always on the server, otherwise it won't send its certificate -->
<key>ServerCertificateIssuerCommonName</key>
<string>${ca_name}</string>
<!-- Optional, the CN or one of the subjectAltNames of the server certificate to verify it, if not set RemoteIdentifier will be used -->
<key>ServerCertificateCommonName</key>
<string>${remoteid}</string>
<!-- The server is authenticated using a certificate -->
<key>AuthenticationMethod</key>
<string>Certificate</string>
<!-- The client uses EAP to authenticate -->
<key>ExtendedAuthEnabled</key>
<integer>1</integer>
<key>PayloadCertificateUUID</key>
<string>${uuid_p12cert}</string>
<!-- The next two dictionaries are optional (as are the keys in them), but it is recommended to specify them as the default is to use 3DES.
IMPORTANT: Because only one proposal is sent (even if nothing is configured here) it must match the server configuration -->
<key>IKESecurityAssociationParameters</key>
<dict>
<key>EncryptionAlgorithm</key>
<string>AES-128</string>
<key>IntegrityAlgorithm</key>
<string>SHA1-96</string>
<key>DiffieHellmanGroup</key>
<integer>14</integer>
</dict>
<key>ChildSecurityAssociationParameters</key>
<dict>
<key>EncryptionAlgorithm</key>
<string>AES-128</string>
<key>IntegrityAlgorithm</key>
<string>SHA1-96</string>
<key>DiffieHellmanGroup</key>
<integer>14</integer>
</dict>
</dict>
</dict>
<dict>
<key>PayloadIdentifier</key>
<string>${remoteid_r}.cert.client</string>
<key>PayloadUUID</key>
<string>${uuid_p12cert}</string>
<key>PayloadType</key>
<string>com.apple.security.pkcs12</string>
<key>PayloadVersion</key>
<integer>1</integer>
<!-- Optional password to decrypt the PKCS#12 container, if not set the user is prompted when installing the profile -->
<key>Password</key>
<string>${passphrase}</string>
<!-- This is the Base64 encoded PKCS#12 container with the certificate and private key for the client.
IMPORTANT: The CA certificate will not be extracted from the container, so either install it separately or include it as payload (as seen above) -->
<key>PayloadContent</key>
<data>
$(cat ${p12cert_file}|base64)
</data>
</dict>
<!-- This payload is optional but it provides an easy way to install the CA certificate together with the configuration -->
<dict>
<key>PayloadIdentifier</key>
<string>${remoteid_r}.cert.ca</string>
<key>PayloadUUID</key>
<string>${uuid_cacert}</string>
<key>PayloadType</key>
<string>com.apple.security.root</string>
<key>PayloadVersion</key>
<integer>1</integer>
<!-- This is the Base64 (PEM) encoded CA certificate -->
<key>PayloadContent</key>
<data>
$(grep -v '^-.*-$' ${cacert_file})
</data>
</dict>
</array>
</dict>
</plist>
EOF
说明:
- iOS 9.3.2图形界面配置证书认证的VPN似乎有问题,所以不得不写个mobileconfig文件来安装。
- 使用该脚本的前提是已经生成了用户p12文件及CA证书,使用前请根据你的vpn配置修改前面部分的几个参数和文件路径。
- 生成的mobileconfig文件里面已经集成了用户的p12文件(base64编码)和CA证书(base64编码),因此只需安装生成的mobileconfig文件即可,无需再另外安装证书。
- 尤其注意,该文件只能通过iOS自带的邮件APP或Safari安装,其他邮件客户端无法安装。
客户端及营运商兼容性
经过多次测试,以下是目前关于各系统对IKEv2支持性的整理:
- Windows 7:可能支持、可能有问题(如果开了fragmentation),取决于具体的客户端系统。
- Windows 10:支持,但需要手动配置网关和跃点(控制面板或Powershell)
- Windows 10 mobile:无法使用,因为没有办法配置网关和跃点,除非使用大公司用的MDM进行部署。
- Android 6.0:无原生支持,但可以安装Strongswan客户端支持。
- iOS 9.3.2:支持,但须通过mobileconfig文件配置
- 上海电信普通宽带:Windows看脸,有低于40%的概率可以连上,高于60%的概率连不上,一旦连上基本不会断线。其他系统基本没问题。
- 上海联通4G:基本都可以连上。
- 上海移动4G:基本连不上,似乎非DNS的UDP包都会被丢弃。
关于安装方法的一些提示
Android
原生不支持IKEv2,请安装Strongswan应用。
导入证书有两种方法。一种是在Strongswan里面导入证书(仅用于Strongswan),另一种是在设置-安全-凭据存储-从设备内存或SD卡安装(系统证书,但会经常出现安全警告)。
Windows 7
思路: CA证书装在本地计算机,用户证书装在当前用户。至于随p12导入的本地计算机的用户证书和当前用户的CA证书,完全可以删除。
安装证书(两步都要):
- 步骤一:在开始菜单中搜索mmc,右键以管理员身份运行。文件-添加或删除控制单元-证书-添加-本地计算机-确定。左侧窗格找到“受信任的根证书颁发机构”,操作-所有任务-导入,将p12文件导入本地计算机。
- 步骤二:直接双击p12文件,将其安装到当前用户。
Windows 10
安装证书(两步都要):
- 步骤一:直接双击p12文件,将其安装到本地计算机。
- 步骤二:直接双击p12文件,将其安装到当前用户。
配置VPN后你会发现网络数据依然是直连的,这是因为Win10对于IKEv2 VPN不添加默认网关,只对VPN的子网生效(Split tunneling)。如果要应用到全局,须从控制面板或Powershell手动配置。
点击系统托盘的网络图标-网络设置-更改适配器选项,右击VPN连接-属性-网络选项卡-双击TCP/IPv4-高级…,勾选“在远程网络使用默认网关”(用于应用全局网关);同时取消勾选“自动跃点”,并在下面文本框里输入15或更小的数字(用于强制通过VPN远程解析DNS)。
(微软:这不是bug,这是feature!别指望我们会修复!)
Windows 10 mobile
除非用大企业的MDM工具进行部署,否则无解。
更新:其实还是能抢救一下的……
(微软:我的系统就是为大企业设计的,小企业和个人用个毛线!)
iOS 9.3.2
通过系统自带的图形界面设置似乎无法成功,只能通过mobileconfig安装。
(苹果:我们的产品连傻瓜用户都会用!我们的Bugs?丢给开发者去折腾好了)
其他提示
OpenVZ上安装需要libipsec,否则会出现如下提示:
charon: 12[IKE] unable to install inbound and outbound IPsec SA (SAD) in kernel
如果是epel源的strongswan,直接 yum install strongswan-libipsec 即可。