Easy IKEv2 VPN for mobile devices (inc iOS)

I recently obtained an iPhone and needed to connect it to my VPN. However my existing VPN server was an OpenVPN installation which works lovely on traditional desktop operating systems and Android, but the iOS client is a bit more questionable having last been updated in September 2014 (pre iOS 9).

I decided to look into what the “proper” VPN option would be for iOS in order to get something that should be supported by the OS as smoothly as possible. Last time I looked this was full of wonderful horrors like PPTP (not actually encrypted!!) and L2TP/IPSec (configuration hell), so I had always avoided like the plague.

However as of iOS 9+, Apple has implemented support for IKEv2 VPNs which offers an interesting new option. What particularly made this option attractive for me, is that I can support every device I have with the one VPN standard:

  • IKEv2 is built into iOS 9 and MacOS El Capitan.
  • IKEv2 is built into Windows 10.
  • Works on Android with a third party client (hopeful for native integration soonish?).
  • Naturally works on GNU/Linux.

Whilst I love OpenVPN, being able to use the stock OS features instead of a third party client is always nice, particularly on mobile where power management and background tasks behaviour can be interesting.

IKEv2 on mobile also has some other nice features, such as MOBIKE, which makes it very seamless when switching between different networks (like the cellular to WiFi dance we do constantly with phones/tablets). This is something that OpenVPN can’t do – whilst it’s generally fast and reliable at establishing a connection, a change in the network means issuing a reconnect, it doesn’t just move the current connection across.

 

Given that I run GNU/Linux servers, I went for one of the popular IPSec solutions available on most distributions – StrongSwan.

Unfortunately whilst it’s technical capabilities are excellent, it’s documentation isn’t great. Best way to describe it is that every option is documented, but what options and why you’d want to use them? Not so much. The “left” vs “right” style documentation is also a right pain to work with, it’s not a configuration format that reads nicely and clearly.

Trying to find clear instructions and working examples of configurations for doing IKEv2 with iOS devices was also difficult and there’s some real traps for young players such as generating SHA1 certs instead of SHA2 when using the tools with defaults.

The other fun is that I also wanted my iOS device setup properly to:

  1. Use certificate based authentication, rather than PSK.
  2. Only connect to the VPN when outside of my house.
  3. Remain connected to the VPN even when moving between networks, etc.

I found the best way to make it work, was to use Apple Configurator to generate a .mobileconfig file for my iOS devices that includes all my VPN settings and certificates in an easy-to-import package, but also (critically) allows me to define options that are not selectable to end users, such as on-demand VPN establishment.

 

After a few nights of messing around and cursing the fact that all the major OS vendors haven’t just implemented OpenVPN, I managed to get a working connection. To avoid others the same pain, I considered writing a guide – but it’s actually a really complex setup, so instead I decided to write a Puppet module (clone from github / or install from puppetforge) which does the following heavy lifting for you:

  • Installs StrongSwan (on a Debian/derived GNU/Linux system).
  • Configures StrongSwan for IKEv2 roadwarrior style VPNs.
  • Generates all the CA, cert and key files for the VPN server.
  • Generates each client’s certs for you.
  • Generates a .mobileconfig file for iOS devices so you can have a single import of all the configuration, certs and ondemand rules and don’t have to have a Mac to use Apple Configurator.

This means you can save yourself all the heavy lifting and setup a VPN with as little as the following Puppet code:

class { 'roadwarrior':
   manage_firewall_v4 => true,
   manage_firewall_v6 => true,
   vpn_name           => 'vpn.example.com',
   vpn_range_v4       => '10.10.10.0/24',
   vpn_route_v4       => '192.168.0.0/16',
 }

roadwarrior::client { 'myiphone':
  ondemand_connect       => true,
  ondemand_ssid_excludes => ['wifihouse'],
}

roadwarrior::client { 'android': }

