On-demand iOS VPN using Configuration Profiles

By Atomstar on Saturday 23 February 2019 12:25 - Comments (2)
Category: Security, Views: 1.173

After setting up an IKEv2 VPN on my raspberry-pi, I wanted my iPhone to connect to it automatically and on-demand.

Using this strongswan guide and Apple's configuration profile reference, I configured two VPN profiles to use on my iOS devices: a basic one and an on-demand connecting profile.

The simple profile simply adds a VPN configuration you can turn on and off as you wish. For the on-demand VPN, I have these requirements:
  • When on home wifi, don't use VPN. Check by SSID
  • When on other wifi, enforce VPN
  • Else, leave up to user (e.g. on cellular)
Be sure to read through and change FIXME entries to suit your needs

Simple profile

<!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>FIXME: My Home VPN</string>
    <!-- This is a reverse-DNS style unique identifier used to detect duplicate profiles -->
    <key>PayloadIdentifier</key>
    <string>FIXME: tld.domain.subdomain.vpn1</string>
    <!-- A globally unique identifier, use uuidgen on Linux/Mac OS X to generate it -->
    <key>PayloadUUID</key>
    <string>FIXME: 0BE3E9A7-52E1-4F99-A9FF-6CFADA6A467F</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>FIXME: tld.domain.subdomain.vpn1.conf1</string>
            <!-- A globally unique identifier for this payload -->
            <key>PayloadUUID</key>
            <string>FIXME: 88998E8A-6335-4AA8-94D7-3114E3DA1512</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>FIXME: My Home VPN</string>
            <key>VPNType</key>
            <string>IKEv2</string>
            <key>IKEv2</key>
            <dict>
                <!-- Hostname or IP address of the VPN server -->
                <key>RemoteAddress</key>
                <string>FIXME: FQDN</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>FIXME: FQDN</string>
                <!-- Local IKE identity, same restrictions as above. If it is empty the client's IP address will be used -->
                <key>LocalIdentifier</key>
                <string></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></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></string>
                <!-- Set AuthenticationMethod to None to enable password based EAP -->
                <key>AuthenticationMethod</key>
                <string>None</string>
                <!-- The client uses EAP to authenticate -->
                <key>ExtendedAuthEnabled</key>
                <integer>1</integer>
                <!-- User name for EAP authentication. Since iOS 9 this is optional, the user is prompted when the profile is installed -->
                <key>AuthName</key>
                <string>FIXME: INSERTUSERNAME</string>
                <!-- Optional password for EAP authentication, if it is not set the user is prompted when the profile is installed -->
                <key>AuthPassword</key>
                <string>FIXME: INSERTPASSWORD</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> <!-- Can be AES-128, AES-256, AES-128-GCM, AES-256-GCM -->
                    <string>AES-128</string>
                    <key>IntegrityAlgorithm</key>
                    <string>SHA2-256</string> <!-- Can be SHA1-96, SHA1-160, SHA2-256, SHA2-384, SHA2-512 -->
                    <key>DiffieHellmanGroup</key>
                    <integer>19</integer> <!-- Can be 1, 2, 5, 14 (Default), 15, 16, 17, 18, 19, 20, or 21, see https://www.iana.org/assignments/ikev2-parameters/ikev2-parameters.xhtml#ikev2-parameters-8 and https://community.cisco.com/t5/security-documents/diffie-hellman-groups/ta-p/3147010-->
                </dict>
                <key>ChildSecurityAssociationParameters</key>
                <dict>
                    <key>EncryptionAlgorithm</key>
                    <string>AES-128</string>
                    <key>IntegrityAlgorithm</key>
                    <string>SHA2-256</string>
                    <key>DiffieHellmanGroup</key>
                    <integer>19</integer>
                </dict>
            </dict>
        </dict>
    </array>
</dict>
</plist>

On-Demand profile

