关于Strongswan配置证书登录的IKEv2 VPN

本文不涉及完整的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 即可。

参考资料

发表评论

您的电子邮箱地址不会被公开。