This article came after a thread on Twitter (opens new window) explaining how to configure tinc (opens new window) on OpenBSD (opens new window) and create a VPN in less than 10 minutes. People seems really interested on this topic, and after a Twitter poll (opens new window), I decided to create an article on all the way I know to create different kind of VPN with OpenBSD.

Firstly, all configure here are executed on OpenBSD-current (opens new window) and/or OpenBSD-6.6 (opens new window) (the last stable release at this time of writing). Secondly, the simulation will be executed on vmm(4) (opens new window), the OpenBSD hypervisor. Finally, I don't have enough knowledge on VPN technologies to give you more information about how it works under the hood. I will give you some references.

So what's the idea? Here a small diagram:


      [fd3e:62ee:99db:bb1a::2/64]
                 [192.168.1.2/24]
         _______      ______     \______
        |       |    (      )    |      |
        | alice |---( bridge )---| bob  |
        |_______|    (______)    |______|
                 \
                  [192.168.1.1/24]
                  [fd3e:62ee:99db:bb1a::1/64]
  _____________                    _____________
               )                  (
  172.16.0.1/24 )================( 172.16.0.2/24
  _____________)                  (______________

Both virtual machines will be connected to a bridge(4) (opens new window). These two machines will also be configured with static addresses on both IP stack (IPv4 and IPv6). No DHCP or auto network configuration, I want to keep the configuration simple.

# Configuring the host

Because we will play with network routing and different other network related subject, a good practice is to make our system able to forward packets. On OpenBSD (and practically all OS), this feature is disable by default, and must be enable manually with sysctl(8) (opens new window).

# network configuration
sysctl net.inet.ip.forwarding=1
sysctl net.inet6.ip6.forwarding=1

If you want to keep your system able to forward even after a reboot, you can append the key/value configuration directly in /etc/sysctl.conf (opens new window).

# enable forwarding after reboot
echo net.inet.ip.forwarding=1 >> /etc/sysctl.conf
echo net.inet6.ip6.forwarding=1 >> /etc/sysctl.conf

Packet Filter (pf(4) (opens new window)) is the de facto firewall on OpenBSD. We will use an anchor (opens new window) instead of create a whole configuration file, this will give us the opportunity to add dynamic configuration and create some context during our tests.

# packet filter configuration
echo "anchor vmd" >> /etc/pf.conf
pfctl -nf /etc/pf.conf && pfctl -f /etc/pf.conf

Now, we can prepare our vmd (opens new window) environment and our new configuration by creating the file located in /etc/vm.conf (opens new window). The different disks and ISO images will be stored in a tree in /home (because this is where I have enough place to store them). After having downloaded install66.iso file based on your local repository configured in /etc/installurl (opens new window), we can enable vmd(8) (opens new window) by using rcctl(8) (opens new window) command.

# vmd configuration
touch /etc/vm.conf
mkdir -p /home/_vmd/disk
mkdir -p /home/_vmd/iso
ftp -o /home/_vmd/iso/install66.iso $(cat /etc/installurl)/6.6/amd64/install66.iso
rcctl enable vmd
rcctl start vmd

Our configuration is almost done, before celebrating, we need to create a bridge(4) (opens new window) interface, this one will be linked to our vmd configuration by creating a new switch called local.

# bridge creation
ifconfig bridge1 create
echo "up" >> /etc/hostname.bridge1
cat >> /etc/vm.conf << EOF
switch "local" {
    interface "bridge1"
}
EOF

We will have 2 virtual machines, alice and bob, with the same configuration, and connected together. The first step is to create the virtual disk with vmctl and append a new configuration to /etc/vm.conf.

# alice vm configuration
vmctl create -s 10G /home/_vmd/disk/alice.qcow2
cat >> /etc/vm.conf << EOF
vm "alice" {
    memory 512M
    disk "/home/_vmd/disk/alice.qcow2"
    cdrom "/home/_vmd/iso/install66.iso"
    interface { switch "local" }
    disable
}
EOF

We can do the same with bob.

# bob vm configuration
vmctl create -s 10G /home/_vmd/disk/bob.qcow2
cat >> /etc/vm.conf << EOF
vm "bob" {
    memory 512M
    disk "/home/_vmd/disk/bob.qcow2"
    cdrom "/home/_vmd/iso/install66.iso"
    interface { switch "local" }
    disable
}
EOF

We have now our 2 virtual machines configuration ready. We can reload vmd and start our VM.

rcctl reload vmd
vmctl start -c alice
vmctl start -c bob

Ready to install OpenBSD? Let's go. I will not present you how to install OpenBSD, too any tutorials (opens new window) on internet and a good documentation is sufficient.

# Post-installation configuration

We need to configure our interface connected to the bridge. By default, vmm(4) (opens new window) uses vio(4) (opens new window) driver, and you should have an interface called vio0 on each virtual machines. Alice VM has two IP addresses, 192.168.1.1/24 and fd3e:62ee:99db:bb1a::1/64.

# on alice
echo "inet 192.168.1.1/24" >> /etc/hostname.vio0
echo "inet6 fd3e:62ee:99db:bb1a::1/64" >> /etc/hostname.vio0
echo "up" >> /etc/hostname.vio0
sh /etc/netstart vio0

Bob has also two IP set, 192.168.1.2/24 and fd3e:62ee:99db:bb1a::2/64.

# on bob
echo "inet 192.168.1.2/24" >> /etc/hostname.vio0
echo "inet6 fd3e:62ee:99db:bb1a::2/64" >> /etc/hostname.vio0
echo "up" >> /etc/hostname.vio0
sh /etc/netstart vio0

To make everything working as expected, we are using netstart(8) (opens new window) script, but we would also have created manually our configuration directly with ifconfig(8) (opens new window). Is our servers are up and running? Is our servers can communicate? Let try that with a simple ping(8) (opens new window) and ping6(8) (opens new window) check.

# on alice
ping -c3 192.168.1.2
ping6 -c3 fd3e:62ee:99db:bb1a::2
# on bob
ping -c3 192.168.1.1
ping6 -c3 fd3e:62ee:99db:bb1a::1

You should have received the pong between the two hosts, if its the case... Our testbed is ready to be used! We have a common network and we can use it to make our tests and create our tunnels and VPN. Before doing some configuration game, a small view of the definition of tunnel (opens new window) from wikipedia:

In computer networks, a tunneling protocol is a communications protocol that allows for the movement of data from one network to another. It involves allowing private network communications to be sent across a public network (such as the Internet) through a process called encapsulation.

Encapsulation (opens new window) is a really important word, you need to understand it before continuing. Here the definition from wikipedia:

In computer networking, encapsulation is a method of designing modular communication protocols in which logically separate functions in the network are abstracted from their underlying structures by inclusion or information hiding within higher level objects.

Now the definition of VPN (Virtual Private Network) (opens new window) from Wikipedia:

A virtual private network (VPN) extends a private network across a public network, and enables users to send and receive data across shared or public networks as if their computing devices were directly connected to the private network. Applications running on a computing device, e.g., a laptop, desktop, smartphone, across a VPN may therefore benefit from the functionality, security, and management of the private network. Encryption is a common, though not an inherent, part of a VPN connection.[1]

The three terms are really close. A tunnel uses encapsulation method to work. A VPN can use an encapsulation method or a defined protocol to work properly. In both case, these technics give you the ability to share network connectivity on different end-point.

# Configuring IP over IP tunnel (ipv4)

The first mecanism we will use is IP over IP tunnel (opens new window). This is probably the less difficult to understand and work pretty well. When I say IP, I include IPv4 and IPv6 protocols.

# on alice
ifconfig gif0 create
ifconfig gif0 tunnel 192.168.1.1 192.168.1.2
ifconfig gif0 alias 172.16.0.1 172.16.0.2

As usual, you can store the configuration in hostname.if(5) (opens new window) file if you want your configure working even after a reboot.

# on alice
# you can eventually store the configuration
echo "tunnel 192.168.1.1 192.168.1.2" >> /etc/hostname.gif0
echo "alias 172.16.0.1 172.16.0.2" >> /etc/hostname.gif0
echo "up" >> /etc/hostname.gif0

We can do the same on Bob

# on bob
ifconfig gif0 create
ifconfig gif0 tunnel 192.168.1.2 192.168.1.1
ifconfig gif0 alias 172.16.0.2 172.16.0.1

# you can eventually store the configuration
echo "tunnel 192.168.1.2 192.168.1.1" >> /etc/hostname.gif0
echo "alias 172.16.0.2 172.16.0.1" >> /etc/hostname.gif0
echo "up" >> /etc/hostname.gif0

So what's going on here? IP over IP is a technique created to encapsulate IP packet over another IP packet. It means that, instead of putting a transport layer protocol like TCP or UDP, we put another network protocol, like IPv4 or IPv6. Can we see that? Yes, we can use tcpdump(8) (opens new window) and try to understand how this tunnel method works.

# on the host
tcpdump -i bridge0

On alice vm, we will just do a ping to bob internal address (172.16.0.2).

# on alice
tcpdump -i vio0
ping -c3 172.16.0.2

tcpdump(8) (opens new window) will output something like the stdout below. Note the (encap), this means your packet is encapsuled.

17:30:46.630247 172.16.0.1 > 172.16.0.2: icmp: echo request (encap)
17:30:46.631530 172.16.0.2 > 172.16.0.1: icmp: echo reply (encap)
17:30:48.610674 172.16.0.1 > 172.16.0.2: icmp: echo request (encap)
17:30:48.611494 172.16.0.2 > 172.16.0.1: icmp: echo reply (encap)

Well, we don't see a lot information about the packets. By default, tcpdump(8) (opens new window) only print some meta-data about the packets. A good way to solve this problem (and to have more verbosity) is to add -XXvvv flags, it will help us to see all the informations contained in each packets.

# on the host
tcpdump -XXvvv -i bridge0

When executed this command will show you something like that:

17:35:01.121296 192.168.1.1 > 192.168.1.2: 172.16.0.1 > 172.16.0.2: 
  icmp: echo request (id:aa08 seq:1) (ttl 255, id 45558, len 84) 
  (ttl 64, id 15809, len 104)
0000: 4500 0068 3dc1 0000 4004 b97d c0a8 0101  E..h=...@..}....
0010: c0a8 0102 4500 0054 b1f6 0000 ff01 b18e  ....E..T........
0020: ac10 0001 ac10 0002 0800 7d4a aa08 0001  ..........}J....
0030: 5896 e585 356b ab31 2549 8f94 dea0 35d7  X...5k.1%I....5.
0040: 2019 d75f 1ddc 60c5 1819 1a1b 1c1d 1e1f   .._..`.........
0050: 2021 2223 2425 2627 2829 2a2b 2c2d 2e2f   !"#$%&'()*+,-./
0060: 3031 3233 3435                           012345
              
17:35:01.122106 192.168.1.2 > 192.168.1.1: 172.16.0.2 > 172.16.0.1: 
  icmp: echo reply (id:aa08 seq:1) (ttl 255, id 1121, len 84) 
  (ttl 64, id 35595, len 104)
0000: 4500 0068 8b0b 0000 4004 6c33 c0a8 0102  E..h....@.l3....
0010: c0a8 0101 4500 0054 0461 0000 ff01 5f24  ....E..T.a...._$
0020: ac10 0002 ac10 0001 0000 854a aa08 0001  ...........J....
0030: 5896 e585 356b ab31 2549 8f94 dea0 35d7  X...5k.1%I....5.
0040: 2019 d75f 1ddc 60c5 1819 1a1b 1c1d 1e1f   .._..`.........
0050: 2021 2223 2425 2627 2829 2a2b 2c2d 2e2f   !"#$%&'()*+,-./
0060: 3031 3233 3435                           012345

