Synopsis #
This chapter covers site-to-site virtual private networks (VPNs) and host-to-host cryptographic tunnels on OpenBSD. It focuses on IKEv2 using the base system iked(8) and the kernel IPsec stack ipsec(4) , with practical patterns for NAT traversal, selector design, and observability. A complete road-warrior deployment using the in-kernel wg(4) interface is maintained separately. Runtime management uses ikectl(8) , packet filtering is handled by pf.conf(5) and pfctl(8) , and link inspection uses tcpdump(8) . The encrypted interface for IPsec is enc(4) .
Use these patterns to interconnect sites over untrusted networks, to publish internal services across providers securely, or to replace legacy tunnels with modern cryptography.
Design Considerations #
- Selectors and scope. Define traffic by networks (for example,
10.0.0.0/24to10.20.0.0/24) rather thanany. Keep selectors minimal and symmetric on both peers. - Authentication. Pre-shared key (PSK) is simplest for site-to-site. Certificates scale better and are preferred where third-party trust or revocation is needed.
- NAT traversal. Permit UDP ports 500 and 4500 and protocol ESP on WAN. IKEv2 NAT-T is automatic when address translation is detected.
- Routing. Enable only the address-family forwarding required by the protected networks, then add the corresponding static or dynamic routes.
- Performance. Prefer AES-GCM proposals to offload integrity to the cipher. Keep MTU and MSS in mind where encapsulation crosses constrained links.
- Operations. Ensure time sync on all peers (for example, with ntpd(8) ). Log IKE and PF events during rollout.
Configuration #
The examples assume:
- Site A WAN:
198.51.100.10, LAN:10.0.0.0/24 - Site B WAN:
203.0.113.10, LAN:10.20.0.0/24
Adjust interface names and addresses to match the deployment.
Baseline system settings (both sites) #
# printf '%s\n' 'net.inet.ip.forwarding=1' >> /etc/sysctl.conf
# Persist IPv4 forwarding for the IPv4 protected networks
# sysctl net.inet.ip.forwarding=1
# Apply IPv4 forwarding immediately
The example carries IPv4 only. Do not enable IPv6 forwarding without an IPv6 routing and PF policy.
PF allowances for IKEv2 and ESP (both sites) #
Permit IKE (UDP 500/4500) and ESP on the WAN, and allow traffic between the protected subnets on the inside. Syntax is defined in pf.conf(5) .
## /etc/pf.conf - minimal allowances for IKEv2/IPsec
set skip on lo
wan = "em0" # adjust
lan_net = "{ 10.0.0.0/24, 10.20.0.0/24 }"
block all
# IKE and IPsec on the WAN
pass in on $wan proto udp to ($wan) port { 500, 4500 } keep state
pass in on $wan proto esp keep state
pass out on $wan proto { udp, esp } keep state
# Permit protected subnets after decryption
pass on enc0 from 10.0.0.0/24 to 10.20.0.0/24 keep state
pass on enc0 from 10.20.0.0/24 to 10.0.0.0/24 keep state
Reload and confirm with pfctl(8) .
# pfctl -f /etc/pf.conf
# pfctl -sr | egrep 'udp .* (500|4500)|proto esp|enc0'
Site-to-site IKEv2 with PSK #
Configuration is in iked.conf(5)
. One side initiates (active), the other listens (passive). Proposals below use AES-GCM.
Site A (initiator) #
## /etc/iked.conf - Site A
ikev2 "s2s-a2b" active esp from 10.0.0.0/24 to 10.20.0.0/24 \
peer 203.0.113.10 \
ikesa enc aes-256-gcm prf sha256 group 14 \
childsa enc aes-256-gcm \
psk "change-this-shared-secret"
Site B (responder) #
## /etc/iked.conf - Site B
ikev2 "s2s-b2a" passive esp from 10.20.0.0/24 to 10.0.0.0/24 \
ikesa enc aes-256-gcm prf sha256 group 14 \
childsa enc aes-256-gcm \
psk "change-this-shared-secret"
Enable and start iked(8) on both peers with rcctl(8) :
# rcctl enable iked
# Start at boot
# rcctl start iked
# Launch now (initiator will dial the responder)
If either peer is behind NAT, IKEv2 will encapsulate ESP in UDP 4500 automatically (NAT-T). Ensure the upstream allows those ports.
WireGuard Deployments #
Configure a Road-Warrior WireGuard VPN
provides the maintained wg(4) procedure, including key handling, persistent interfaces, allowed IP ranges, routes, PF, return-path design, and packet-level verification. Keep WireGuard routing and firewall policy in that complete procedure rather than copying an isolated runtime example.
Verification #
- IKEv2 state and flows with ikectl(8) :
$ ikectl show sa
# IKE_SA and CHILD_SA state, SPIs, lifetimes
$ ikectl show flows
# Traffic selectors currently installed
- Path testing:
$ ping -n -c 3 10.20.0.1
# Test the protected path from Site A to Site B
- PF and routes:
# pfctl -vvsr | egrep 'udp .* (500|4500)|proto esp'
# Confirm IKEv2 and ESP allowances
$ netstat -rn | grep '10\.20\.0\.'
# Confirm the protected-network route
Troubleshooting #
- No IKE negotiation. Verify UDP 500/4500 reachability and that the responder is in
passivemode. Inspect logs in/var/log/daemonforiked. - Selector mismatch. If one side uses
10.0.0.0/24 to 10.20.0.0/24and the other uses10.0.0.0/24 to 10.0.0.0/24, CHILD_SA will not install. Alignfrom/tonetworks. Checkikectl show flows. - NAT or path asymmetry. Ensure upstreams carry UDP 4500 unchanged and that return traffic follows the same egress. For IPsec,
enc0rules should allow the protected subnets. - MTU black holes. If large transfers stall, clamp MSS on the WAN during testing in PF (
scrub in max-mss ...) and refine after measuring path MTU. - Clock skew. Certificates and IKE lifetimes are time-sensitive. Ensure NTP is working with ntpd(8) .