Skip to main content
โšก Calmops

IPsec VPN: IKEv2 Setup with strongSwan and Certificate Authentication

Introduction

IPsec is the VPN protocol built into every major OS โ€” Windows, macOS, iOS, and Android all support IKEv2/IPsec natively, with no client software needed. This makes it ideal for mobile device VPNs and enterprise deployments. strongSwan is the leading open-source IPsec implementation on Linux.

IPsec vs WireGuard vs OpenVPN:

  • IPsec: Native OS support, no client install, complex config
  • WireGuard: Fastest, simplest, needs client app
  • OpenVPN: Best firewall traversal, needs client app

IPsec Architecture

IKE (Internet Key Exchange):
  Phase 1 (IKE SA): Authenticate peers, establish secure channel
  Phase 2 (Child SA): Negotiate encryption for actual data

Data plane:
  ESP (Encapsulating Security Payload): Encrypts + authenticates data
  AH (Authentication Header): Authenticates only (rarely used)

Modes:
  Transport mode: Encrypts payload only (host-to-host)
  Tunnel mode:    Encrypts entire IP packet (gateway-to-gateway, VPN)

Install strongSwan

sudo apt update
sudo apt install strongswan strongswan-pki libcharon-extra-plugins

# Verify
ipsec version

Generate Certificates

# Create PKI directory
mkdir -p ~/pki/{cacerts,certs,private}
chmod 700 ~/pki

# 1. Generate CA key and certificate
ipsec pki --gen --type ecdsa --size 256 --outform pem > ~/pki/private/ca.key.pem
ipsec pki --self --ca --lifetime 3650 \
    --in ~/pki/private/ca.key.pem --type ecdsa \
    --dn "CN=VPN CA, O=My Company" \
    --outform pem > ~/pki/cacerts/ca.cert.pem

# 2. Generate server key and certificate
ipsec pki --gen --type ecdsa --size 256 --outform pem > ~/pki/private/server.key.pem
ipsec pki --pub --in ~/pki/private/server.key.pem --type ecdsa | \
    ipsec pki --issue --lifetime 1825 \
    --cacert ~/pki/cacerts/ca.cert.pem \
    --cakey ~/pki/private/ca.key.pem \
    --dn "CN=vpn.example.com, O=My Company" \
    --san vpn.example.com \
    --flag serverAuth --flag ikeIntermediate \
    --outform pem > ~/pki/certs/server.cert.pem

# 3. Copy to strongSwan directories
sudo cp ~/pki/cacerts/ca.cert.pem /etc/ipsec.d/cacerts/
sudo cp ~/pki/certs/server.cert.pem /etc/ipsec.d/certs/
sudo cp ~/pki/private/server.key.pem /etc/ipsec.d/private/
sudo chmod 600 /etc/ipsec.d/private/server.key.pem

Server Configuration

IKEv2 with EAP (Username/Password for Mobile Clients)

sudo nano /etc/ipsec.conf
# /etc/ipsec.conf

config setup
    charondebug="ike 1, knl 1, cfg 0"
    uniqueids=never

# IKEv2 with EAP-MSCHAPv2 (works with Windows, macOS, iOS, Android natively)
conn ikev2-eap
    auto=add
    compress=no
    type=tunnel
    keyexchange=ikev2
    fragmentation=yes
    forceencaps=yes

    # Server side
    left=%any
    [email protected]
    leftcert=server.cert.pem
    leftsendcert=always
    leftsubnet=0.0.0.0/0

    # Client side
    right=%any
    rightid=%any
    rightauth=eap-mschapv2
    rightsourceip=10.10.10.0/24
    rightdns=1.1.1.1,8.8.8.8
    rightsendcert=never

    # Crypto (modern, strong)
    ike=aes256gcm16-sha256-ecp256!
    esp=aes256gcm16-sha256!

    # Timeouts
    dpdaction=clear
    dpddelay=300s
    rekey=no

    eap_identity=%identity
# /etc/ipsec.secrets โ€” user credentials
sudo nano /etc/ipsec.secrets
# /etc/ipsec.secrets

# Server certificate private key
: RSA server.key.pem

# VPN users (username : EAP "password")
alice : EAP "alice-password-123"
bob   : EAP "bob-secure-pass"
# Enable IP forwarding
echo "net.ipv4.ip_forward=1" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

# NAT for VPN clients
sudo iptables -t nat -A POSTROUTING -s 10.10.10.0/24 -o eth0 -j MASQUERADE
sudo iptables -A FORWARD -i eth0 -o eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT
sudo iptables -A FORWARD -i eth0 -o eth0 -j ACCEPT

# Start strongSwan
sudo systemctl enable --now strongswan-starter
sudo ipsec status

Site-to-Site IPsec

# /etc/ipsec.conf โ€” Site A (192.168.1.0/24)