If you are not familiar with hexadecimal output, and if you prefer graphical view, you can also use -w ${dump_file_name}.pcap flag to store everything in a pcap file and read it with wireshark (opens new window). Raw hexadecimal packets are a bit ugly, but not so hard to understand. The first line (and the second/third because it was split) is composed of the meta-data (time received/sent, source/destination, misc information about the packet). Next, we have our raw dump. The left column represent the address of the starting bytes. The 8th following column represents the data. Finally, the last column is the ASCII(7) (opens new window) representation of the data. Here an example, we will read the ping's answer from bob.

The packet was read at 17:35:01.122106. It was sent from 192.168.1.2 to 192.168.1.1. It contains an encapsulated packet from 172.16.0.2 to 172.16.0.1. The message is an ICMP (opens new window) echo reply with the ID aa08 and sequence 1. We can omit the different information about the TTL, length or ID.

Next we can read the raw hexadecimal output. To read it properly, we read it from left to right. One hexadecimal number represents 4 bits. 2 hexadecimal numbers represent 8 bits or 1 byte. We have 8 column of 16 bits, so, each line represent 128 bits or 16 bytes. The address is incremented by 1 on every new byte. In this case, the value stored on address 0x0000 is 45 and its ASCII representation is E., the value stored on the address 0x0008 is 40 and its ASCII representation is @., finally, the last value stored on the address 0x000f is 02 with the ASCII representation ...

We are reading an IP packet, its mean that the data stored in this raw hexadecimal output should be defined in the RFC791 (opens new window). We can also find the structure of the packet on wikipedia (opens new window). Everytime we are working with tcpdump(8) you should have a view of the structure of each packets. With experience, you will not require it in the future and you will be able to read binary packets... Scary right?