The above example sets up a routed VPN using 10.10.10.0/24 as the VPN client range and routes the 192.168.0.0/16 network behind the VPN server back through. (Note that I haven’t added masquerading options yet, so your gateway has to know to route the vpn_range back to the VPN server).

It then defines two clients – “myiphone” and “android”. And in the .mobileconfig file generated for the “myiphone” client, it will specifically generate rules that cause the VPN to maintain a constant connection, except when connected to a WiFi network called “wifihouse”.

The certs and .mobileconfig files are helpfully placed in  /etc/ipsec.d/dist/ for your rsync’ing pleasure including a few different formats to help load onto fussy devices.

 

Hopefully this module is useful to some of you. If you’re new to Puppet but want to take advantage of it, you could always check out my introduction to Puppet with Pupistry guide.

If you’re not sure of my Puppet modules or prefer other config management systems (or *gasp* none at all!) the Puppet module should be fairly readable and easy enough to translate into your own commands to run.

There a few things I still want to do – I haven’t yet done IPv6 configuration (which I’ll fix since I run a dual-stack network everywhere) and I intend to add a masquerade firewall feature for those struggling with routing properly between their VPN and LAN.

I’ve been using this configuration for a few weeks on a couple iOS 9.3.1 devices and it’s been working beautifully, especially with the ondemand configuration which I haven’t been able to do on any other devices (like Android or MacOS) yet unfortunately. The power consumption overhead seems minimal, but of course your mileage may vary.

It would be good to test with Windows 10 and as many other devices as possible. I don’t intend for this module to support non-roadwarrior type configs (eg site-to-site linking) to keep things simple, but happy to merge any PRs that make it easier to connect more mobile devices or branch routers back to a main VPN host. Also happy to merge PRs for more GNU/Linux distribution support- currently only support Debian/Ubuntu, but it shouldn’t be hard to add others.

If you’re on Android, this VPN will work for you, but you may find the OpenVPN client better and more flexible since the Android client doesn’t have the same level of on demand functionality that iOS has built in. You may also find OpenVPN a better option if you’re regularly using restrictive networks that only allow “HTTPS” out, since it can be run on TCP/443, whereas StrongSwan IKEv2 runs on UDP port 500 or 4500.

This entry was posted in Uncategorized and tagged , , , , , , , , , . Bookmark the permalink.