conn site-to-site
    auto=start
    type=tunnel
    keyexchange=ikev2

    # Site A
    left=203.0.113.1          # Site A's public IP
    leftsubnet=192.168.1.0/24 # Site A's LAN
    [email protected]

    # Site B
    right=203.0.113.2         # Site B's public IP
    rightsubnet=192.168.2.0/24 # Site B's LAN
    [email protected]

    # Authentication
    authby=secret             # or: authby=rsasig for certificates

    # Crypto
    ike=aes256gcm16-sha256-ecp256!
    esp=aes256gcm16-sha256!

    # Keep alive
    dpdaction=restart
    dpddelay=30s
    dpdtimeout=120s
# /etc/ipsec.secrets โ€” pre-shared key
@site-a.example.com @site-b.example.com : PSK "very-long-random-preshared-key-here"

Client Configuration

Windows (Built-in IKEv2)

# PowerShell โ€” add VPN connection
Add-VpnConnection -Name "My VPN" `
    -ServerAddress "vpn.example.com" `
    -TunnelType "IKEv2" `
    -AuthenticationMethod "EAP" `
    -EncryptionLevel "Required" `
    -RememberCredential $true

# Import CA certificate (required for certificate auth)
Import-Certificate -FilePath "ca.cert.pem" `
    -CertStoreLocation "Cert:\LocalMachine\Root"

macOS (Built-in IKEv2)

System Preferences โ†’ Network โ†’ + โ†’ VPN โ†’ IKEv2
Server Address: vpn.example.com
Remote ID: vpn.example.com
Authentication: Username
Username: alice
Password: alice-password-123

iOS/Android

Both support IKEv2 natively in Settings โ†’ VPN.

Linux (networkmanager-strongswan)

sudo apt install network-manager-strongswan

# Or configure manually
sudo nano /etc/ipsec.conf
# Linux client config
conn my-vpn
    auto=start
    keyexchange=ikev2
    left=%any
    leftauth=eap-mschapv2
    leftsourceip=%config
    right=vpn.example.com
    rightauth=pubkey
    rightcert=ca.cert.pem
    [email protected]
    eap_identity=alice
# /etc/ipsec.secrets
alice : EAP "alice-password-123"

Certificate-Based Authentication (More Secure)

# Generate client certificate
ipsec pki --gen --type ecdsa --size 256 --outform pem > ~/pki/private/alice.key.pem
ipsec pki --pub --in ~/pki/private/alice.key.pem --type ecdsa | \
    ipsec pki --issue --lifetime 825 \
    --cacert ~/pki/cacerts/ca.cert.pem \
    --cakey ~/pki/private/ca.key.pem \
    --dn "CN=alice, O=My Company" \
    --outform pem > ~/pki/certs/alice.cert.pem

# Create PKCS12 bundle for client import
openssl pkcs12 -export \
    -inkey ~/pki/private/alice.key.pem \
    -in ~/pki/certs/alice.cert.pem \
    -certfile ~/pki/cacerts/ca.cert.pem \
    -out alice.p12 \
    -passout pass:client-password
# Server config for certificate auth
conn ikev2-cert
    auto=add
    keyexchange=ikev2
    left=%any
    [email protected]
    leftcert=server.cert.pem
    leftsendcert=always
    leftsubnet=0.0.0.0/0
    right=%any
    rightauth=pubkey
    rightsourceip=10.10.10.0/24
    rightdns=1.1.1.1
    ike=aes256gcm16-sha256-ecp256!
    esp=aes256gcm16-sha256!

Monitoring and Troubleshooting

# Check connection status
sudo ipsec status
sudo ipsec statusall

# Show Security Associations
sudo ip xfrm state
sudo ip xfrm policy

# Real-time logs
sudo journalctl -u strongswan-starter -f

# Test connectivity
ping 10.10.10.1  # ping VPN server from client

# Debug mode (verbose)
sudo ipsec stop
sudo ipsec start --nofork  # runs in foreground with full debug output

# Common issues:
# "no proposal chosen" โ†’ cipher mismatch, check ike= and esp= settings
# "authentication failed" โ†’ wrong credentials or certificate issue
# "no route to host" โ†’ check IP forwarding and NAT rules
# "CHILD_SA failed" โ†’ check esp= cipher settings

Revoking Access

# Revoke a user's certificate
ipsec pki --revoke --cacert ~/pki/cacerts/ca.cert.pem \
    --cakey ~/pki/private/ca.key.pem \
    --cert ~/pki/certs/alice.cert.pem \
    --outform pem > ~/pki/crls/crl.pem

# Copy CRL to strongSwan
sudo cp ~/pki/crls/crl.pem /etc/ipsec.d/crls/

# Reload
sudo ipsec reload

# For EAP users: just remove from /etc/ipsec.secrets and reload
sudo ipsec reload

Resources

Comments