We have now our IP packet format where each bits/bytes association can represent a specific value and meaning:

  • At the address 0x0000: 4 bits (first) for the version of the protcol with the value 4 followed by 4 bits representing the length with the value 5, represented as E in ASCII;

  • At the address 0x0001: 8 bits for the Type of Service field (now definedas DSCP and ECN in the RFC) with the value 00 and represented as . in ASCII;

  • At the addresses 0x0002 → 0x0003: the total length of the packet coded on 16 bits with the value 0068 and represented as .h in ASCII;

  • At the addresses 0x0004 → 0x0005: 16bits for the identification with the value 8b0b represented as .. in ASCII;

  • At the addresses 0x0006 → 0x0007: 3 bits for the flag with the value 0, 13 bits for the fragment offset with the value 000, both represented as .. in ASCII;

  • At the address 0x0008: 8 bits for the TTL with the value 40 and represented as @ in ASCII;

  • At the address 0x0009: 8 bits for the protocol with the value 04 and represented as . in ASCII;

  • At the addresses 0x000a → 0x000b: 16 bits for the header checksum with the value 6c33 represented as l3 in ASCII;

  • At the addresses 0x000c → 0x000f: 32 bits for the source IP address with the value c0a8 0102 and represented as .... in ASCII. If we convert this hexadecimal address in decimal, it will give you 192.168.1.2;

  • At the addresses 0x0010 → 0x0013: 32 bits for the destination IP address with the value c0a8 0101 and represented as .... in ASCII. If we convert this hexadecimal address in decimal, it will give you 192.168.1.1;

  • This is the end of our IP packet, the next value will represent the above layer, and, in our case, this protocol will start with 4. Yes, it is another IPv4 packet! We can redo the same process from the beginning, and you will find that

  • At the addresses 0x0020 → 0x0023: 32 bits for the source IP address with the value ac10 0002, represented as .... in ASCII. If we convert this hexadecimal address in decimal, it will give us 172.16.0.2;

  • At the addresses 0x0024 → 0x0027: 32 bits for the destination IP address with the value ac10 0001 represented as .... in ASCII. If we convert this hexadecimal address in decimal, we will obtain 172.16.0.1.

What does it mean? It means that we have encapsuled an IP packet over another IP packet. The first packet is for the address of the local network, where the two vm can talk. When the VM got the message, the remaining one is another IP packet. This time, it will look in its routing table and forward to the corresponding address.

Here a classic IPv4 packet with a TCP segment (it could be any other protocols):

  0 ___________ 160 _________ 288
 /_____________/_____________/ |
 |             |             | |
 | IPv4 packet | TCP segment | |
 |_____________|_____________|/

And now an IP over IP packet, another IPv4 packet is concatened after the first IPv4 packet.

  0 ___________ 160 _________ 320 _________ 448
 /_____________/_____________/_____________/ |
 |             |             |             | |
 | IPv4 packet | IPv4 packet | TCP segment | |
 |_____________|_____________|_____________|/

The same configuration can also work with IPv6, the only change here, is that the encapsulation is made over IPv6. In this example, I will remove the tunnel on both alice and bob, give a private IPv6 (opens new window) (defined in RFC4143 (opens new window)) to each nodes and encapsulate an IPv4 packer over an IPv6.

# on alice
ifconfig gif0 -tunnel -inet
ifconfig gif0 tunnel fd3e:62ee:99db:bb1a::1 fd3e:62ee:99db:bb1a::2
ifconfig gif0 alias 172.16.0.1 172.16.0.2
# on bob
ifconfig gif0 -tunnel -inet
ifconfig gif0 tunnel fd3e:62ee:99db:bb1a::2 fd3e:62ee:99db:bb1a::1
ifconfig gif0 alias 172.16.0.2 172.16.0.1

Both hosts are now with the configuration, we can intercept the traffic and interpret it with tcpdump(8).

# on the host
tcpdump -XXvvv -i bridge0

Normally, it will give you something like that:

18:00:17.797228 fd3e:62ee:99db:bb1a::1 > fd3e:62ee:99db:bb1a::2: 
  172.16.0.1 > 172.16.0.2: 
  icmp: echo request (id:a894 seq:2) 
  (ttl 255, id 59928, len 84) [flowlabel 0xe] (len 84, hlim 64)
0000: 6000 000e 0054 0440 fd3e 62ee 99db bb1a  `....T.@.>b.....
0010: 0000 0000 0000 0001 fd3e 62ee 99db bb1a  .........>b.....
0020: 0000 0000 0000 0002 4500 0054 ea18 0000  ........E..T....
0030: ff01 796c ac10 0001 ac10 0002 0800 4d27  ..yl..........M'
0040: a894 0002 5947 f52c 54bb 8b2c d9d7 38ca  ....YG.,T..,..8.
0050: 1a90 1655 1610 2a09 db8a 0238 1819 1a1b  ...U..*....8....
0060: 1c1d 1e1f 2021                           .... !
              
18:00:17.798045 fd3e:62ee:99db:bb1a::2 > fd3e:62ee:99db:bb1a::1: 
  172.16.0.2 > 172.16.0.1: 
  icmp: echo reply (id:a894 seq:2) 
  (ttl 255, id 50294, len 84) [flowlabel 0xc] (len 84, hlim 64)
0000: 6000 000c 0054 0440 fd3e 62ee 99db bb1a  `....T.@.>b.....
0010: 0000 0000 0000 0002 fd3e 62ee 99db bb1a  .........>b.....
0020: 0000 0000 0000 0001 4500 0054 c476 0000  ........E..T.v..
0030: ff01 9f0e ac10 0002 ac10 0001 0000 5527  ..............U'
0040: a894 0002 5947 f52c 54bb 8b2c d9d7 38ca  ....YG.,T..,..8.
0050: 1a90 1655 1610 2a09 db8a 0238 1819 1a1b  ...U..*....8....
0060: 1c1d 1e1f 2021                           .... !

Can we go deeper? Yes! Now, you can configure an IP over IP tunnel, but, maybe you are interested to understand the code source or, at least, read it? I assume you have already cloned the repository from CVS or Github. The file containing the source code is located in sys/net/if_gif.c (opens new window).

# Configuring a GRE tunnel

Generic Routing Encapsulation (opens new window), aka GRE, is another solution for encapsulating different kind of protocol. This one was made by Cisco in 1994 and was defined in RFC1701 (opens new window). GRE supports a variety of useful features (but not the encryption), but, un fortunately, OpenBSD does not support them (from the manual page):

RFC 1701 and RFC 2890 describe a variety of optional GRE header fields in the protocol that are not implemented in the gre and egre interface drivers. The only optional field the driver implement support for is the Key header.

Before playing with gre(4) (opens new window) interface we must enable a kernel flag with sysctl(8) (opens new window) on each virtual machine.

# on both virtual machine
sysctl net.inet.gre.allow=1

# if you want to enable it after a reboot
echo net.inet.gre.allow=1 >> /etc/sysctl.conf

So now, we can create our gre interfaces with ifconfig(8) (opens new window).

