Wireguard, OpenVPN, and IPSec for Client VPNs
Today I’m trying to understand if Wireguard really is over-hyped, if OpenVPN is really worth all the hassle to get the user-side features like client authentication and two factor, and if IPSec has any place in the modern VPN landscape. Specifically, looking at traditional ‘road warrior’ or client access VPNs, where all of your users are dialing in to your enterprise network, not the new-fangled mesh VPNs or zero trust setups.
This page contains all of the configurations I used in testing, feel free to use them on your own. I did all of my testing on Debian 13 (Trixie) which is currently in testing, on Linux 6.12. My clients are also running the same OS/kernel.
Contents⌗
- Video
- Beginning
- IPIP
- Wireguard
- OpenVPN
- OpenVPN - Certificates
- OpenVPN - Server
- OpenVPN - Client
- OpenVPN - DCO
- IPSec
- IPSec - Server
- IPSec - Client
Video⌗
Beginning⌗
We need to enable IP forwarding on our router for any of these setups. This doesn’t persist across reboots, but most of my configs on this page are not intended to.
# Enable Forwarding
echo 1 > /proc/sys/net/ipv6/conf/all/forwarding
IPIP⌗
No install here. Just copy the commands and change the IPs as necessary. client is given 2001:db8:7:a::/64 in this example with the router taking the first address for itself, Enterprise subnet is 2001:db8:7::/48 in this example, Public IPs must be hardcoded for IPIP (cannot be dynamic).
# IPIP Setup (on Client)
ip link add name ipip6 type ip6tnl local <client IP> remote <server IP> mode any
ip link set up ipip6
ip addr add 2001:db8:7:a::2/64 dev ipip6
ip -6 route add 2001:db8:7::/48 dev ipip6
# IPIP Setup (on Server)
ip link add name ipip6 type ip6tnl local <server IP> remote <client IP> mode any
ip link set up ipip6
ip addr add 2001:db8:7:a::/64 dev ipip6
#addr add implicitly adds a /64 route to that interface, so no other routes required
Wireguard⌗
First we install it (apt install wireguard
)
Next, we need to create keys on each nodes, so run this command on both sides:
#Gen private key
wg genkey | tee /etc/wireguard/server.key
#Gen public key from private key
cat /etc/wireguard/server.key | wg pubkey | tee /etc/wireguard/server.pub
# You will need these two keys later, so be ready to copy and paste them
Next, we need a /etc/wireguard/wg0.conf
on both nodes:
# Conf for SERVER side
[Interface]
PrivateKey = oBic4cnZOBjmucL10dtiB5QZpUkqPPjEm9rA6E90iUg=
Address = 2001:db8:7:b::/128
ListenPort = 51820
# Peer (CLIENT's public key here)
[Peer]
PublicKey = 1b0J0WcCObEOq0R9zIQoitRa6I4YqLDDL7evmNwN3Go=
AllowedIPs = 2001:db8:7:b::/64
Endpoint = testsrc2.palnet.net:51820
# Conf for CLIENT side
[Interface]
PrivateKey = qKUMygkE+YPWE2MmdCOi5j6Sno78/w9FvXHGTIVpoVU=
Address = 2001:db8:7:b::4/64
ListenPort = 51820
# Peer (SERVER's public key here)
[Peer]
PublicKey = 2VIGy0JS27RjGI9VDug2iNK4dd/Ned9dNDnlCOP1DTQ=
AllowedIPs = 2001:db8:7::/48
Endpoint = wgmetal.palnet.net:51820
Then bring up the interfaces (wg-quick up wg0
on both sides). WG will automatically brign up the route to our enterprise subnet, and it will work.
OpenVPN⌗
First we install it (apt install openvpn
).
Certificates⌗
We will be using cert auth here, so we need an x.509 cert for both sides. For simplicity, I’m using self-signed ECDSA certs using prime256v1 for leafs and scep384 for the CA
#Generate private key using scep384:
openssl ecparam -name secp384r1 -genkey -out root.key
#Sign the root certificate
#Pathlen:0 means there can be only one more cert below this CA (no more CAs)
#Make sure you update the subj name with your own names
#C=US is also the country, it's optional
#O= is the organization, also optional
#CN= is the Common Name and it's required
#I also set validity to 69 years, make sure you watch for expiration (manually)
openssl req -new -key root.key -x509 -nodes -days 25202 -out root.pem -subj "/C=US/O=apalrd.net/CN=openvpn" -addext "basicConstraints=critical,CA:TRUE,pathlen:0"
#Now you can view it (for fun)
openssl x509 -in root.pem -text -noout
Next, generate the server’s cert + key
#Change this to the name of your server
export sv=wgmetal.palnet.net
#Generate scep256 key for this client
#If you want 192-bit security, use scep384r1 instead of prime256v1
openssl ecparam -name prime256v1 -genkey -out $sv.key
#Generate a CSR (certificate signing request) for my new key
#again, C and O are optional, CN is the Common Name of the cert
#Allowed only for server auth
openssl req -new -key $sv.key -out $sv.csr -subj "/C=US/O=apalrd.net/CN=$sv" -addext "extendedKeyUsage = serverAuth" --addext "keyUsage = digitalSignature,keyAgreement" -addext "subjectAltName = DNS:$sv"
#Sign the CSR using the root
#Shorter lived at only 365 days
openssl x509 -req -in $sv.csr -CA root.pem -CAkey root.key -CAcreateserial -out $sv.crt -days 365 -sha256 -copy_extensions=copyall
#Now you can view it (for fun)
openssl x509 -in $sv.crt -text -noout
And finally, generate the client cert:
#Change this to the name of your client
export cl=testsrc2.palnet.net
#Generate scep256 key for this client
#If you want 192-bit security, use scep384r1 instead of prime256v1
openssl ecparam -name prime256v1 -genkey -out $cl.key
#Generate a CSR (certificate signing request) for my new key
#again, C and O are optional, CN is the Common Name of the cert
openssl req -new -key $cl.key -out $cl.csr -subj "/C=US/O=apalrd.net/CN=$cl" -addext "extendedKeyUsage = clientAuth" --addext "keyUsage = digitalSignature,keyAgreement"
#Sign the CSR using the root
#Sign it allowing for server and client auth as the key usage
openssl x509 -req -in $cl.csr -CA root.pem -CAkey root.key -CAcreateserial -out $cl.crt -days 365 -sha256 -copy_extensions=copyall
#Now you can view it (for fun)
openssl x509 -in $cl.crt -text -noout
Transfer your .key and .crt files to the server and client respectively. The server will need its client .key and .crt, and the server will need its server .key and .crt, and they both need root.pem
Server⌗
The server setup is quite complex. Here’s my final /etc/openvpn/server.conf
:
server
#UDP over IPV6 outside of tunnel
port 1194
proto udp6
dev tun
user nobody
group nogroup
persist-key
persist-tun
keepalive 10 120
topology subnet
#IPv6 inside of tunnel
server-ipv6 2001:db8:7:c::/64
ifconfig-ipv6-pool 2001:db8:7:c::/64
push "route-ipv6 2001:db8:7::/48"
ifconfig-pool-persist ipp.txt
#Crypto parameters
dh none
ecdh-curve prime256v1
tls-crypt tls-crypt.key
#Client verification
ca root.pem
cert server.crt
key server.key
remote-cert-tls client
# no CRL in my setup
#crl-verify root.pem
auth SHA256
cipher AES-128-GCM
data-ciphers AES-128-GCM
tls-server
tls-version-min 1.2
tls-cipher TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256
client-config-dir /etc/openvpn/ccd
status /var/log/openvpn/status.log
verb 3
Also, a few commands to get it started:
openvpn --genkey secret /etc/openvpn/tls-crypt.key
mkdir -p /etc/openvpn/ccd
mkdir -p /var/log/openvpn
systemctl enable --now openvpn@server
Once we’ve generated tls-crypt.key we need to copy that file to the client (it’s our Shared Secret). We don’t * have * to use shared secret auth, but I have here.
Client⌗
For the client we need a /etc/openvpn/client.conf
file:
client
remote wgmetal.palnet.net 1194
dev tun
resolv-retry infinite
nobind
persist-key
persist-tun
remote-cert-tls server
verify-x509-name wgmetal.palnet.net name
auth SHA256
cipher AES-128-GCM
data-ciphers AES-128-GCM
auth-nocache
tls-client
tls-version-min 1.2
tls-cipher TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256
verb 3
#Client certs
cert client.crt
key client.key
#Server's CA
ca root.pem
tls-crypt tls-crypt.key
remote-cert-tls server
And a simple command to start:
systemctl enable --now openvpn@client
DCO⌗
The whole reason for this test is to see if DCO is as performant! And we aren’t using DCO just yet. So, here’s how to enable it:
#Make sure you are running a kernel version which is in the repo
apt update
apt upgrade -y
#Reboot if you installed a new kernel here, or linux-headers will not find itself
apt install -y linux-headers-$(uname -r)
#This will compile the module with DKMS
apt install -y openvpn-dco-dkms
modprobe ovpn-dco-v2
At this point, restarting the server (or client, it works both ways) should enable DCO. Check the logs (journalctl -xeu openvpn@server
) to make sure.
IPSec⌗
Oh boy you are in for a fun ride here, we’re gonna install Strongswan (apt install -y strongswan
) to get started.
StrongSwan Server⌗
We are gonna need the server’s certificate and key from earlier, and since StrongSwan is stupid, we need to convert it to DER format (openssl ec -inform pem -outform der -in server.key -out server.der
). Put the root cert into /etc/ipsec.d/cacerts/root.pem
, the server’s cert in /etc/ipsec.d/certs/server.crt
, and the server’s DER key in /etc/ipsec.d/private/server.der
.
We’re going to completely erase /etc/ipsec.conf
, you can replace it with my new one:
config setup
charondebug="ike 1, knl 1, cfg 0"
uniqueids=no
# our ikev2 vpn
conn ikev2-vpn
auto=add
compress=no
type=tunnel
keyexchange=ikev2
fragmentation=yes
forceencaps=yes
dpdaction=clear
dpddelay=300s
rekey=no
#left side (idfk why they name it left and right)
#ip to bind to locally
left=%any
#this must match the SAN of the certificate
#or, skip the @ and use an IP
leftid=@wgmetal.palnet.net
#leftid=2001:db8:6969::420
leftcert=server.crt
leftsendcert=always
#route to tell the clients
leftsubnet=2001:db8:7::/48
#again, no idea why they call them left/right
#anyway clients can be anything
right=%any
rightid=%any
#eap-tls would use client certs, but we will do mschap for now
rightauth=eap-mschapv2
#address pool for clients
rightsourceip=2001:db8:7:d::/64
#you can assign DNS too if you want
#rightdns=2620:fe::fe
#set this to never for mschap or always for eap-tls
rightsendcert=never
#??
eap_identity=%identity
#crypto
#yes, these are no longer recommended in favor of AES 256
#however I tested OpenVPN with aes128gcm so I wanted to make it fair
ike=aes128gcm16-prfsha256-ecp256
esp=aes128gcm16-ecp256
For some reason, StrongSwan doesn’t use a file path to a key in the config file. I guess they treat the path itself as secret? Anyway, edit /etc/ipsec.secrets
:
# This file holds shared secrets or RSA private keys for authentication.
# Our ECDSA private key
: ECDSA server.der
# Our admin user accounts (these are plaintext passwords)
admin : EAP "admin"
admin2 : EAP "admin"
admin3 : EAP "admin"
admin4 : EAP "admin"
admin5 : EAP "admin"
admin6 : EAP "admin"
admin7 : EAP "admin"
StrongSwan Client⌗
The client side is a bit easier. We again need our root certificate (put it in /etc/ipsec.d/cacerts/root.pem
), but not a client cert, since we are doing old fashioned usernames and passwords here.
We’re going to setup the config for the client (/etc/ipsec.conf
) also:
config setup
conn ikev2-rw
right=wgmetal.palnet.net
# This should match the `leftid` value on your server's configuration
rightid=%wgmetal.palnet.net
rightsubnet=::/0
rightauth=pubkey
leftsourceip=%config
leftid=admin2
leftauth=eap-mschapv2
eap_identity=admin2
auto=start
And we need a ipsec.secrets
file here too:
# This file holds shared secrets or RSA private keys for authentication.
# Our user account (these are plaintext passwords)
admin2 : EAP "admin"