The ikectl(8) utility also allows you to maintain a simple X.509 certificate authority (CA) for IKEv2 peers.
An IKEv1 server (isakmpd(8)) is also available and, coupled with npppd(8), it allows you to build an IKEv1/L2TP VPN where IKEv2 can't be deployed.
Native WireGuard support is also available via the wg(4) device. As the manual explains, it can be configured the same way as all other network interfaces in OpenBSD.
/etc/iked/local.pub
, with the private key being stored in
/etc/iked/private/local.key
.
/etc/iked/local.pub
on the first system ("server1") should
be copied to /etc/iked/pubkeys/fqdn/server1.domain
on the
second system ("server2").
Then, /etc/iked/local.pub
on the second system should be
copied to /etc/iked/pubkeys/fqdn/server2.domain
on the first.
Replace "serverX.domain" with your own FQDN.
From that point, let's assume that server1 has a public IP of
192.0.2.1
and an internal network on 10.0.1.0/24
,
and that server2 has a public IP of 198.51.100.1
and an internal
network on 10.0.2.0/24
.
To enable the initiator to reach the responder, the isakmp
UDP
port should be open on the responder.
If one of the peers is behind NAT, the ipsec-nat-t
UDP port
should also be open on the responder.
If both peers have public IPs, then the ESP protocol should be allowed.
pass in log on $ext_if proto udp from 198.51.100.1 to 192.0.2.1 port {isakmp, ipsec-nat-t} tag IKED pass in log on $ext_if proto esp from 198.51.100.1 to 192.0.2.1 tag IKEDAn example
/etc/iked.conf
configuration for server1 (acting as
the responder) might look like this:
ikev2 'server1_rsa' passive esp \ from 10.0.1.0/24 to 10.0.2.0/24 \ local 192.0.2.1 peer 198.51.100.1 \ srcid server1.domainAnd a simple config for server2 acting as the initiator:
ikev2 'server2_rsa' active esp \ from 10.0.2.0/24 to 10.0.1.0/24 \ peer 192.0.2.1 \ srcid server2.domainUsing
iked -dv
can help you understand the exchange.
In this example, the responder is behind NAT:
server1# iked -dv ... ikev2_recv: IKE_SA_INIT request from initiator 198.51.100.1:500 to 192.0.2.1:500 policy 'server1_rsa' id 0, 510 bytes ikev2_msg_send: IKE_SA_INIT response from 192.0.2.1:500 to 198.51.100.1:500 msgid 0, 451 bytes ikev2_recv: IKE_AUTH request from initiator 198.51.100.1:4500 to 192.0.2.1:4500 policy 'server1_rsa' id 1, 800 bytes ikev2_msg_send: IKE_AUTH response from 192.0.2.1:4500 to 198.51.100.1:4500 msgid 1, 720 bytes, NAT-T sa_state: VALID -> ESTABLISHED from 198.51.100.1:4500 to 192.0.2.1:4500 policy 'server1_rsa'On the initiator side:
server2# iked -dv ... ikev2_msg_send: IKE_SA_INIT request from 0.0.0.0:500 to 192.0.2.1:500 msgid 0, 510 bytes ikev2_recv: IKE_SA_INIT response from responder 192.0.2.1:500 to 198.51.100.1:500 policy 'server2_rsa' id 0, 451 bytes ikev2_msg_send: IKE_AUTH request from 198.51.100.1:4500 to 192.0.2.1:4500 msgid 1, 800 bytes, NAT-T ikev2_recv: IKE_AUTH response from responder 192.0.2.1:4500 to 198.51.100.1:4500 policy 'server2_rsa' id 1, 720 bytes sa_state: VALID -> ESTABLISHED from 192.0.2.1:4500 to 198.51.100.1:4500 policy 'server2_rsa'The IPsec flows can be viewed with ipsecctl(8):
server1# ipsecctl -sa FLOWS: flow esp in from 10.0.2.0/24 to 10.0.1.0/24 peer 198.51.100.1 srcid FQDN/server1.domain dstid FQDN/server2.domain type use flow esp out from 10.0.1.0/24 to 10.0.2.0/24 peer 198.51.100.1 srcid FQDN/server1.domain dstid FQDN/server2.domain type require flow esp out from ::/0 to ::/0 type deny SAD: esp tunnel from 192.0.2.1 to 198.51.100.1 spi 0xabb5968a auth hmac-sha2-256 enc aes-256 esp tunnel from 198.51.100.1 to 192.0.2.1 spi 0xb1fc90b8 auth hmac-sha2-256 enc aes-256 server2# ipsecctl -sa FLOWS: flow esp in from 10.0.1.0/24 to 10.0.2.0/24 peer 192.0.2.1 srcid FQDN/server2.domain dstid FQDN/server1.domain type use flow esp out from 10.0.2.0/24 to 10.0.1.0/24 peer 192.0.2.1 srcid FQDN/server2.domain dstid FQDN/server1.domain type require flow esp out from ::/0 to ::/0 type deny SAD: esp tunnel from 192.0.2.1 to 198.51.100.1 spi 0xabb5968a auth hmac-sha2-256 enc aes-256 esp tunnel from 198.51.100.1 to 192.0.2.1 spi 0xb1fc90b8 auth hmac-sha2-256 enc aes-256With that, both internal networks should be able to reach each other. Traffic between them should appear after decapsulation on the
enc0
interface, and can be filtered as such.
In that example, tag VPN
has been added to the policy:
# pfctl -vvsr|grep VPN @16 pass log on enc0 tagged VPN # tcpdump -nei pflog0 rnr 16 00:03:26.793522 rule 16/(match) pass in on enc0: 10.0.2.24 > 10.0.1.13: icmp: echo requestSome words of warning:
srcid
, then iked will try to
use the key matching its FQDN by default.
local
, it just
ensures iked
listens on the correct interface.
peer
, it just
ensures connections are coming from a trusted IP.
ikev2 'server1_rsa' passive esp \ from 10.0.1.0/24 to 10.0.2.0/24 \ from 10.0.1.0/24 to 198.51.100.1 \ from 192.0.2.1 to 10.0.2.0/24 \ local 192.0.2.1 peer 198.51.100.1 \ srcid server1.domainAnd the initiator configuration would be:
ikev2 'server2_rsa' active esp \ from 10.0.2.0/24 to 10.0.1.0/24 \ from 10.0.2.0/24 to 192.0.2.1 \ from 198.51.100.1 to 10.0.1.0/24 \ peer 192.0.2.1 \ srcid server2.domain
Depending on the use case, as all traffic will go through the responder, one must ensure the initiator is configured to use a DNS server it can reach (possibly one on the responder).
10.0.5.0/24
network is used to support the
VPN. The actual internal IP address will automatically be installed by iked
on the lo1 interface.
We'll assume the public IP for the client is 203.0.113.2
.
As with the previous example, exchanging the default-provided RSA public keys
is enough to set up a simple authentication between the responder and the
initiator: /etc/iked/local.pub
on the first system ("server1")
should be copied to /etc/iked/pubkeys/fqdn/server1.domain
on the
second system ("roadwarrior").
Then, /etc/iked/local.pub
on the roadwarrior system should be
copied to /etc/iked/pubkeys/fqdn/roadwarrior
on the first.
Replace "serverX.domain" with your own FQDN.
The responder iked.conf(5) creates flows from any destination to the dynamic IP leases from the address pool, which will be decided at runtime, and tags the packets with ROADW:
ikev2 'responder_rsa' passive esp \ from any to dynamic \ local 192.0.2.1 peer any \ srcid server1.domain \ config address 10.0.5.0/24 \ tag "ROADW"The responder needs to provide an IP address to the initiator. This is achieved with the
config
directives.
When using the config address
option, to dynamic
will be replaced with the assigned dynamic IP address.
It also needs to allow IPsec from any host (since clients might connect from
anywhere), allow traffic tagged ROADW on enc0
and apply NAT to it:
pass in log on $ext_if proto udp from any to 192.0.2.1 port {isakmp, ipsec-nat-t} tag IKED pass in log on $ext_if proto esp from any to 192.0.2.1 tag IKED pass log on enc0 tagged ROADW match out log on $ext_if inet tagged ROADW nat-to $ext_ifThe initiator configures a global flow to send all its traffic to the responder, telling it to identify itself with the key named "roadwarrior":
ikev2 'roadwarrior' active esp \ from dynamic to any \ peer 192.0.2.1 \ srcid roadwarrior \ dstid server1.domain \ request address any \ iface lo1The initiator uses the
request address any
option to request a
dynamic IP address from the responder. The iface lo1
option
specifies the interface on which the received address and corresponding routes
will be installed.
The responder should have a proper NAT configuration for the road warrior
client.
Gracefully stopping the VPN on the initiator can be achieved using
ikectl decouple
(iked
is still running, pending
ikectl couple
so that it reconnects to the responder) or with
ikectl reset sa && rcctl stop iked
to permanently
stop iked
and ensure no flows are left behind.
It is also required to set up a PKI and X.509 certificates so that the initiator can validate the certificate advertised by the responder:
server1# ikectl ca vpn create server1# ikectl ca vpn install certificate for CA 'vpn' installed into /etc/iked/ca/ca.crt CRL for CA 'vpn' installed to /etc/iked/crls/ca.crl server1# ikectl ca vpn certificate server1.domain create server1# ikectl ca vpn certificate server1.domain install writing RSA key server1# cp /etc/iked/ca/ca.crt /var/www/htdocs/On the android device, browse to
http://192.0.2.1/ca.crt
and
import the CA certificate in the strongSwan client.
From that point, there are several choices to authenticate the initiator to the
responder:
eap "mschap-v2"
(which is the only EAP method supported
for now) as such:
user 'android' 'password' ikev2 'responder_eap' passive esp \ from any to dynamic \ local 192.0.2.1 peer any \ srcid server1.domain \ eap "mschap-v2" \ config address 10.0.5.0/24 \ config name-server 192.0.2.1 \ tag "ROADW"In the strongSwan client, a new profile is configured using:
192.0.2.1
for the server field
CN=VPN CA
certificate for the
CA certificate field
client1.domain
for the User identity field
server1.domain
in the Server identity field
(under 'advanced settings')
10.0.5.0/24
network, and all its traffic goes through the VPN, using
192.0.2.1
as its DNS server.
iked ca
, exported as an archive, and the .pfx file should be
made available online so that the client can install it.
The .pfx file bundles:
server1# ikectl ca vpn certificate client1.domain create server1# cp /etc/ssl/vpn/client1.domain.crt /etc/iked/certs/ server1# ikectl ca vpn certificate client1.domain export server1# tar -C /tmp -xzf client1.domain.tgz *pfx server1# cp /tmp/export/client1.domain.pfx /var/www/htdocs/client1.domain.pfxThe CA public certificate and client certificate bundle have to be imported in the strongSwan client when configuring the new profile.
The responder config is slightly simpler since there's no need to specify
eap
nor set a username/password:
ikev2 'responder_x509' passive esp \ from any to dynamic \ local 192.0.2.1 peer any \ srcid server1.domain \ config address 10.0.5.0/24 \ config name-server 192.0.2.1 \ tag "ROADW"In the strongSwan client, a new profile is configured, using:
192.0.2.1
for the server field
CN=client1.domain
certificate for the
User certificate field
client1.domain
for the User identity field
server1.domain
in the Server identity field (under
'advanced settings')
ca.crt
in the certificate authority store, and
ClientIP.p12
in the personal store.
The StrongSwan
project has a good documentation on this topic, with screenshots.
Windows doesn't easily allow setting the srcid
parameter for the
client, so the CN field of the client certificate has to match the client FQDN
sent to the responder, or its IP by default.
It is also required that srcid
on the responder matches the
responder FQDN (or its IP, if not using FQDN) - otherwise one might experience
a dreaded error 3801
.
The Libreswan project has
valuable details
on those requirements.
Once the certificates are imported, configure a new VPN connection with:
user 'windows' 'password' ikev2 'responder_eap' passive esp \ from any to dynamic \ local 192.0.2.1 peer any \ srcid server1.domain.fqdn \ eap "mschap-v2" \ config address 10.0.5.0/24 \ config name-server 192.0.2.1 \ tag "ROADW"By default, all the windows traffic will now go through the IKEv2 VPN.
At the time of writing, current versions of Windows use weak encryption
by default (3DES/SHA1).
This can be corrected with the PowerShell command
Set-VpnConnectionIPsecConfiguration
.
xl2tpd
third-party package is needed to act
as an L2TP client.
It is first required to enable
isakmpd(8) and ipsec
services so that the daemon is started and the
ipsec.conf(5)
configuration file loaded at boot:
# rcctl enable ipsec # rcctl enable isakmpd # rcctl set isakmpd flags -KThe following ipsec.conf(5) configuration should allow to connect to an IKEv1 server at
A.B.C.D
with a provided PSK, only allowing the UDP port 1701
for L2TP:
ike dynamic esp transport proto udp from egress to A.B.C.D port l2tp \ psk mekmitasdigoatStarting isakmpd(8) and loading ipsec.conf(5) using ipsecctl(8) should allow you to visualize configured Security Associations (SAs) and flows:
# rcctl start isakmpd # ipsecctl -f /etc/ipsec.conf # ipsecctl -sa FLOWS: flow esp in proto udp from A.B.C.D port l2tp to W.X.Y.Z peer A.B.C.D srcid my.client.fqdn dstid A.B.C.D/32 type use flow esp out proto udp from W.X.Y.Z to A.B.C.D port l2tp peer A.B.C.D srcid my.client.fqdn dstid A.B.C.D/32 type require SAD: esp transport from A.B.C.D to W.X.Y.Z spi 0x0d16ad1c auth hmac-sha1 enc aes esp transport from W.X.Y.Z to A.B.C.D spi 0xcd0549ba auth hmac-sha1 enc aesIf this is not the case, it might be required to tweak the phase 1 (Main) and phase 2 (Quick) parameters, when both parties exchange crypto parameters to agree on the best combination available. Ideally, those parameters should be provided by the remote server admin, and should be used in ipsec.conf(5):
ike dynamic esp transport proto udp from egress to A.B.C.D port l2tp \ main auth "hmac-sha1" enc "3des" group modp1024 \ quick auth "hmac-sha1" enc "aes" \ psk mekmitasdigoatOnce the IKEv1 tunnel is up and running, the L2TP tunnel needs to be configured. OpenBSD doesn't provide an L2TP client by default, so installing
xl2tpd
is required.
# pkg_add xl2tpdRefer to
/usr/local/share/doc/pkg-readmes/xl2tpd
for instructions
on how to properly setup the L2TP client.