# on alice
ifconfig gre0 create up
ifconfig gre0 tunnel 192.168.1.1 192.168.1.2
ifconfig gre0 inet 172.16.0.1 172.16.0.2
# on bob
ifconfig gre0 create up
ifconfig gre0 tunnel 192.168.1.2 192.168.1.1
ifconfig gre0 inet 172.16.0.2 172.16.0.1

Like encapsulating IP packet over another IP packet, GRE tunnel has the same syntax and it is straight forward to create many interfaces on the fly. The configuration require to set the end-point on both side (e.g. 192.168.1.1-2/24) and to configure our internal network (e.g. 172.16.0.1-2) on both side of the tunnel.

# on alice
ping -c3 172.16.0.2
# on bob
ping -c3 172.16.0.1

If you want to see the data flow, you can listen on bridge0 directly from the host.

tcpdump -i bridge0

It should give you something like this output. tcpdump is able to understand the GRE packet and describe the content.

21:46:38.383389 192.168.1.1 > 192.168.1.2: 
  gre 10.0.0.1 > 10.0.0.2: icmp: echo request
21:46:38.384279 192.168.1.2 > 192.168.1.1: 
  gre 10.0.0.2 > 10.0.0.1: icmp: echo reply

The gre(4) man page contains everything you need to understand how GRE tunnel works but also how to configure it.

  • http://bxr.su/OpenBSD/sys/net/if_gre.c

# Configuring MikroTik Ethernet over IP tunnel

Mikrotik Ethernet over IP tunnel is a kind of GRE tunnel designed to be used with Mikrotik equipments but you can also use it with two OpenBSD boxes. Like GRE, this tunnel doesn't support encryption or signature and should be used with care if you have to share confidential information.

# on alice
ifconfig eoip0 create tunnel 192.168.1.1 192.168.1.2
# on bob
ifconfig eoip0 create tunnel 192.168.1.2 192.168.1.1
  • http://bxr.su/OpenBSD/sys/net/if_gre.c#496

# Configuring EtherIP tunnel

To be clear, it is the first time I am using etherip(4) interface, I never had the occasion to use it in real world environment. EtherIP can encapsulate an Ethernet trame over an IP packet.

# on alice
# on bob

# IPSec VPN with with iked (IKEv2)

If it's not the first time you are reading about VPN, you probably have already heard about IPSec. IPSec is probably one of the most complex protocol I have to understand in the past, and, to be honest, it is always a nightmare to work with.

OpenBSD team was probably the first one to include by default kame (opens new window) implementation in OpenBSD27 (opens new window), back in 2000.

iked(8) appeared for the first time in OpenBSD48 (opens new window) in 2010. This daemon was designed to control an IPSec VPN using IKEv2.

Here a small map, it will probably help you to define all terms from the command line and the configuration.

                             [192.168.1.1]                  [172.16.1.2]
     ____      ___________  /   ________      ___________  /   ____
    (    )    |           |/   (        )    |           |/   (    )
   ( net1 )---| responder |---( interco1 )---| initiator |---( net2 )
    (____)   /|___________|    (________)   /|___________|    (____) 
            /                              /
[172.16.1.1]                  [192.168.1.2]

 responder: passive ipsec configuration
 initiator: active ipsec configuration
 interco1: 192.168.1.0/24
 net1: 172.16.1.0/24
 net2: 172.16.1.0/24
  

A small description of each actors:

  • responder has a passive ipsec configuration, waiting for another actor (e.g. initiator) to ask for a session. This actor has two networks, the first one, 192.168.1.1/24 on vio0 can represent the "internet" outgoing connection. On the other side, it has also another network, called here net1, shared with the initiator.

  • initiator has an active ipsec configuration, it will engage the authentication with another node (e.g. responder). It has also two networks with a shared one.

  • interco1 is our bridge, set on the host. We will listen here to see the different connection.

The first step, on each server, you should ensure that different parameters are correctly configured.

sysctl net.inet.esp.enable=1
sysctl net.inet.ah.enable=1
touch /etc/iked.conf
chmod 600 /etc/iked.conf

In this exemple, we will create a vether interface with the shared network. Alice will have 172.16.1.1/24...

# on alice
ifconfig vether0 create alias 172.16.1.1/24 up
echo "alias 172.16.1.1/24 up" >> /etc/hostname.vether0

... and Bob 172.16.1.2/24.

# on bob
ifconfig vether0 create alias 172.16.1.2/24 up
echo "alias 172.16.1.2/24 up" >> /etc/hostname.vether0

# Pre Shared Key (PSK) authentication

In this case, receiver is alice. We can configure iked in passive mode and set the different autorized flow.

# on alice
cat >> /etc/iked.conf << EOF
iked2 passive esp from 192.168.1.0/24 to 192.168.1.0/24 \
    from 172.16.1.0/24 to 172.16.1.0/24 \
    local 192.168.1.1 \
    psk "dont_use_psk_its_a_bad_idea"
EOF
rcctl start iked

initiator will be bob, and we can now configure iked in active mode.

# on bob
iked2 active esp from 192.168.1.0/24 to 192.168.1.0/24 \
    from 172.16.1.0/24 to 172.16.1.0/24 \
    local 192.168.1.2 peer 192.168.1.1 \
    psk "dont_use_psk_its_a_bad_idea"
EOF
rcctl start iked

In both case, you can use ikectl to increase the verbosity, reset or show different information about the daemon. So, what do we have in the logs? If syslog is configured by default, you should see something like the next output in /var/log/daemon on both server.

# on alice
Jan 19 19:43:03 alice iked[98221]: spi=0x747cb0e8540e8f28: 
  recv IKE_SA_INIT req 0 peer 192.168.1.2:500 local 192.168.1.1:500, 510 bytes, policy 'policy1'
Jan 19 19:43:03 alice iked[98221]: spi=0x747cb0e8540e8f28: 
  send IKE_SA_INIT res 0 peer 192.168.1.2:500 local 192.168.1.1:500, 446 bytes
Jan 19 19:43:03 alice iked[98221]: spi=0x747cb0e8540e8f28: 
  recv IKE_AUTH req 1 peer 192.168.1.2:500 local 192.168.1.1:500, 304 bytes, policy 'policy1'
Jan 19 19:43:03 alice iked[98221]: spi=0x747cb0e8540e8f28: 
  send IKE_AUTH res 1 peer 192.168.1.2:500 local 192.168.1.1:500, 256 bytes
Jan 19 19:43:03 alice iked[98221]: spi=0x747cb0e8540e8f28: 
  sa_state: VALID -> ESTABLISHED from 192.168.1.2:500 to 192.168.1.1:500 policy 'policy1'