<!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>FIXME: Home OnDemand VPN profile</string>
    <!-- This is a reverse-DNS style unique identifier used to detect duplicate profiles -->
    <key>PayloadIdentifier</key>
    <string>FIXME: tld.domain.subdomain.vpn1</string>
    <!-- A globally unique identifier, use uuidgen on Linux/Mac OS X to generate it -->
    <key>PayloadUUID</key>
    <string>FIXME: 0BE3E9A7-52E1-4F99-A9FF-6CFADA6A467F</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>FIXME: tld.domain.subdomain.vpn1.conf1</string>
            <!-- A globally unique identifier for this payload -->
            <key>PayloadUUID</key>
            <string>FIXME: 88998E8A-6335-4AA8-94D7-3114E3DA1512</string>
            <key>PayloadType</key>
            <string>com.apple.vpn.managed</string>
            <key>PayloadVersion</key>
            <integer>1</integer>
            <!-- Display name of VPN connection settings -->
            <key>PayloadDisplayName</key>
            <string>FIXME: Home OnDemand VPN</string>
            <!-- This is the name of the VPN connection as seen in the VPN application later, shown as 'service name' -->
            <key>UserDefinedName</key>
            <string>FIXME: Home OnDemand VPN</string>
            <key>VPNType</key>
            <string>IKEv2</string>
            <key>IKEv2</key>
            <dict>
                <!-- Hostname or IP address of the VPN server -->
                <key>RemoteAddress</key>
                <string>FIXME: FQDN</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>FIXME: FQDN</string>
                <!-- Local IKE identity, same restrictions as above. If it is empty the client's IP address will be used -->
                <key>LocalIdentifier</key>
                <string></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></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></string>
                <!-- Set AuthenticationMethod to None to enable password based EAP -->
                <key>AuthenticationMethod</key>
                <string>None</string>
                <!-- The client uses EAP to authenticate -->
                <key>ExtendedAuthEnabled</key>
                <integer>1</integer>
                <!-- User name for EAP authentication. Since iOS 9 this is optional, the user is prompted when the profile is installed -->
                <key>AuthName</key>
                <string>FIXME: INSERTUSERNAME</string>
                <!-- Optional password for EAP authentication, if it is not set the user is prompted when the profile is installed -->
                <!-- Use random strings, e.g. like: dd if=/dev/urandom count=1 bs=32 2>/dev/null | base64 -->
                <key>AuthPassword</key>
                <string>FIXME: INSERTPASSWORD</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> <!-- Can be AES-128, AES-256, AES-128-GCM, AES-256-GCM -->
                    <string>AES-128</string>
                    <key>IntegrityAlgorithm</key>
                    <string>SHA2-256</string> <!-- Can be SHA1-96, SHA1-160, SHA2-256, SHA2-384, SHA2-512 -->
                    <key>DiffieHellmanGroup</key>
                    <integer>19</integer> <!-- Can be 1, 2, 5, 14 (Default), 15, 16, 17, 18, 19, 20, or 21, see https://www.iana.org/assignments/ikev2-parameters/ikev2-parameters.xhtml#ikev2-parameters-8 and https://community.cisco.com/t5/security-documents/diffie-hellman-groups/ta-p/3147010-->
                </dict>
                <key>ChildSecurityAssociationParameters</key>
                <dict>
                    <key>EncryptionAlgorithm</key>
                    <string>AES-128</string>
                    <key>IntegrityAlgorithm</key>
                    <string>SHA2-256</string>
                    <key>DiffieHellmanGroup</key>
                    <integer>19</integer>
                </dict>
                <key>OnDemandEnabled</key>
                <integer>1</integer>
                <key>OnDemandRules</key>
                <array>
                    <!-- 
                         1. Check if we are connected to a WiFi network
                         2. Check if the SSID is included in the SSIDMatch-array
                         3. If 1 + 2 are true, then disconnect the tunnel
                    -->
                    <dict>
                        <key>InterfaceTypeMatch</key>
                        <string>WiFi</string>
                        <key>SSIDMatch</key>
                        <array>
                            <string>FIXME: HOMEWIFISSID</string>
                        </array>                    
                        <key>Action</key>
                        <string>Disconnect</string>
                    </dict>
                    <!-- 
                        Connect on all WiFi not home
                    -->
                    <dict>
                        <key>InterfaceTypeMatch</key>
                        <string>WiFi</string>
                        <key>Action</key>
                        <string>Connect</string>
                    </dict>

                    <!-- 
                         1. For each connection attempt, test if the domain name is included in the Domains-array
                         2. If 1 is true, try domain name resolution
                         3. If 2 fails or times out, establish a VPN connection
                    -->
                    <!--                
                    <dict>
                        <key>Action</key>
                        <string>EvaluateConnection</string>
                        <key>ActionParameters</key>
                        <array>
                            <dict>
                                <key>Domains</key>
                                <array>
                                    <string>*.internal.mydomain.com</string>
                                </array>    
                                <key>DomainAction</key>
                                <string>ConnectIfNeeded</string>
                            </dict>
                        </array>
                    </dict>
                -->
                    <!-- 
                        Default entry, ignore any other cases
                    -->
                    <dict>
                        <key>Action</key>
                        <string>Ignore</string>
                    </dict>
                </array> <!-- End of OnDemandRules -->
            </dict> <!-- End of IKEv2 -->
        </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>tld.domain.subdomain.vpn1.cert</string>
            <key>PayloadUUID</key>
            <string>8CF9A17F-EB30-45A4-A416-7121046879AD</string>
            <key>PayloadType</key>
            <string>com.apple.security.root</string>
            <key>PayloadVersion</key>
            <integer>1</integer>
            <!-- This is the Base64 (PEM) encoded CA certificate -->
            <!-- Get from cat /etc/ipsec.d/certs/vpn-server-cert.pem or similar -->
            <key>PayloadContent</key>
            <data>
MIIFaz...
            </data>
        </dict> 
    </array> <!-- End of array of configurations -->
</dict>
</plist>

Volgende: DNS-based AdBlock on OpenWRT 02-'19 DNS-based AdBlock on OpenWRT
Volgende: Speeding up an nginx webserver 02-'19 Speeding up an nginx webserver

Comments


By Tweakers user ThinkPad, Monday 25 February 2019 09:39

Would the on-demand option also be possible with OpenVPN?

By Tweakers user Atomstar, Monday 25 February 2019 17:41

ThinkPad wrote on Monday 25 February 2019 @ 09:39:
Would the on-demand option also be possible with OpenVPN?
For on-demand OpenVPN use, I think the separate App should work. iOS doesn't natively support OpenVPN, and there is no mention of OpenVPN in the Apple Configuration Profile Reference.

Comment form
(required)
(required, but will not be displayed)
(optional)