the codingmerc llc

application programming expert for hire

iOS VPN on-demand profile with OpenVPN

If your goal is to create an iOS mobile profile to establish an openVPN connection on-demand using the iOS native VPN functionality, please read on!

Prerequisites

In order to use the on-demand connection functionality provided natively by iOS we need the following ingredients:

You can download Apple Configurator from the macOS App Store. It is currently available as version 2 for free. The openVPN profile should include everything required to correctly initiate a VPN connection from you iOS device. I highly encourage you to test the profile on iOS using OpenVPN Connect before attempting to convert it to an on-demand profile.

Authenticating on-demand

Before we start working on the profile we need to create a pkcs12 bundle including the credentials required to authenticate with the openVPN connection. The on-demand profile does not allow the user to manually enter a username and password. This information needs to be included in the mobile profile.

# openssl pkcs12 -export -in openVPN.crt -inkey openVPN.key -certfile server.crt -name iPhone -out iPhone.p12

The above openssl command will create a pkcs12 bundle called iPhone.p12. The command will ask you for a password required to import the credentials.

Creating the mobile profile

Launch Apple Configurator. Press [Command + N] on the Keyboard or select File -> New Profile from the menu.

Apple Configurator 2  General

General

We need to fill out some of the sections outlined on the left hand side of the profile editor. Starting with General. You can enter anything you want really, just make sure to choose a unique identifier as installing profiles with the same identifier will overwrite the previously installed profile. You can not install multiple profiles that share an identifier on the same device.

Apple Configurator  Certificates

Certificates

The next section we work on is certificates. Here you will insert your credentials to authenticate the connection. Insert the .p12 file here by clicking the + button in the top right. Enter the password from the openssl command in the respective field so that the credentials can be imported.

Optionally include additional certificates useful or required after you established the VPN connection. In my case I included a  host certificate to securely establish a TLS connection to a web server on the VPN.

Apple Configurator 2  VPNVPN

The last section we need to modify is VPN. Please make sure to double check each value entered as it highly likely that your profile will not be able to establish an on-demand connection if you enter the wrong values. Feel free to experiment with you own values. I will outline some required fields below:

Custom Data

The Custom Data field needs to include all the information from the .ovpn file as key-value pairs. Open your openVPN profile with a text editor. The first option in each line needs to be added as the key, everything afterwards in the same line is added as value.

If an option (like nobind or float) does not have a value argument, insert NOARGS as value!
 
This sections must include pairs for the following keys if the server requires them: remote, ca, cert, key, tls-auth, key-direction, auth-user-pass, comp-lzo, cipher, auth, ns-cert-type and remote-cert-tls. Make sure to include the pair with key vpn-on-demand and value 1.
 
Please consult the OpenVPN Connect iOS FAQ for more information on specific fields and troubleshooting.
 

On-Demand Configuration

Save your created profile (do not sign it yet!) and open it using a text editor. It will look like a .plist XML file.
 