The connection is initialized by Bob (it's the initiator).

# on bob
Jan 19 19:43:03 bob iked[2955]: spi=0x747cb0e8540e8f28: 
  send IKE_SA_INIT req 0 peer 192.168.1.1:500 local 192.168.1.2:500, 510 bytes
Jan 19 19:43:03 bob iked[2955]: spi=0x747cb0e8540e8f28: 
  recv IKE_SA_INIT res 0 peer 192.168.1.1:500 local 192.168.1.2:500, 446 bytes, policy 'policy1'
Jan 19 19:43:03 bob iked[2955]: spi=0x747cb0e8540e8f28: 
  send IKE_AUTH req 1 peer 192.168.1.1:500 local 192.168.1.2:500, 304 bytes
Jan 19 19:43:03 bob iked[2955]: spi=0x747cb0e8540e8f28: 
  recv IKE_AUTH res 1 peer 192.168.1.1:500 local 192.168.1.2:500, 256 bytes, policy 'policy1'
Jan 19 19:43:03 bob iked[2955]: spi=0x747cb0e8540e8f28: 
  sa_state: VALID -> ESTABLISHED from 192.168.1.1:500 to 192.168.1.2:500 policy 'policy1

IPSec authentication protocol is divided in 6 parts, 3 from the initiator and 3 from the responder. 3 modes exists, main mode, agressive mode and base mode.

  1. initialization phase (IKE_SA_INIT) consists to give a proposal request containing the algorithms, negotiations process and the attributes. The responder, in this case, must select the different supported materials offered by the initiator.

  2. In our case, we are using a preshared key available on both end-point and the second exchange (IKE_AUTH) step gives the key and the nonce

  3. Finally, the last exchange (IKE_AUTH) will share an ID payload and a hash payload. If this step is a success, our VPN is now alive and ready to exchange encrypted packets.

But, what's going on the wire? By using tcpdump, we can see more interesting things.

# on host
tcpdump -i bridge0
20:43:03.800342 192.168.1.2.isakmp > 192.168.1.1.isakmp: isakmp v2.0 exchange IKE_SA_INIT
        cookie: 747cb0e8540e8f28->0000000000000000 msgid: 00000000 len: 510
20:43:03.883732 192.168.1.1.isakmp > 192.168.1.2.isakmp: isakmp v2.0 exchange IKE_SA_INIT
        cookie: 747cb0e8540e8f28->4b19da5dfa972d31 msgid: 00000000 len: 446
20:43:03.918289 192.168.1.2.isakmp > 192.168.1.1.isakmp: isakmp v2.0 exchange IKE_AUTH
        cookie: 747cb0e8540e8f28->4b19da5dfa972d31 msgid: 00000001 len: 304
20:43:03.921735 192.168.1.1.isakmp > 192.168.1.2.isakmp: isakmp v2.0 exchange IKE_AUTH
        cookie: 747cb0e8540e8f28->4b19da5dfa972d31 msgid: 00000001 len: 256
20:48:08.624410 192.168.1.2.isakmp > 192.168.1.1.isakmp: isakmp v2.0 exchange IKE_SA_INIT
        cookie: cdbf9c993440b100->0000000000000000 msgid: 00000000 len: 510
        payload: SA len: 112 [|isakmp] (ttl 64, id 7709, len 538)
  0000: 4500 021a 1e1d 0000 4011 d762 c0a8 0102  E.......@..b....
  0010: c0a8 0101 01f4 01f4 0206 0b72 cdbf 9c99  ...........r....
  0020: 3440 b100 0000 0000 0000 0000 2120 2208  4@..........! ".
  0030: 0000 0000 0000 01fe 2200 0070 0000 006c  ........"..p...l
  0040: 0101 000b 0300 000c 0100 000c 800e 0100  ................
  0050: 0300 000c 0100 000c 800e 00c0 0300 000c  ................
  0060: 0100 000c 800e                           ......

20:48:08.701677 192.168.1.1.isakmp > 192.168.1.2.isakmp: isakmp v2.0 exchange IKE_SA_INIT
        cookie: cdbf9c993440b100->97af7ba5d2574913 msgid: 00000000 len: 446
        payload: SA len: 48 [|isakmp] (ttl 64, id 18426, len 474)
  0000: 4500 01da 47fa 0000 4011 adc5 c0a8 0101  E...G...@.......
  0010: c0a8 0102 01f4 01f4 01c6 e112 cdbf 9c99  ................
  0020: 3440 b100 97af 7ba5 d257 4913 2120 2220  4@....{..WI.! " 
  0030: 0000 0000 0000 01be 2200 0030 0000 002c  ........"..0...,
  0040: 0101 0004 0300 000c 0100 000c 800e 0100  ................
  0050: 0300 0008 0200 0005 0300 0008 0300 000c  ................
  0060: 0000 0008 0400
  
20:48:08.738394 192.168.1.2.isakmp > 192.168.1.1.isakmp: isakmp v2.0 exchange IKE_AUTH
        cookie: cdbf9c993440b100->97af7ba5d2574913 msgid: 00000001 len: 304
        payload: E len: 276 [|isakmp] (ttl 64, id 288, len 332)
  0000: 4500 014c 0120 0000 4011 f52d c0a8 0102  E..L. ..@..-....
  0010: c0a8 0101 01f4 01f4 0138 4afb cdbf 9c99  .........8J.....
  0020: 3440 b100 97af 7ba5 d257 4913 2e20 2308  4@....{..WI.. #.
  0030: 0000 0001 0000 0130 2300 0114 ebe4 b667  .......0#......g
  0040: df43 3278 ca6c c5b5 7cf2 9e27 8948 70f3  .C2x.l..|..'.Hp.
  0050: 9bef 443e 1c23 c291 2404 a611 5bc1 244b  ..D>.#..$...[.$K
  0060: 2e9e 0f3d 5208                           ...=R.

20:48:08.741177 192.168.1.1.isakmp > 192.168.1.2.isakmp: isakmp v2.0 exchange IKE_AUTH
        cookie: cdbf9c993440b100->97af7ba5d2574913 msgid: 00000001 len: 256
        payload: E len: 228 [|isakmp] (ttl 64, id 20439, len 284)
  0000: 4500 011c 4fd7 0000 4011 a6a6 c0a8 0101  E...O...@.......
  0010: c0a8 0102 01f4 01f4 0108 0fd8 cdbf 9c99  ................
  0020: 3440 b100 97af 7ba5 d257 4913 2e20 2320  4@....{..WI.. # 
  0030: 0000 0001 0000 0100 2400 00e4 6c1b 010a  ........$...l...
  0040: bc80 6c59 1662 ea76 2e6a 565b 2248 ec0f  ..lY.b.v.jV["H..
  0050: a1b8 058f 6e25 cdf9 2745 6d09 55b8 1c87  ....n%..'Em.U...
  0060: dcfa 9b1c db1c                           ......

What's going on when we ping Bob network from Alice?

# on alice
# ping -c1 172.16.1.2
PING 172.16.1.2 (172.16.1.2): 56 data bytes
64 bytes from 172.16.1.2: icmp_seq=0 ttl=255 time=5.649 ms

--- 172.16.1.2 ping statistics ---
1 packets transmitted, 1 packets received, 0.0% packet loss
round-trip min/avg/max/std-dev = 5.649/5.649/5.649/0.000 ms

Here the output of tcpdump from the host point of view.

20:48:08.738394 192.168.1.2.isakmp > 192.168.1.1.isakmp: isakmp v2.0 exchange IKE_AUTH
        cookie: cdbf9c993440b100->97af7ba5d2574913 msgid: 00000001 len: 304
        payload: E len: 276 [|isakmp] (ttl 64, id 288, len 332)
  0000: 4500 014c 0120 0000 4011 f52d c0a8 0102  E..L. ..@..-....
  0010: c0a8 0101 01f4 01f4 0138 4afb cdbf 9c99  .........8J.....
  0020: 3440 b100 97af 7ba5 d257 4913 2e20 2308  4@....{..WI.. #.
  0030: 0000 0001 0000 0130 2300 0114 ebe4 b667  .......0#......g
  0040: df43 3278 ca6c c5b5 7cf2 9e27 8948 70f3  .C2x.l..|..'.Hp.
  0050: 9bef 443e 1c23 c291 2404 a611 5bc1 244b  ..D>.#..$...[.$K
  0060: 2e9e 0f3d 5208                           ...=R.

20:48:08.741177 192.168.1.1.isakmp > 192.168.1.2.isakmp: isakmp v2.0 exchange IKE_AUTH
        cookie: cdbf9c993440b100->97af7ba5d2574913 msgid: 00000001 len: 256
        payload: E len: 228 [|isakmp] (ttl 64, id 20439, len 284)
  0000: 4500 011c 4fd7 0000 4011 a6a6 c0a8 0101  E...O...@.......
  0010: c0a8 0102 01f4 01f4 0108 0fd8 cdbf 9c99  ................
  0020: 3440 b100 97af 7ba5 d257 4913 2e20 2320  4@....{..WI.. # 
  0030: 0000 0001 0000 0100 2400 00e4 6c1b 010a  ........$...l...
  0040: bc80 6c59 1662 ea76 2e6a 565b 2248 ec0f  ..lY.b.v.jV["H..
  0050: a1b8 058f 6e25 cdf9 2745 6d09 55b8 1c87  ....n%..'Em.U...
  0060: dcfa 9b1c db1c                           ......

Well, it seems that our VPN is working and we don't know the content of the different packets and what we are doing between the two hosts. The first packet is the ping from , and the second is the pong from Bob.

What about IPv6 now? Can we encapsulate our IPv6 packet in an IPSec VPN? Let try it.

# on alice
ifconfig vether0 inet6 alias fdd0:48b3:1360:ab79::1/64

# patch with the private IPv6 address
patch /etc/iked.conf - <<EOF
--- iked.conf.old       Sun Jan 19 20:54:32 2020
+++ iked.conf   Sun Jan 19 21:02:19 2020
@@ -1,4 +1,5 @@
 ikev2 passive esp from 192.168.1.0/24 to 192.168.1.0/24 \
+       from fdd0:48b3:1360:ab79::/64 to fdd0:48b3:1360:ab79::/64 \
        from 172.16.1.0/24 to 172.16.1.0/24 \
        local 192.168.1.1 psk "dont_use_psk_its_a_bad_idea"
EOF

# reload our iked service
rcctl restart iked
# on bob
ifconfig vether0 inet6 alias fdd0:48b3:1360:ab79::2/64

# patch with the private IPv6 address
patch /etc/iked.conf - <<EOF
--- iked.conf.old       Sun Jan 19 20:54:32 2020
+++ iked.conf   Sun Jan 19 21:02:19 2020
@@ -1,4 +1,5 @@
 ikev2 passive esp from 192.168.1.0/24 to 192.168.1.0/24 \
+       from fdd0:48b3:1360:ab79::/64 to fdd0:48b3:1360:ab79::/64 \
        from 172.16.1.0/24 to 172.16.1.0/24 \
        local 192.168.1.1 psk "dont_use_psk_its_a_bad_idea"
EOF

# restart out iked service
rcctl restart iked
# on alice
# ping6 -c1 fdd0:48b3:1360:ab79::2
PING fdd0:48b3:1360:ab79::2 (fdd0:48b3:1360:ab79::2): 56 data bytes
64 bytes from fdd0:48b3:1360:ab79::2: icmp_seq=0 hlim=64 time=2.845 ms

--- fdd0:48b3:1360:ab79::2 ping statistics ---
1 packets transmitted, 1 packets received, 0.0% packet loss
round-trip min/avg/max/std-dev = 2.845/2.845/2.845/0.000 ms

It seems we can reach Bob IPv6 network via IPSec! Can we try another protocol than ICMP to ensure everything is working as expected?

# on alice
# uname -a | nc -l6 8888
OpenBSD bob.my.domain 6.6 GENERIC#353 amd64
# on bob 
# uname -a | nc -6 fdd0:48b3:1360:ab79::1 8888
OpenBSD alice.my.domain 6.6 GENERIC#353 amd64

We got the alice uname command output on bob prompt and the bob's one on alice. Our VPN is working also on IPv6!

# Public Key authentication

IPSec supports different public key authentication algorithms like classical RSA but also some Elliptic Curves like ecdsa256, ecdsa384 et ecdsa512. Pre-Shared Key authentication is not recommended, but is easy to set quickly to test configuration. By default, at boot time, OpenBSD will generate different keys, the ones for sshd but also for isakmpd and iked. You cat see how it works by reading /etc/rc boot script with the make_keys function.

# Generate keys for isakmpd, iked and sshd if they don't exist yet.
make_keys() {
        local _isakmpd_key=/etc/isakmpd/private/local.key
        local _isakmpd_pub=/etc/isakmpd/local.pub
        local _iked_key=/etc/iked/private/local.key
        local _iked_pub=/etc/iked/local.pub

        if [[ ! -f $_isakmpd_key ]]; then
                echo -n "openssl: generating isakmpd/iked RSA keys... "
                if openssl genrsa -out $_isakmpd_key 2048 >/dev/null 2>&1 &&
                        chmod 600 $_isakmpd_key &&
                        openssl rsa -out $_isakmpd_pub -in $_isakmpd_key \
                            -pubout >/dev/null 2>&1; then
                        echo done.
                else
                        echo failed.
                fi
        fi

        if [[ ! -f $_iked_key ]]; then
                # Just copy the generated isakmpd key
                cp $_isakmpd_key $_iked_key
                chmod 600 $_iked_key
                cp $_isakmpd_pub $_iked_pub
        fi

        ssh-keygen -A

        if [[ ! -f /etc/soii.key ]]; then
                openssl rand -hex 16 > /etc/soii.key &&
                    chmod 600 /etc/soii.key && sysctl -q \
                    "net.inet6.ip6.soiikey=$(</etc/soii.key)"
        fi
}

iked key are a copy of the isakmpd key, generated with openssl with the following command:

openssl genrsa -out $_isakmpd_key 2048
chmod 600 $_isakmpd_key
openssl rsa -out $_isakmpd_pub -in $_isakmpd_key -pubout

where $_isakmpd_key is the path of the key (/etc/isakmpd/private/local.key) and $_isakmpd_pub is the path of the public key (/etc/isakmpd/local.pub). These two files are respectively copied in /etc/iked/private/local.key and /etc/iked/local.pub.

# on alice
scp /etc/iked/local.pub bob:/etc/iked/pubkeys/ipv4/192.168.1.1
# on bob
scp /etc/ked.local.pub alice:/etc/iked/pubkeys/ipv4/192.168.1.2

Everything seems fine, we can now restart our services on both host and wait for the authentication.

# on alice
rcctl restart iked
tail -f /var/log/daemon
rcctl restart iked
tail -f /var/log/daemon

On the host point of view, we can see the authentication mecanism.

08:55:18.461595 192.168.1.2.isakmp > 192.168.1.1.isakmp: isakmp v2.0 exchange IKE_SA_INIT
        cookie: 6ed37a4129045518->0000000000000000 msgid: 00000000 len: 510
08:55:18.534344 192.168.1.1.isakmp > 192.168.1.2.isakmp: isakmp v2.0 exchange IKE_SA_INIT
        cookie: 6ed37a4129045518->8f9072710175e4fd msgid: 00000000 len: 451
08:55:18.589649 192.168.1.2.isakmp > 192.168.1.1.isakmp: isakmp v2.0 exchange IKE_AUTH
        cookie: 6ed37a4129045518->8f9072710175e4fd msgid: 00000001 len: 896
08:55:18.607037 192.168.1.1.isakmp > 192.168.1.2.isakmp: isakmp v2.0 exchange IKE_AUTH
        cookie: 6ed37a4129045518->8f9072710175e4fd msgid: 00000001 len: 848

We can try to ping the different network on both side with ping and... it works as expected. We got our pong.

08:55:49.517151 192.168.1.2 > 192.168.1.1: esp spi 0x927eb88c seq 2 len 136
08:55:49.518586 192.168.1.1 > 192.168.1.2: esp spi 0x2c000edc seq 2 len 136

Well, the authentication seems more lighter than the one with the pre-shared key approach. What's going on here? First thing, we will take a look on a more complete network dump.

09:04:42.389510 192.168.1.2.isakmp > 192.168.1.1.isakmp: isakmp v2.0 exchange IKE_SA_INIT
        cookie: 5d03638185ed1357->0000000000000000 msgid: 00000000 len: 510
        payload: SA len: 112 [|isakmp] (ttl 64, id 47717, len 538)
  0000: 4500 021a ba65 0000 4011 3b1a c0a8 0102  E....e..@.;.....
  0010: c0a8 0101 01f4 01f4 0206 7517 5d03 6381  ..........u.].c.
  0020: 85ed 1357 0000 0000 0000 0000 2120 2208  ...W........! ".
  0030: 0000 0000 0000 01fe 2200 0070 0000 006c  ........"..p...l
  0040: 0101 000b 0300 000c 0100 000c 800e 0100  ................
  0050: 0300 000c 0100 000c 800e 00c0 0300 000c  ................
  0060: 0100 000c 800e                           ......

09:04:42.466845 192.168.1.1.isakmp > 192.168.1.2.isakmp: isakmp v2.0 exchange IKE_SA_INIT
        cookie: 5d03638185ed1357->8f61e186050d5d4c msgid: 00000000 len: 451
        payload: SA len: 48 [|isakmp] (ttl 64, id 16299, len 479)
  0000: 4500 01df 3fab 0000 4011 b60f c0a8 0101  E...?...@.......
  0010: c0a8 0102 01f4 01f4 01cb 5370 5d03 6381  ..........Sp].c.
  0020: 85ed 1357 8f61 e186 050d 5d4c 2120 2220  ...W.a....]L! " 
  0030: 0000 0000 0000 01c3 2200 0030 0000 002c  ........"..0...,
  0040: 0101 0004 0300 000c 0100 000c 800e 0100  ................
  0050: 0300 0008 0200 0005 0300 0008 0300 000c  ................
  0060: 0000 0008 0400                           ......

09:04:42.534405 192.168.1.2.isakmp > 192.168.1.1.isakmp: isakmp v2.0 exchange IKE_AUTH
        cookie: 5d03638185ed1357->8f61e186050d5d4c msgid: 00000001 len: 896
        payload: E len: 868 [|isakmp] (ttl 64, id 16746, len 924)
  0000: 4500 039c 416a 0000 4011 b293 c0a8 0102  E...Aj..@.......
  0010: c0a8 0101 01f4 01f4 0388 1cae 5d03 6381  ............].c.
  0020: 85ed 1357 8f61 e186 050d 5d4c 2e20 2308  ...W.a....]L. #.
  0030: 0000 0001 0000 0380 2300 0364 c8c9 5b11  ........#..d..[.
  0040: ea09 65bb 6cdc e6c0 d208 11b1 9537 94b0  ..e.l........7..
  0050: 2a10 2675 ccf3 65e0 d0da 689e 3909 7fb4  *.&u..e...h.9...
  0060: efa4 56c1 d2c7                           ..V...

09:04:42.551935 192.168.1.1.isakmp > 192.168.1.2.isakmp: isakmp v2.0 exchange IKE_AUTH
        cookie: 5d03638185ed1357->8f61e186050d5d4c msgid: 00000001 len: 848
        payload: E len: 820 [|isakmp] (ttl 64, id 17224, len 876)
  0000: 4500 036c 4348 0000 4011 b0e5 c0a8 0101  E..lCH..@.......
  0010: c0a8 0102 01f4 01f4 0358 9c63 5d03 6381  .........X.c].c.
  0020: 85ed 1357 8f61 e186 050d 5d4c 2e20 2320  ...W.a....]L. # 
  0030: 0000 0001 0000 0350 2400 0334 7662 e39e  .......P$..4vb..
  0040: 9a20 9407 eb13 187a 9495 c619 fee3 15af  . .....z........
  0050: 7618 008d c925 79e6 3133 aa68 2f9c 5711  v....%y.13.h/.W.
  0060: f92b f67c 215d                           .+.|!]

