ios

A 2-post collection

IKEv2 VPN with StrongSWAN

Wow this was harder than i thought.

I just wanted to get a modern VPN on all my devices without the hassle to install third-party VPN clients on all of them (hello OpenVPN o/). The protocol of choice seems to be IKEv2 as all devices that I own seem to support this and it is more secure than the old PPTP or L2TP protocols the devices could support natively.

But let's just jump directly into it.

IPsec config

On the server we will be using StrongSWAN. All configuration is for Ubuntu 15.10 but should work on any distribution that has StrongSWAN as the configuration did not really change in the last few years.

Install StrongSWAN

At first we need to install StrongSWAN (all steps from here on should be done as the root user, switch to root by issuing sudo su - and typing your password):

apt install strongswan strongswan-plugin-af-alg strongswan-plugin-agent strongswan-plugin-certexpire strongswan-plugin-coupling strongswan-plugin-curl strongswan-plugin-dhcp strongswan-plugin-duplicheck strongswan-plugin-eap-aka strongswan-plugin-eap-aka-3gpp2 strongswan-plugin-eap-dynamic strongswan-plugin-eap-gtc strongswan-plugin-eap-mschapv2 strongswan-plugin-eap-peap strongswan-plugin-eap-radius strongswan-plugin-eap-tls strongswan-plugin-eap-ttls strongswan-plugin-error-notify strongswan-plugin-farp strongswan-plugin-fips-prf strongswan-plugin-gcrypt strongswan-plugin-gmp strongswan-plugin-ipseckey strongswan-plugin-kernel-libipsec strongswan-plugin-ldap strongswan-plugin-led strongswan-plugin-load-tester strongswan-plugin-lookip strongswan-plugin-ntru strongswan-plugin-pgp strongswan-plugin-pkcs11 strongswan-plugin-pubkey strongswan-plugin-radattr strongswan-plugin-sshkey strongswan-plugin-systime-fix strongswan-plugin-whitelist strongswan-plugin-xauth-eap strongswan-plugin-xauth-generic strongswan-plugin-xauth-noauth strongswan-plugin-xauth-pam strongswan-pt-tls-client

This one seems a bit excessive but I just installed everything I could find for StrongSWAN as I am lazy.

Configuration

The next step is to get rid of the default configuration and supply our own:

The best bet here is to just move away the default config in /etc/ipsec.conf (or delete it as it does not contain anything of any value) and copy and paste the config above into it.

You will have to modify some values:

  • yourhostname.net should be the hostname of the box you connect to.
  • rightsourceip should be a private IPv4 network and a subnet of the IPv6 subnet of your server (if your server got a /64 probably add another address part and use a /112 here)
  • rightdns is the dns server that will be sent to the client, I just used Google's free DNS servers here.

If you only want to use IPv4 just remove the v6 addresses.

Packet forwarding

To allow the connected VPN clients to actually talk to each other you'll have to enable packet forwarding, if you don't do that the clients will only be able to speak with the server.

Create a new file in /etc/sysctl.d named 99-vpn.conf:

Reload the settings with sysctl --system

If you want to give the VPN clients Internet access you'll have to enable NAT for the interfaces and routing for IPv6. I just added these lines to /etc/rc.local, you probably want to use the default facility for iptables rules of your distribution though:

Don't forget to actually run the script afterwards to enable the rules without rebooting. (D'oh!)

Generating certificates

To be on the real secure side and to make device provisioning as easy as possible we use X.509 certificates to connect to the VPN.
There are 3 sets of certificates:

  • The root CA
  • The VPN server certificate
  • The client certificates

Switch to /etc/ipsec.d and run all the following in that directory.

Root CA

For a CA we need a key first (we pick a 4096 bit long RSA key here):

ipsec pki --gen --type rsa --size 4096 --outform der > private/strongswanKey.der
chmod 600 private/strongswanKey.der

So let's create a root CA:

ipsec pki --self --ca --lifetime 3650 --in private/strongswanKey.der --type rsa --dn "C=DE, O=Dunkelstern, CN=Dunkelstern VPN Root CA" --outform der > cacerts/strongswanCert.der

So what's all that stuff?

  • First we tell the ipsec tool to create a self signed ca with roughly 10 years of lifetime
  • Use the key we generated
  • Tell the pki tool some settings: The country (DE), the Organisation (Dunkelstern) and the Common Name (Dunkelstern VPN Root CA)

You should probably move all the root CA private files (the key!) off the machine after you're done with them and put them on a disk into a safe or something.

VPN Certificates

So we have a CA but we definitely do not want to use that directly for the VPN server, so we create a derivative Certificate that has the root CA as parent. So at first we need a key again:

ipsec pki --gen --type rsa --size 4096 --outform der > private/vpnHostKey.der
chmod 600 private/vpnHostKey.der

And now comes the interesing part:

export vpn_host="vpn.example.org"
export vpn_ipv4="10.0.0.1"
export vpn_ipv6="::1"

