IKEv2 VPNサーバーにiPhone (iOS)でオンデマンド接続させる方法

# できるようになる事

iOSの構成プロファイルを作成する事で、wifiと電話回線の切り替えなどが発生しても自動的にVPNサーバに接続できるようになります。

今回の例では、携帯電話回線や自宅以外のwifiでは必ずVPN経由で通信が行われ、自宅のwifiに接続すると自動的にVPNを切断します。

## 背景

データ通信量の削減と見かけ上の通信速度を向上させるために[ziproxy](http://ziproxy.sourceforge.net/)を使用しているのですが、一々VPNの接続をするのが面倒くさいので何とかしたかった事が発端です。

# IKEv2サーバーの構築

[How To Setup IKEV2 Strongswan VPN Server on Ubuntu For iOS / iPhone](https://www.proxyrack.com/blog/how-to-setup-ikev2-strongswan-vpn-server-on-ubuntu-for-ios-iphone/)の手順に従って、ここに書かれている通りにやれば接続できます。

注意する事は一つだけで、何かを省略しようとはせず、書かれている事を忠実に実施する点です。

# iOS構成プロファイル
## iOS構成プロファイルとは

iOSのあれやこれやを設定してくれる設定ファイルみたいなものです。Apple Configurator 2というツールを使用すれば生成できますが、このツールはmacos用なので他のOSでは使用できません。しかし、構成プロファイルはplist形式のXMLファイルなので手で問題なく書けます。

## iOS構成プロファイルの作成

基本的には[構成プロファイルキーのリファレンスのVPNペイロードの項目](https://developer.apple.com/jp/documentation/featuredarticles/iPhoneConfigurationProfileRef/Introduction/Introduction.html#//apple_ref/doc/uid/TP40010206-CH1-SW27)を参照しながら書いていきます。日本語版は英語版よりも少し内容が古いようでいくつかのデフォルト値の記載が英語版と異なっている事があるので、英語版の[Configuration Profile Reference](https://developer.apple.com/library/content/featuredarticles/iPhoneConfigurationProfileRef/Introduction/Introduction.html#//apple_ref/doc/uid/TP40010206-CH1-SW27)を正として扱いましょう。

とは言うものの、全部自力で書くのは面倒なので、私が使っている構成プロファイルの載せておきます。

```
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
 <dict>
 <key>PayloadContent</key>
 <array>
 <dict>
 <key>IKEv2</key>
 <dict>
 <key>AuthName</key>
 <string>認証に使うユーザー名</string>
 <key>AuthPassword</key>
 <string>認証に使うパスワード</string>
 <key>AuthenticationMethod</key>
 <string>SharedSecret</string>
 <key>サーバー認証に使う事前共有キー PSK! PSK!</key>
 <dict>
 <key>DiffieHellmanGroup</key>
 <integer>2</integer>
 </dict>
 <key>DeadPeerDetectionRate</key>
 <string>High</string>
 <key>DisableMOBIKE</key>
 <integer>0</integer>
 <key>NATKeepAliveOffloadEnable</key>
 <integer>1</integer>
 <key>NATKeepAliveInterval</key>
 <integer>20</integer>
 <key>DisableRedirect</key>
 <integer>0</integer>
 <key>EnableCertificateRevocationCheck</key>
 <integer>0</integer>
 <key>EnablePFS</key>
 <integer>0</integer>
 <key>ExtendedAuthEnabled</key>
 <true/>
 <key>IKESecurityAssociationParameters</key>
 <dict>
 <key>EncryptionAlgorithm</key>
 <string>AES-256 </string>
 <key>IntegrityAlgorithm</key>
 <string>SHA2-256</string>
 <key>DiffieHellmanGroup</key>
 <integer>2</integer>
 <key>LifeTimeInMinutes</key>
 <integer>1440</integer>
 </dict>
 <key>LocalIdentifier</key>
 <string>myserver.com.client</string>
 <key>RemoteAddress</key>
 <string>VPNサーバーのグローバルIPアドレス</string>
 <key>RemoteIdentifier</key>
 <string>myserver.com.server</string>
 <key>SharedSecret</key>
 <string>サーバー認証に使う事前共有キー PSK! PSK!</string>
 <key>UseConfigurationAttributeInternalIPSubnet</key>
 <integer>0</integer>
 </dict>
 <key>IPv4</key>
 <dict>
 <key>OverridePrimary</key>
 <integer>1</integer>
 </dict>
 <key>PayloadDescription</key>
 <string>Configures VPN settings for iphone</string>
 <key>PayloadDisplayName</key>
 <string>VPN</string>
 <key>PayloadIdentifier</key>
 <string>com.apple.vpn.managed.それっぽいUUID</string>
 <key>PayloadType</key>
 <string>com.apple.vpn.managed</string>
 <key>PayloadUUID</key>
 <string>それっぽいUUID</string>
 <key>PayloadVersion</key>
 <real>1</real>
 <key>Proxies</key>
 <dict>
 <key>HTTPEnable</key>
 <integer>1</integer>
 <key>HTTPProxy</key>
 <string>ziproxyのプライベートIPアドレス</string>
 <key>HTTPPort</key>
 <integer>ziproxyのポート</integer>
 <key>HTTPSEnable</key>
 <integer>1</integer>
 <key>HTTPSProxy</key>
 <string>ziproxyのプライベートIPアドレス</string>
 <key>HTTPSPort</key>
 <integer>ziproxyのポート</integer>
 <key>ProxyAutoConfigEnable</key>
 <integer>0</integer>
 <key>ProxyAutoDiscoveryEnable</key>
 <integer>0</integer>
 </dict>
 <key>UserDefinedName</key>
 <string>vpn</string>
 <key>VPNType</key>
 <string>IKEv2</string>

 <key>OnDemandEnabled</key>
 <integer>1</integer>
 <key>OnDemandRules</key>
 <array>
  <dict>
    <key>Action</key>
    <string>Disconnect</string>
    <key>SSIDMatch</key>
    <array>
      <string>家のwifiのSSID</string>
    </array>
  </dict>

  <dict>
    <key>Action</key>
    <string>EvaluateConnection</string>
    <key>Action</key>
    <array>
      <dict>
        <key>InterfaceTypeMatch</key>
        <string>WiFi</string>
      </dict>
    </array>
  </dict>

  <dict>
    <key>Action</key>
    <string>EvaluateConnection</string>
    <key>Action</key>
    <array>
      <dict>
        <key>InterfaceTypeMatch</key>
        <string>Cellular</string>
      </dict>
    </array>
  </dict>

  <dict>
    <key>Action</key>
    <string>Connect</string>
    <key>InterfaceTypeMatch</key>
    <string>WiFi</string>
  </dict>

  <dict>
    <key>Action</key>
    <string>Connect</string>
    <key>InterfaceTypeMatch</key>
    <string>Cellular</string>
  </dict>

 </array>

 
 <key>VendorConfig</key>
 <dict/>
 </dict>
 </array>
 <key>PayloadDisplayName</key>
 <string>IKEv2</string>
 <key>PayloadIdentifier</key>
 <string>それっぽいUUID</string>
 <key>PayloadRemovalDisallowed</key>
 <false/>
 <key>PayloadType</key>
 <string>Configuration</string>
 <key>PayloadUUID</key>
 <string>それっぽいUUID</string>
 <key>PayloadVersion</key>
 <integer>1</integer>
 </dict>
</plist>
```

# OnDemandRules について
OnDemandEnabledを1に設定するとオンデマンド通信が有効になり、定義した状況に応じてVPNの接続・切断を自動化できます。

私が使っている構成では、以下のルールを使っています。

- 自宅wifiにつながるとVPN切断
- wifi使用時に通信を試みるとVPN接続
- 携帯電話回線使用時に通信を試みるとVPN接続
- wifiに接続するとVPN接続
- 携帯電話回線に接続すると(wifiが切れると)VPN接続

## 自宅wifiにつながるとVPN切断
```
  <dict>
    <key>Action</key>
    <string>Disconnect</string>
    <key>SSIDMatch</key>
    <array>
      <string>家のwifiのSSID</string>
    </array>
  </dict>
```

このdict型で自宅wifiにつながるとVPN切断を定義しています。SSIDは複数指定できるので、自宅以外でもVPNを使用しなくていい場所は除外できます。

## wifi使用時に通信を試みるとVPN接続
```
  <dict>
    <key>Action</key>
    <string>EvaluateConnection</string>
    <key>Action</key>
    <array>
      <dict>
        <key>InterfaceTypeMatch</key>
        <string>WiFi</string>
      </dict>
    </array>
  </dict>
```

ActionにEvaluateConnectionを指定すると、arrayで指定したすべての条件を満たせばVPNの接続が行われます。条件は複数指定可能です。

## EvaluateConnectionとConnectの違い
ActionにはEvaluateConnectionとConnectとDisconnectが指定できます。Disconnectはそのままの意味でVPN接続を終了するアクションなのでいいのですが、EvaluateConnectionとConnectは日本語版ドキュメントの記述だとちょっと違いが分かりにくいかもしれません。

EvaluateConnectionは通信を試みる度に評価されるのに対してConnectは使用するインターフェースが変わった時にだけ評価されます。つまりConnectが実行されるのはwifiが接続・切断された時だけなので、それ以降にVPN接続が何らかの理由で切断されても再接続はされません。その一方、EvaluateConnectionは通信時に評価されるのでVPN接続が切れていれば自動的に再接続してくれます。  
私の構成例ではEvaluateConnectionとConnectの両方を使用しているのは自動再接続とVPN接続の待ち時間を節約するためです。

他にも色々と設定可能な項目があるので詳しくはドキュメントを参照してください。