The big difference here, is, I guess, because we are using IKEv2. With IKEv1, even with public key authentication, we have 6 messages between the two nodes. In our case, we have 4 exchanges with both nodes. IKEv1 is based on RFC4995 (opens new window) and IKEv2 is based on RFC5996 (opens new window)/RFC7296 (opens new window).

# NAT traversal configuration

Unfortunately, many end users have a NAT on their local network and are sometime locked with this configuration and IPSec.

# OpenBSD VPN with isakmpd (IKEv1)

Now, we have the knowledge to create a modern VPN with IPSec and IKEv2, but OpenBSD supports also the old method, IKEv1, still used by many manufacturers. IKEv1 is controlled with isakmpd(8) daemon and is configured by /etc/ipsec.conf file. You can also configure this service by create /etc/isakmpd/isakmpd.conf file.

# on alice
# on bob

# Pre-Shared Key

# Public Key Authentication

# Configuring a VPN with OpenSSH

I think many people who are reading this article are aware of the power of OpenSSH and all the embedded feature like Local (-L (opens new window)), Remote (-R (opens new window)) forward or Dynamic forward (-D (opens new window)) but there is more! You can also use -w (opens new window) parameter to configure a tun interface. Even more, the official documentation give you how to configure a VPN based on SSH (opens new window). Crazy right?