ipsec pki --pub --in private/vpnHostKey.der --type rsa | ipsec pki --issue --lifetime 730 --cacert cacerts/strongswanCert.der --cakey private/strongswanKey.der --dn "C=DE, O=Dunkelstern, CN=$vpn_host" --san $vpn_ipv4 --san @$vpn_ipv4 --san $vpn_ipv6 --san @$vpn_ipv6 --flag serverAuth --flag ikeIntermediate --outform der > certs/vpnHostCert.der

You'll have to replace some values here:

  • vpn_host the hostname of the VPN server
  • vpn_ipv4 the public IPv4 address
  • vpn_ipv6 the public IPv6 address

Now it is really time to move the CA root private key ;)

Client certificates

To create client certificates I made a small script as you'll probably do this often:

Usage is something like create_vpn_user.sh kopernikus which will drop a p12 file in /etc/ipsec.d/p12/ with the name you supplied. The p12 file is encrypted with a pass-phrase you'll have to supply.

iOS

To get a working VPN config onto an iOS device you'll have to use a *.mobileconfig configuration profile as the VPN GUI of the iPhone and iPad has a bug that prevents valid connections as of iOS 9.3.

Create mobile config profile

Fetch the Apple Configurator 2 from the AppStore on a Mac (it's free but sadly there is no configurator for Windows)

After starting the configurator choose File->New Profile from the menu and fill the generic info field as you want:

Apple Configurator generic info

For the next step you'll need the p12 file and the vpnHostCert.der file to add to the certificate store:

Apple Configurator certificate store

And the last step: Configure the VPN

Apple Configurator VPN

Make sure you select the certificate auth method and the correct certificate. The Remote Identifier is what's in leftid in the ipsec.conf, the Local Identifier is what's in the Certificate's common name (machine@host).

Attention: Set the Encryption algorithm for IKE SA Params and Child SA Params to something sensible, do not use the 3DES default. 3DES is inherently unsafe!

If you're ready, save the config somewhere. So there's another step to get it running: Another Bug workaround!

Open the generated file in some text-editor and search for OverridePrimary and set it to zero!

Install on iOS device

The easiest way to install the configuration profile is just sending it to yourself as an email and then tap the attachment and allow it to install the VPN. If your device is enrolled in MDM (Mobile device management) you can send the profile over the air.

Connect

To connect to the VPN go into Settings->General->VPN and turn on the switch. All traffic will now be sent through the VPN. If the switch turns off immediately you either forgot to set OverridePrimary to zero or you chose an encryption that the server does not understand. Look into the server logfile for more information.

Mac

Just create a config file like you do for an iOS device and double click it. It will open in the System Preferences Profiles pane (which is not visible until you import a profile)

OSX System Prefs

I had to click the plus button and add the profile again as the preference pane did open but it did not automatically import the profile.

If you want to have an icon in the menu bar, switch to the Network pref-pane and tick the checkbox to show that icon:

Show in menu bar

Windows

(UPDATE: Wait stop right here! Read the follow up: The right way to setup a VPN on windows)

Oh my... Microsoft! To get it running on Windows you will have to jump over some obstacles. I am no windows guy, perhaps there's an easier way to do it, please mail me: hallo@dunkelstern.de

Import Certificates

Run the Management Console from the Win+R box with mmc:

Management Console

Now add the Certificates snap-in (File -> Add/Remove Snap in...):

Certificates Snap in

Switch to Console Root -> Certificates -> Personal -> Certificates and import the p12 file by clicking through the import wizard at Action -> All tasks -> Import.

Now you should have two new entries:

  • machinename@host
  • XY VPN Root CA

Move the Root CA to Trusted Root Certificate Authorities -> Certificates to trust it.

Setup VPN

First open the Network and Sharing Center (best done by right-clicking on the network icon in the task bar)

Network and Sharing Center

Now set up a new Network connection:

Network connection Workplace

VPN, not dialup

VPN step 1

Now go back to the Network and Sharing Center and click on Change Adapter Settings on the left. Select your VPN connection and open the properties window.

VPN Properties

Switch to the Security pane and set the VPN type to IKEv2 and the authentication to Use machine certificates.

IKEv2

On the Networking pane open the properties dialog for both Internet Protocol versions and set up the DNS servers as windows does not automatically take those from the VPN connection

DNS here

The connection is now ready to be activated, but there are some bugs hidden.

Public network? Why?

If you double click to connect now (which throws you over to the modern control panel)

Connect finally

You may notice that the network is classified as a public network. If you don't want it to be public follow the next steps. Skip them if public is ok with you.

First open the policy editor by running gpedit.msc from the Win+R window. Now navigate to Local Computer Policy -> Computer Configuration -> Windows Settings -> Security Settings -> Network List Manager Policies (Why is everything so wordy?), right click the VPN connection and set the Network Location

Network Location type

Getting IPv6 to work

If you look into the VPN connection status you may notice that IPv4 Connectivity says Internet and IPv6 Connectivity tells you No Network access. This is because Windows does not setup a default route through the VPN tunnel for IPv6 but depends on the Router on the other end to respond to router queries or send router advertisements. This could be done on the server but noone but Windows does need this and the error could be fixed by just running a single command on the windows box:

route -6 add ::0/0 2a01:4f8:190:2012:3::2

Where the IP on the end is the IP of your tunnel endpoint (look it up in the properties).

So how can we tell Windows to run that command for us... let's say it's a bit hacky what we are about to do now:

  1. Write a small netsh script to run
  2. Find the "VPN connection established"-Event in the event log
  3. Attach a scheduled task to that event type to run the script.

Hacky enough? Ok let's go.

Create a new text-file with the following content:

interface ipv6
add route ::0/0 "VPN Connection" 2a01:4f8:190:2012:3::2
exit

Replace VPN Connection with the name of your connection and the IP address with the IP address you get from the VPN server. Move that text file somewhere where you will not delete it by accident.

Now open the event viewer (Win+R run eventvwr) and navigate to
Event Viewer (Local) -> Applications and Service Logs -> Microsoft -> Windows -> NetworkProfile -> Operational (wow!)

Event viewer path

Now find the event that signifies that the VPN connection was established. It usually has the Event ID 10000 and we need an entry with the State: Connected flag set.

Log entry

Finally we attach a task to that event, which will be called everytime the VPN connects.

Task

My script was called route.nsh and I dropped it into C:\ directly.

Attention: Do not finish the wizard without telling it to open the task properties at the end, we have something to do there!

Set the Run with highes privileges checkbox.

High privilege

... turn off the energy save mode and set the task to run only if the VPN connection is available:

On battery too

Finished! If you now disconnect and then reconnect the VPN IPv6 will work through the tunnel!

Linux

coming soon.

Useful Xcode breakpoints

Here I will document useful breakpoints when you're developing for OSX or iOS with Xcode. This is primarily for me to remember what is useful as I am googling some of these all the time.

It's sad that there are no "breakpoint-templates" that will automatically apply to all Xcode projects that you'll ever create. But enough of the introductory words, here comes the list:

Objective C exception breakpoint

Obviously the most important breakpoint there is, this one has a "template" of some sort as it has its very own menu entry:

Objective C Exception Breakpoint

So far so good, but the annoying thing is that the exception message will not be printed when the breakpoint is reached but only if you continue (and crash your program for real, and of course void your stack backtrace)

So add this to the default:

i386/iOS simulator (32Bit)

Objective C Exception Breakpoint modifications

Just add the following actions:

  • po *(id*)($esp+4)

which will print for example:

-[__NSCFConstantString characterAtIndex:]: Range or index out of bounds
(lldb) 

x86_64/iOS simulator (64Bit)

Use the following actions:

  • po $rdx
  • po $rcx
  • po $r8

iOS device (ARM/ARM64)

Use these:

  • po $r2
  • po $r3
  • po *(id*)($sp)

If all this is to verbose to you look at the prebuild lldb script from here: http://qwan.org/2013/06/18/how-to-snatch-the-error-code-from-the-trap-frame-in-xcode/

Memory errors

Attention: This may be superseeded by the new Address sanitizer in Xcode 7

Sometimes it happens that you find a nasty memory error that is not at all obvious where it came from. Mostly it appears somewhere far away from the original error because the stack was smashed or a buffer overflow bleed into neighboring variables and overwrote something.

To find those errors you'll usually first enable all the memory protection error loggers that are available:

Memory debugging

And now you'll add a symbolic breakpoint at malloc_error_break to catch the offender that is smashing your stack right away (or at least very near to the cause)

Core Graphics Errors

All of Core Graphics error logging will go through the function CGPostError so it is sensible to add a breakpoint at exactly that location. It will break at anything Core Graphics will spit out. You know those 'XXX tried to draw to a nil context` errors may be hard to find if a lot is going on in your application at the same time.

Slow loading views

Perhaps because you used a wrong font-name? Who knows! Set a symbolic breakpoint to CTFontLogSuboptimalRequest to be notified when Core Text does not find the font directly but has to resort to a detailed search (that is very slow, on an iPad 4 it takes about 3 seconds to complete)

Auto layout

This one is obvious when it happens. But it's better to catch it directly instead of waiting for one to happen so for completeness, set a symbolic breakpoint to UIViewAlertForUnsatisfiableConstraints

Improve the debugger

Sometimes lldb can be quite stubborn:

(lldb) p self.window.bounds
error: property 'bounds' not found on object of type 'UIWindow *'
error: 1 errors parsing expression

But this can be fixed:

(lldb) expr @import UIKit
(lldb) p self.window.bounds
(CGRect) $4 = (origin = (x = 0, y = 0), size = (width = 375, height = 667))

Wow, much better, but what has this to do with breakpoints you may ask? Just try the following: Set a new breakpoint in your app delegate that is hit immediately after starting your application, set it to auto-continue and add an action expr @import UIKit.

The next run of your app will stop briefly at that breakpoint, execute that command and continue running. If you hit any other breakpoint (or press pause) the command already has been executed and lldb knows everything about UIKit already! Great!

(You may want to @import Foundation and @import CoreGraphics too)