Your own VPN with OpenIKED & OpenBSD

Calum MacRae
Jun 5, 2018 · 9 min read

Well, it’s been quite some time since I blogged! I figure I’ll jump back in with a short ‘n sweet post.

Remote connectivity to your home network is something I think a lot of people find desirable. Over the years, I’ve just established an SSH tunnel and use it as a SOCKS proxy, sending my traffic through that. It’s a nice solution for a “poor man’s VPN”, but it can be a bit clunky, and it’s not great having to expose SSH to the world, even if you make sure to lock everything down 🤔

I set out the other day to finally do it properly. I’d come across this great post by Gordon Turner:

Whilst it was exactly what I was looking for, it outlined how to set up an L2TP VPN. Really, I wanted IKEv2 for performance and security reasons (I won’t elaborate on this here, if you’re curious about the differences, there’s a lot of content out on the web explaining this).

The client systems I’d be using have native support for IKEv2 (iOS, macOS, other BSD systems). But, I couldn’t find any tutorials in the same vein.

So, let’s get stuck in!


A quick note ✍️

This guide will walk through the set up of an IKEv2 VPN using OpenIKED on OpenBSD. It will detail a “road warrior” configuration, and use a PSK (pre-shared-key) for authentication. I’m sure it can be easily adapted to work on any other platforms that OpenIKED is available on, but keep in mind my steps are specifically for OpenBSD.

Server Configuration

As with all my home infrastructure, I crafted this set-up declaratively. So, I had the deployment of the VM setup in Terraform (deployed on my private Triton cluster), and wrote the configuration in Ansible, then tied them together using radekg/terraform-provisioner-ansible.

One of the reasons I love Ansible is that its syntax is very simplistic, yet expressive. As such, I feel it fits very well into explaining these steps with snippets of the playbook I wrote.

I’ll link the full playbook a bit further down for those interested.

First off, we need to alter the kernel state so it’s fit to manage VPN traffic. Naturally, the parameters we’re going to be setting are in the net.inet namespace:

Hopefully this is clear enough. For those unfamiliar with Ansible, I’ll show what it looks like in the shell:

These should also be persisted to /etc/sysctl.conf :

Note: this is already handled by the above Ansible task

If you’d like to maintain a list of those out there trying to constantly hit SSH on your network which you can use to outright block them, follow this step. We’ll use this in our pf configuration a bit further down.

You can use whatever subnet configuration you desire, here. But, if you choose to do something outside of 10.0.1.0/24 (used in these examples); you’ll need to make sure you substitute it elsewhere throughout the post.

Note: the notify expression here is just running $ sh /etc/netstart

If you didn’t bother with the “naughty list” step earlier, leave out any lines containing “bad” or “badguys”.

Also, you’ll want to make sure you set the intra macro to the interface your system uses for network connectivity (presumably to the internet). Mine is vio0 , but yours may be something different. You can find this out using good old ifconfig.

Note: the notify expression here is just running $ pfctl -f /etc/pf.conf

For this step, you’ll need to have generated a secure pre-shared-key. Represented here in the contents of /etc/iked.conf as {{iked_psk}} (Ansible variable syntax).

This is the secret you’ll be using on your clients to connect. Make sure the string itself is secure and stored securely!

Personally; I’ve used Ansible Vault to store an encrypted variable in my Ansible configuration for deployment, and use pass to carry it around with me on my client devices.

Note: the notify expression here is just running $ rcctl reload iked

You may notice the name-server configuration here. This is the address of the DNS server you want clients to query when connected to the VPN. Yours is very likely a different address, so make sure you set this appropriately!

For more information on iked configuration, see: iked.conf(5)

In the shell: $ rcctl start iked && rcctl enable iked

For those of you that would like to deploy using Ansible: here’s the playbook I wrote. You’ll want to set your own PSK in the iked_psk variable. This could be plain text if you like, but I wouldn’t advise it. If you’d like to store encrypted variable like I have, you can encrypt your PSK using ansible-vault encrypt.


Gateway configuration 🚪

I’m only going to touch on this very briefly, as guidance on this is outside of the scope of this tutorial and will largely depend on your network configuration.