First thing first, you need to allow sshd on the host where the tunnel will be configured. You can do it on both servers but in our example, only Bob will have the right to open a tunnel.

# on alice
# create a tunnel interface
ifconfig tun0 create 172.16.2.1 172.16.2.2 netmask 255.255.255.252

# start an ssh client in background to bob with tunnel support 
ssh -w 0:0 192.168.1.1 -l ${user} -N -f
# on bob
# patch sshd configuration
patch /etc/ssh/sshd_config << EOF
@@ -75,7 +75,7 @@
 #UseDNS no
 #PidFile /var/run/sshd.pid
 #MaxStartups 10:30:100
-# PermitTunnel no
+PermitTunnel yes
 #ChrootDirectory none
 #VersionAddendum none
EOF

# check configuration file and restart sshd if all is okay
sshd -t && rcctl restart sshd

# create the tunnel interface
ifconfig tun0 create 172.16.2.2 172.16.2.1 netmask 255.255.255.252

On both server, the tunnel interface should be up and running.

# on alice
# ifconfig tun0
tun0: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 1500
        index 9 priority 0 llprio 3
        groups: tun
        status: active
        inet 172.16.2.1 --> 172.16.2.2 netmask 0xfffffffc
# on bob
# ifconfig tun0
tun0: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 1500
        index 8 priority 0 llprio 3
        groups: tun
        status: active
        inet 172.16.2.2 --> 172.16.2.1 netmask 0xfffffffc