<!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>Password</key>
                    <string>my_certificate_password</string>
                    <key>PayloadCertificateFileName</key>
                    <string>iPhone.p12</string>
                    <key>PayloadContent</key>
                    <data>
                        MIIQCAIBAzCCD84GCSqGSIb3DQEHAaCCD78Egg+7MIIPtzCCCA8G
                        CSqGSIb3DQEHBqCCCAAwggf8AgEAMIIH9QYJKoZIhvcNAQcBMBwG
                        			...
                        vjAxMCEwCQYFKw4DAhoFAAQUVsfItyK2mO7WkfLj1qtxql9QwoME
                        CHklj4X4qN1XAgIIAA==
                        </data>
                        <key>PayloadDescription</key>
                        <string>Adds a PKCS#12-formatted certificate</string>
                        <key>PayloadDisplayName</key>
                        <string>iPhone.p12</string>
                        <key>PayloadIdentifier</key>
                        <string>com.apple.security.pkcs12.58843978-B7D8-4050-BD77-8EB88BB41F00</string>
                        <key>PayloadType</key>
                        <string>com.apple.security.pkcs12</string>
                        <key>PayloadUUID</key>
                        <string>58843978-B7D8-4050-BD77-8EB88BB41F00</string>
                        <key>PayloadVersion</key>
                        <integer>1</integer>
                    </dict>
                    <dict>
                        <key>IPv4</key>
                        <dict>
                            <key>OverridePrimary</key>
                            <integer>0</integer>
                        </dict>
                        <key>PayloadDescription</key>
                        <string>Configures VPN settings</string>
                        <key>PayloadDisplayName</key>
                        <string>VPN</string>
                        <key>PayloadIdentifier</key>
                        <string>com.apple.vpn.managed.432DFAA8-91D5-4954-8E7D-5E205595E7D4</string>
                        <key>PayloadType</key>
                        <string>com.apple.vpn.managed</string>
                        <key>PayloadUUID</key>
                        <string>432DFAA8-91D5-4954-8E7D-5E205595E7D4</string>
                        <key>PayloadVersion</key>
                        <integer>1</integer>
                        <key>Proxies</key>
                        <dict>
                            <key>HTTPEnable</key>
                            <integer>0</integer>
                            <key>HTTPSEnable</key>
                            <integer>0</integer>
                        </dict>
                        <key>UserDefinedName</key>
                        <string>homeVPN</string>
                        <key>VPN</key>
                        <dict>
                            <key>AuthName</key>
                            <string>iPhone</string>
                            <key>AuthenticationMethod</key>
                            <string>Certificate</string>
                            <key>OnDemandEnabled</key>
                            <integer>1</integer>
                            <key>OnDemandMatchDomainsAlways</key>
                            <array/>
                            <key>OnDemandMatchDomainsOnRetry</key>
                            <array/>
                            <key>OnDemandRules</key>
                            <array>
                                ...
                            </array>
                            <key>PayloadCertificateUUID</key>
                            <string>58843978-B7D8-4050-BD77-8EB88BB41F00</string>
                            <key>RemoteAddress</key>
                            <string>DEFAULT</string>
                        </dict>
                        <key>VPNSubType</key>
                        <string>net.openvpn.OpenVPN-Connect.vpnplugin</string>
                        <key>VPNType</key>
                        <string>VPN</string>
                        <key>VendorConfig</key>
                        <dict>
                            <key>auth-user-pass</key>
                            <string>my_username\my_password</string>
                            <key>ca</key>
                            <string>-----BEGIN CERTIFICATE——\nMIIDQTCCAimgAwIBAgIJA … 6XTnCagHl5wV+ZI2eTc\n-----END CERTIFICATE-----\n</string>
                            <key>cert</key>
                            <string>-----BEGIN CERTIFICATE——\nMIID1jCCAr6gAwIBAgIBC … lEq3EyC5qJc4=\n-----END CERTIFICATE-----\n</string>
                            <key>cipher</key>
                            <string>AES-256-CBC</string>
                            <key>comp-lzo</key>
                            <string>adaptive</string>
                            <key>float</key>
                            <string>NOARGS</string>
                            <key>keepalive</key>
                            <string>15 60</string>
                            <key>key</key>
                            <string>-----BEGIN RSA PRIVATE KEY——\nMIIG4wIBAAKCAYEA … psUtuM+qAfu\n-----END RSA PRIVATE KEY-----\n</string>
                            <key>nobind</key>
                            <string>NOARGS</string>
                            <key>ns-cert-type</key>
                            <string>server</string>
                            <key>proto</key>
                            <string>tcp-client</string>
                            <key>remote</key>
                            <string>vpn.my_remote.com 1194</string>
                            <key>resolv-retry</key>
                            <string>infinite</string>
                            <key>tls-auth</key>
                            <string>-----BEGIN OpenVPN Static key V1——\nfee00942b7f0eb65179 … 4b36102a683a4\n-----END OpenVPN Static key V1-----\n</string>
                            <key>vpn-on-demand</key>
                            <string>1</string>
                        </dict>
                    </dict>
                </array>
                <key>PayloadDescription</key>
                <string>Connect via OpenVPN on demand</string>
                <key>PayloadDisplayName</key>
                <string>OpenVPN onDemand</string>
                <key>PayloadIdentifier</key>
                <string>com.codingmerc.openvpn.ondemand.blog</string>
                <key>PayloadOrganization</key>
                <string>CodingMerc, LLC</string>
                <key>PayloadRemovalDisallowed</key>
                <false/>
                <key>PayloadType</key>
                <string>Configuration</string>
                <key>PayloadUUID</key>
                <string>02BFE2AD-94BC-4980-8DF3-025D9CDA73DF</string>
                <key>PayloadVersion</key>
                <integer>1</integer>
            </dict>
        </plist>

Make sure that all certificates and information are in the correct format. I redacted my certificate information in the example above.

OnDemandRules

Lets have a look at the OnDemandRules section of the file. Here you need configure a rule-set when you want your device to establish secure VPN connection on-demand using the openVPN profile included in the mobile configuration.

<key>OnDemandRules</key>
<array>
<dict>
<key>Action</key>
<string>Disconnect</string>
<key>DNSServerAddressMatch</key>
<array>
<string>192.168.1.1</string>
</array>
<key>InterfaceTypeMatch</key>
<string>WiFi</string>
<key>SSIDMatch</key>
<string>MyVPNSiteWifi</string>
</dict>

If the phone is connected to the Wifi that belongs to my VPN network site I don’t want to connect on-demand. This connection is already considered secure.

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

If the phone is connected to any other Wifi I need it to establish a VPN connection to my secure site.

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

If the phone is connected via cellular only and not connected to a Wifi currently I need it to establish a VPN connection on-demand.

  <dict>
<key>Action</key>
<string>Disconnect</string>
</dict>
</array>

If none of the rules above match, the VPN connection should be disconnected.

I did not find an official documentation to all available rules and options. I was able to derive this basic set of information from the example iOS VPN On-Demand Rules found on Derman Enterprises blog.

Sign Profile

After you saved your rules set you can optionally sign your on-demand VPN connection mobile profile if you want and have a valid set of credentials. This will display the signing information along with the profiles information when the configuration is installed on the device.
Just open the file in Apple Configurator again and choose File -> Sign Profile from the menu.

Installation

You can now install the (signed) profile on an iOS device. Just share it via AirDrop or eMail for example or distribute it via MDM. 

© 2023 the codingmerc llc