On-demand iOS VPN using Configuration Profiles

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

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.

Comments are closed