Simply put, you’ll need the following to get your shiny new VPN server on the front-line:

  1. Traffic targeted at your network destined for UDP ports 500, 4500, and 1701 needs to be able to reach your VPN server. This means you’ll need to permit UDP traffic on these ports through any firewalls you have, and you will likely need to forward the ports to the server.
  2. Depending on where you’re VPN’ing in from, you’ll likely need a public IP address (or DNS record pointing to such) to reach your network. Most residential ISPs where I’m from will either offer the option for a static IP address (usually at a small extra cost) or, although they may state its “dynamic” (DHCP) — it’ll likely be “sticky”. That is to say; the address leased by their DHCP server will have a very, very long TTL, and as such will stay the same for a very long time, if not indefinitely. It really depends on your ISP!

Client configuration

With this configuration in place, you can configure your client(s) to authenticate using your PSK. You can of course configure it differently, depending on your needs and the functionality available with OpenIKED, if you so desire.

If you’re using a device running a recent version of iOS, it’s got IKEv2 support natively, and is very easy to configure:

  1. Open Settings then navigate to General > VPN
  2. Select Add VPN Configuration…
  3. Type should be IKEv2
  4. Description can be whatever you want
  5. Server should be the address (DNS or IP) to reach your server
  6. Remote ID should be the hostname of your server, unless you configured it to be something else. See the note at the bottom of the srcid parameter documentation in iked.conf(5) regarding its omission. If my server’s hostname (set in /etc/myname on OpenBSD) was vpn.my.net; this is what I’d use as Remote ID
  7. Local ID can be left blank
  8. Unless configured otherwise, set User Authentication to None then select < Back at the top
  9. Toggle off Use Certificate
  10. This should show the Secret field. Here’s where you put your PSK

It should look roughly something like this:

You should now have a VPN menu item in your main Settings screen, just under Personal Hotspot. You can toggle your VPN from here. Fingers crossed it works! 🤞

macOS 💻

The configuration is largely the same, so I won’t repeat the steps. To add a VPN configuration on macOS open Settings > Network. Use the little ‘+’ symbol in the bottom left of the network list to add a new network and select VPN. The VPN Type should default to IKEv2, if not — make sure you set it as that. Give it a name, and follow the same steps as iOS for the configuration. You should be all set!


Troubleshooting 🙄

If you run into problems connecting once you’ve got everything configured, there are a few things you can check over.

Firstly, make sure the iked service is actually up and running. You can check this with $ rcctl check iked

One of the first things to always check for with connectivity problems to a service you control is to make sure the network traffic can actually reach its destination. Personally, I’d follow something along these lines:

  1. Stop the iked service
  2. Now that the service is stopped, you can start a netcat process listening on the relevant ports. Check each one as you go. Let’s start with UDP port 500: $ nc -u -l 500 👈 this should start a netcat process listening to UDP port 500, bound to the address 0.0.0.0 (all interfaces).
  3. On another system, outside your network (presumably with internet connectivity), check to see if you can reach that port and see if your traffic is reaching the server: $ nc -vuz your.server.addr 500
  4. Due to the nature of UDP, netcat will always report it was successful on the client machine. But, if your netcat process on your server has spat an X or a few X’s into stdout, then traffic is reaching your server. If you don’t see anything, traffic is not making it through to your server for some reason.

If you’ve confirmed that traffic can make it through to your server, the next step would be to start inspecting iked’s behaviour. With the iked service still stopped, start it in the foreground with the -d flag, and some -v flags for verbosity (the more v’s the more verbosity)

You can use trusty tcpdump to inspect the traffic of an interface. In our case, both enc0 and pflog0 are of interest:

$ tcpdump -ni enc0 or $ tcpdump -ni pflog0

The first will watch your enc0 interface which iked should be using. The second will show you realtime logs from pf .

Once you’re done with debugging, make sure you turn any services back on!


Happy Hacking!

I’m really happy with this setup, having easy, secure access to my home network wherever I am is really nice. Hopefully you’ve enjoyed reading and have your own OpenIKED server running, or at least found the insights useful!