Our 172.16.2.0/30 network is our interconnection network and we can now add the routes.

# on alice
route add ${route} 172.16.2.2
# on bob
route add ${route} 172.16.2.1

# Configuring a VPN with Tinc

Here our first VPN solution from the packages! Tinc (opens new window) is a meshed VPN, pretty easy to use, stable and secure. This VPN use TCP/655 and UDP/655 by default. Because it is meshed, tincd(8) (opens new window) (the daemon) is both a client and a server.

Why tinc? Tinc is a really efficient, portable and easy to install VPN solution. You can use it on practically all Unix/Linux systems without issues. Tinc was designed to be fully meshed (in other words, all nodes are connected together).

pkg_add tinc

or by using the ports by compiling it yourself

cd /usr/ports/net/tinc && make && make install

Well, you should now have different all you need to configure tinc on your server.

# on alice
# on bob

# Configuring a VPN with OpenVPN

pkg_add openvpn

or by installing it from the ports:

cd /usr/ports/net/openbsd && make && make install

# OpenBSD stack with others one

# Linux

  • https://strongswan.org/
  • https://www.openswan.org/

# FreeBSD

  • https://www.freebsd.org/cgi/man.cgi?query=if_ipsec
  • https://www.freebsd.org/cgi/man.cgi?query=ipsec

# NetBSD

  • https://netbsd.gw.com/cgi-bin/man-cgi?racoon
  • https://netbsd.gw.com/cgi-bin/man-cgi?ipsec+4
  • https://netbsd.gw.com/cgi-bin/man-cgi?ipsecif

# Microsoft

  • https://docs.microsoft.com/en-us/windows/win32/fwp/ipsec-configuration
  • https://docs.microsoft.com/en-us/powershell/module/netsecurity/get-netipsecrule?view=win10-ps
  • https://docs.microsoft.com/en-us/powershell/module/netsecurity/set-netipsecrule?view=win10-ps

# FAQ

Many people asked questions about my article and over the time as system administrators. So, here a small FAQ.

# I have an issue with tap device and vmd

config_setvm: can't open tap tap: No such file or directory

This issue means you already have used all tap interfaces on your system. Fortunately, you can create new ones if needed. OpenBSD doesn't have a dynamic special file creation like on Linux or FreeBSD, and you will need to use MAKEDEV(8) (opens new window) script to generate them. Here a snippet to create 10 new tap interfaces:

cd /dev
jot 10 1 | xargs -I%i args sh MAKEDEV tap%i

# I want to use a switch(4) interface instead of a bridge

Since OpenBSD-6.1, a new interface called switch(4) (opens new window) was introduced. You can create a new interface by using ifconfig(8) (opens new window):

ifconfig switch0 create

You will also need to configure switchd (opens new window) by editing/creating /etc/switchd.conf (opens new window) file. Don't forget to read switchctl(8) (opens new window) man page.

# Resources

  • https://en.wikipedia.org/wiki/IPv4

  • https://en.wikipedia.org/wiki/IPv6

  • https://en.wikipedia.org/wiki/Transmission_Control_Protocol

  • https://man.openbsd.org/gif

  • https://man.openbsd.org/gre.4

  • https://man.openbsd.org/ifconfig.8

  • https://man.openbsd.org/hostname.if.5

  • https://man.openbsd.org/inet6.4

  • https://man.openbsd.org/inet.4

  • https://man.openbsd.org/eoip.4

  • https://man.openbsd.org/etherip.4

  • https://man.openbsd.org/bridge.4

  • https://fr.wikipedia.org/wiki/IP_over_Avian_Carriers

# IPSec

  • https://www.openbsd.org/faq/faq17.html
  • https://man.openbsd.org/iked
  • https://man.openbsd.org/ikectl
  • https://man.openbsd.org/ipsec
  • https://tools.ietf.org/html/rfc4301
  • https://fr.wikipedia.org/wiki/IPsec
  • https://tools.ietf.org/html/rfc6071
  • https://jc2-2017.inria.fr/files/2017/05/jean.pdf
  • https://eprint.iacr.org/2007/125.pdf
  • https://eprint.iacr.org/2006/370.pdf
  • http://bxr.su/OpenBSD/sbin/iked/
  • http://bxr.su/OpenBSD/usr.sbin/ikectl/
  • http://bxr.su/OpenBSD/sbin/ipsecctl/
  • http://bxr.su/OpenBSD/sys/netinet/ipsec_input.c
  • http://bxr.su/OpenBSD/sys/netinet/ipsec_output.c