A Simple OpenIKEd site-to-site VPN
How to setup a simple site-to-site OpenIKEd based VPN on OpenBSD
Some time ago I needed to establish a simple and reasonably secure VPN between two of my networks. The built-in IKEv1 and ISAKMP system on OpenBSD felt dated, while OpenVPN seemed very complicated and didn’t take advantage of many of the features of OpenBSD, so I decided instead to use OpenIKEd.
iked(8), is a straightforward VPN daemon which only supports IKEv2. It was written by Reyk Floeter to be a “lean, clean, secure, better configurable and [more] interoperable” alternative to other similar daemons. While this claim is in many ways true, I unfortunately found OpenIKEd to be a bit of a pain to set up. I eventually discovered this was largely due to my firewall configuration, but I was also unable to find any good examples of the specific task I was attempting to carry out. For the benefit of future users, this article details the setup process for a simple site-to-site VPN using OpenIKEd with pre-shared keys on OpenBSD 5.8/5.9.
Using pre-shared keys isn’t the safest way to do this and doesn’t take advantage of OpenIKEd’s support for public key authentication. To improve safety, this template uses
pf(4) to restrict connections to only the desired remote hosts and also uses a random 64 character case-sensitive alphanumeric strong password. I recommend making sure the permissions on
/etc/iked.conf are set to
In this example, the two networks are located at
126.96.36.199 each with internal networks of
First we need to enable an interface for the VPN. We do this by making a
hostname.if(5) file which is located in
/etc. For encrypted interfaces we use the name
enc followed by a number; because this is the first encrypted interface, we use the name
enc0. In other words, we create the file
/etc/hostname.enc0 and fill it with the word ‘up’. You can do this with your favourite text editor, or simply:
OpenIKEd requires some changes to the kernel state. These changes need to be set in
sysctl.conf in order to keep the setting persistent across boots. A sane yet thorough configuration is as follows:
I would note that this procedure involves disabling the authentication headers as they are not required.
To set these states you can reboot or, to avoid rebooting, run each of the entries through the
sysctl(8) command. To use the
sysctl command you must enter each key/value pair into the shell as root in a manner similar to the following:
This will set only ‘net.inet.ip.forwarding’ to 1; to update all the states you will have to do this for all key/value pairs. Afterwards you can verify that the values have actually been set by running:
As a VPN by definition needs to do networking, you will have to modify the firewall to allow packets to enter and leave. Due to the wide variety of firewall configurations this example setup is based on a ‘block all’ approach and only covers the broad elements. It is very easy to make a mistake here so make sure the traffic destined for these rules isn’t short circuited before by other existing rules in your setup.
To simplify your configuration you will need to establish a table containing the WAN addresses of your peers. This will allow for the addition of other VPN’s simply by adding them to the table. This can be done as follows:
Next we allow the establishment of the actual connection. We are using Encapsulating Security Payload (ESP) which uses the ESP protocol along with UDP on port 500 and 4500. We want each peer to be able to talk with the other using the appropriate protocol and ports on each of their
egress interfaces. This can be done as follows:
Lastly we need to allow the traffic that flows through the tunnel to reach its appropriate destination. There are two ways of doing this. The simplest is to just allow all traffic on the
enc0 interface. This can be achieved by adding the following line near ‘
set skip on lo0’ which is present in most
Alternatively, for finer control you can add a more complicated set of rules which explicitly manages the flow of traffic between the networks. This can be done by creating additional macros, tables, and rules such as:
This will pass almost all traffic, though the tables and rules can be easily modified and extended to restrict sources and destinations of traffic. In addition, outbound rules need to be created to allow this traffic to actually travel out an interface to the local network. This is due to the
(if-bound) component of the rules. In this template, traffic which has come across the VPN will be allowed out any interface which has been placed in the
'trust' group. (to add an interface to the
'trust' group you must add the line “
group trust” to that interfaces
Now we need to actually complete the configuration of
iked, which is done in the file
iked.conf can be thought of as having two parts; macros and connections. We place the macros at the top of the file and the connections at the bottom.
This connection is an IKEv2 VPN using ESP, tunnelling traffic between each:
- gateway and its remote network
using the pre-shared key:
Phase 1 uses HMAC SHA512 for authentication and as its pseudo-random function, AES256 for encryption, and the non-standard Curve25519 for key exchange. Phase 2 also uses HMAC SHA512 for authentication and Curve25519 for key exchange, but uses the superior AES256 GCM for encryption. Lastly all traffic will be tagged “VPN” and traverse the
To check that the VPN is established, use
ipsecctl(8). To list the current flows and security associations, run
If everything is working correctly you should get a response similar to:
- OpenBSD 6.4, There is an issue with the use of
ipcompwhich can cause the resultant VPN to be unstable. Not using ip compression fixes this. I have removed its use in this post.
Any source in this article is released under the ISC License.