Protecting my macOS and iOS devices with an OpenBSD VPN_

April 04, 2019 @09:35

I have mentioned a few times that I rely on OpenBSD VPNs to ensure that clients outside of my home network get the same level of protection as they do inside. This means that I can use already existing DNS and proxy infrastructure to prevent various malvertizing, tracking, beacons, and poorly behaved applications and websites from leaking personal information, and I can prevent wifi hotspots from analyizing my traffic or injecting JavaScript. Creating the actual infrastructure is out of scope for this post, but I did previously post some information about what the DNS configuration looks like.

Part 1: OpenBSD

Setting up a VPN with OpenBSD is extremely simple compared to the many alternatives. This is a large part of why I like OpenBSD so much. I have several site to site VPN tunnels as well as the road warrior configuration all terminating on the same iked(8) instance. In my case I use an internal certificate authority for all on-network SSL/TLS so it was fairly easy to extend that to authentication for my VPNs. You can run your VPN with a pre-shared key; however, for security purposes I cannot recommend that and will instead talk about the configuration as I have implemented it.

I'm going to assume you have generated a CA and certificate and key pair for your VPN server as well as your clients in PEM (PKCS1) format.

  1. Your CA's certificate goes in /etc/iked/ca/ca.crt. This can be a bundle of CA certificates to trust.
  2. Your VPN server's certificate goes in /etc/iked/certs/ and is named based on the ID you will be using. I use FQDNs for peer identification so mine is simply the hostname of the server. So something like /etc/iked/certs/vpnserver.example.com
  3. The private key for your VPN server goes in /etc/iked/private/private.key
  4. Ensure you have pf(4) setup to properly allow traffic both to iked(8) and from the client tunnel(s) to the Internet (with NAT if you need it). For more information see pf.conf(5)

You should now be ready to tackle your iked.conf(5). Here you will need to make some choices based on your devices. You may need to look up their capabilities and test some of the options. I have used the following configuration with clients running iOS 12.2 and macOS 10.14.4. I consider this to provide the minimum viable security which is why I have the re-key lifetime fairly short.

ikev2 "RoadWarrior" passive esp \
        from 0.0.0.0/0 to 0.0.0.0/0 \
        peer 0.0.0.0/0 \
        ikesa enc aes-256 \
                prf hmac-sha2-256 \
                auth hmac-sha2-256 \
                group modp2048 \
        childsa enc aes-256 \
                auth hmac-sha2-256 \
                group modp2048 \
        srcid vpnserver.example.com \
        lifetime 180m bytes 16G \
        config address [ Addresses to assign to clients in CIDR ] \
        config name-server [ Your DNS server 1 ] \
        config name-server [ Your DNS server 2 ] \
        config netmask [ Dotted quad netmask for your client network ] \
        tag "$name-$id"

This rule should be first in your iked.conf(5) to ensure it is matched only if there is not another rule that is more specific. Please familiarize yourself with the iked.conf(5) manpage, it explains all the options available to you. The key here is that I'm setting up the tunnel to capture all IPv4 traffic from the client, and the client can come from any IPv4 address. This is part of why I don't use a pre-shared key for this. A pre-shared key could be easily compromised turning your VPN endpoint into an open proxy for all sorts of Internet ne'er-do-wells; however, if public key cryptography is being used you would have to leak a valid, signed certificate and corresponding private key. In that unlikely even you can always revoke that certificate and place a CRL in /etc/iked/crls/.

Make sure you read the iked(8) manpage, depending on your network setup you may find that you need one or more of the flags for reliable operation. In particular -6, and the -t, -T pair may be important to you.

Once you reach this point start up iked.

Part 2: macOS and iOS clients

Apple has included a reasonably fully featured VPN client in both macOS and iOS, though most of the connection configuration is not exposed via a GUI. I used Apple Configurator 2 to generate a configuration profile that can be installed on both macOS and iOS clients. You will need a CA certificate, and a client certificate and key pair. For clients I issue client only certificates which adds the benefit of the client cert not being considered valid to run a server with. In the extremely unlikely case of a compromised client, the certificate cannot be used to impersonate a server. You will want the CA certificate as a .PEM file (PKCS1) and the client certificate and key bundled in a .p12 (PKCS12) file to make the Apple Configurator happy.

Apple Configurator 2

The work flow for the Apple Configuration 2 utility is fairly straightforward. There are many more options available to you than what I will cover here. For example in my production deployments I include various device restrictions, I preload the WPA2-Enterprise configuration for my wifi SSID, configure my AirPrint printers, all alongside the VPN and certificate configuration shown here. This is just what you need to get a VPN connection going.

  1. Fill out the mandatory General page.
  2. Add your CA and client certificate bundles.
  3. Configure the VPN client, matching the authentication and transport settings from iked.conf(5) above.

Apple Configurator 2 General

Add Certificates

VPN Page 1

VPN Page 2

VPN Page 3

Manual Additions for Connect on Demand

I want my VPN to connect any time I'm not on a trusted wifi network. Thankfully Apple lets you connect to your VPN on demand with some simple rule based matching (see the VPN section of the Configuration Profile reference guide). Unfortunately the Configurator does not allow you to setup those rules in the GUI, so once you have your profile made you will need to add some keys into the plist. Open up the .mobileconfig you just created in a text editor and look for the chunk that looks like this.

        <dict>
            <key>IKEv2</key>
            ...
            ...
            <key>PayloadDescription</key>
            <string>Configures VPN settings</string>
            ...
            ...
        </dict>

To that dictionary you will need to add some additional keys to setup an OnDemand configuration. An example of the policy I use is below, but the reference document above describes the rules available in more detail. The example will start the VPN connection any time the device is not connected to the list of trusted wifi SSIDs and will automatically disconnect when the device connects to one of those trusted SSIDs.

<key>OnDemandEnabled</key>
<integer>1</integer>
<key>OnDemandRules</key>
<array>
    <dict>
        <key>Action</key>
        <string>Disconnect</string>
        <key>SSIDMatch</key>
        <array>
            <string>Your Home SSID</string>
            <string>Your Other SSID</string>
        </array>
    </dict>
    <dict>
        <key>Action</key>
        <string>Connect</string>
    </dict>
</array>

Now install the profile and you should see your VPN appear in the Settings application. Once you leave your SSIDMatch networks you should see the icon at the top of the screen showing that you are now connected.

iOS VPN Indicator

Part 3: Errata and Caveats

This has worked really well for me for years now, however there have been edge cases. In particular I have found some cases where the captive portal detection in iOS doesn't work with the on demand VPN connection.

To disable your VPN in iOS open Settings and tap General, then scroll down to VPN and then tap the "i" next to your connection name. On the following screen de-select Connect On Demand. This will disable the OnDemandRules and let traffic flow out the normal wifi / cellular interfaces.

Real clear tap target, Apple...

Disable Connect On Demand

Since your device's Internet access will now be reliant upon your VPN tunnel you may way to look into adding high availability to your OpenBSD endpoint. The manpages for carp(4), pfsync(4), and sasyncd(8) are good places to start looking.

Subscribe via RSS. Send me a comment.