15 Responses to Easy IKEv2 VPN for mobile devices (inc iOS)

  1. John says:

    Hi Jethro. Thanks for the tutorial and module. I am new to Puppet so this post seems like its missing some instruction or code. I was happy to go through your pupistry tutorial and was relieved that you were using AWS as well as a Mac and thought this post/tutorial would also allow me to set up a IKEv2 VPN on AWS EC2. I definitely misunderstood something along the way. Have you configured this with an AWS EC2 instance? Also it would be great to extend this post with a full tutorial with a basic setup of the IKEv2 from start to finish? Cheers, John

    • Jethro Carr says:

      hi John,

      Nothing in here is AWS specific, you just need to provision a Linux server as usual and then ideally add this Puppet module to your standard Puppet configuration.

      However if you’re struggling with Puppet, see the comment to ed below – it may be more achievable for you.

      Re tutorial – I don’t plan to write one. IKE and IPSec setup is a right PITA and the learning curve and ease of debugging is extremely high. I wrote this module so I could get a solution that works first time, every time instead :-)

      regards,
      Jethro

  2. ed says:

    As someone with no working knowledge of puppet, what is the fastest way to get this script up and running on a single computer?

    I’ve installed puppet and this script, and have tried to do a puppet apply but to no avail. Thanks!

    • Jethro Carr says:

      You can install Puppet but not run the daemon and treat it like a script if you wish. Example:


      apt-get install puppet

      puppet module install jethrocarr/roadwarrior

      cat > /root/configure_vpn.pp < < EOF class { 'roadwarrior': manage_firewall_v4 => false,
      manage_firewall_v6 => false,
      vpn_name => 'vpn.example.com',
      vpn_range_v4 => '10.10.10.0/24',
      vpn_route_v4 => '192.168.0.0/16',
      }

      roadwarrior::client { 'myiphone':
      ondemand_connect => true,
      ondemand_ssid_excludes => ['wifihouse'],
      }

      roadwarrior::client { 'android': }
      EOF

      # Edit configuration to suit requirements.
      # vim /root/configure_vpn.pp

      # Apply configuration. You can re-run this if you make changes to
      # the above file in future (such as adding new clients).
      puppet apply /root/configure_vpn.pp

  3. JJ says:

    Hi Jethro,
    I have everything setup completely following your tutorial (great tutorial by the way!!) — however, I’m unable to connect to the VPN server when not connected to my home wifi. I’ve even put my VPN server into a DMZ, and that makes no difference either. I look through the logs in /var/log/auth.log and I see no indication that my phone is even trying to make a connection to the server. Do you know where I may be getting tripped up? I’m happy to provide other logs if needed as well. Thanks!

    • Jethro Carr says:

      hi JJ,

      If your phone isn’t making any connection, most likely something network related, eg misconfiguration of your DNS, invalid NAT

      I’d suggest doing a packet dump on your VPN server with tcpdump and see what traffic (if any) is coming in during connection attempts. If you’re not getting any packets when your phone tries to connect, then there’s something wrong with your network setup.

      If you get stuck, feel free to email me more information and I may be able to make a better suggestion.

      regards,
      Jethro

  4. Gary says:

    Hi Jethro

    Nice module. Runs into a dependency issue for me on Ubuntu 14.04 — there is no libstrongswan-extra-plugins module. Probably needs an OS switch on Debian -v- Ubuntu… I may make a PR if I fix it.

    Thanks

    Gary

  5. mail says:

    Hi, I followed all steps but got many error messages after applying the config

    Warning: /Stage[main]/Roadwarrior/File[/etc/ipsec.conf]: Skipping because of failed dependencies

    Error: Execution of ‘/usr/bin/apt-get -q -y -o DPkg::Options::=–force-confold install libstrongswan-extra-plugins’ returned 100: Reading package lists…
    Building dependency tree…
    Reading state information…
    E: Unable to locate package libstrongswan-extra-plugins

    I was not able to install these extra plugins, I got the error message that this packages could not be located.

    Thanks for your help

  6. Jafar says:

    The VPN server when not connected to my home wifi. I’ve even put my VPN server into a DMZ, and that makes no difference either. I look through the logs in /var/log/auth.log and I see no indication that my phone is even trying to make a connection to the server. Do you know where I may be getting tripped up.

    • Jethro Carr says:

      hi Jafar,

      If your phone isn’t making any connection, most likely something network related, eg misconfiguration of your DNS, invalid NAT

      I’d suggest doing a packet dump on your VPN server with tcpdump and see what traffic (if any) is coming in during connection attempts. If you’re not getting any packets when your phone tries to connect, then there’s something wrong with your network setup.

      regards,
      Jethro

  7. Jethro Carr says:

    hi all,

    If having any issues with this module, please file against https://github.com/jethrocarr/puppet-roadwarrior/issues – it’s a better way of issue tracking and resolving than blog posts.

    I won’t be accepting future comments on this post regarding technical issues if you failed to read the above :-)

    regards,
    Jethro

  8. frankw says:

    Thanks that was very helpful – I didn’t use the puppet module, but I managed to getiOS10 IKEv2 onDemand working with an already setup Strongswan and your instructions.

  9. Laurent says:

    Hi Jethro

    Really well done. Works out of the box ! Many thanks for it.
    Just wandering if:

    conn CiscoIPSec
    keyexchange=ikev1

    in ipsec.conf does not create a security risk.

    What do you think ?
    Laurent

Leave a Reply