Skip to content

IKEv2/IPSec VPN Setup Guide (Ubuntu + Huawei/macOS Client)

IKEv2 Basics

Protocol Overview

  • IKEv2 (Internet Key Exchange v2) is the signaling protocol; IPSec handles the actual encryption
  • Transport: UDP only — port 500 (handshake) + port 4500 (NAT traversal)
  • Two modes:
  • Tunnel Mode — entire IP packet is encapsulated in ESP, client gets a virtual IP. Used for road warrior / mobile clients
  • Transport Mode — only payload is encrypted, both endpoints keep real IPs. Used for server-to-server encryption

Authentication Methods

Type Description
PSK Pre-shared key, simplest to configure
MSCHAPv2 PSK + username/password via EAP
RSA Certificate-based, most secure

Key Concepts

  • SA (Security Association) — negotiated session parameters (keys, algorithms)
  • DPD (Dead Peer Detection) — heartbeat mechanism. dpddelay=300s means send a probe after 300s idle
  • NAT-T — when NAT is detected, traffic switches from UDP/500 to UDP/4500 automatically
  • MASQUERADE — dynamic SNAT, replaces client virtual IP with server's public IP. Third-party servers only see the VPN server's IP

Server Setup (Ubuntu)

1. Install strongSwan

apt update && apt install strongswan strongswan-pki libcharon-extra-plugins -y

2. /etc/ipsec.conf

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

conn ikev2-psk
    auto=add
    compress=no
    type=tunnel
    keyexchange=ikev2
    fragmentation=yes
    forceencaps=yes
    ike=aes256-sha256-modp2048!
    esp=aes256-sha256!
    dpdaction=clear
    dpddelay=300s
    rekey=no
    left=%any
    leftid=@myserver          # must match client's Remote ID
    leftsubnet=0.0.0.0/0      # routes pushed to client (0.0.0.0/0 = full tunnel)
    right=%any
    rightid=%any
    rightsourceip=10.10.30.0/24   # virtual IP pool for clients
    rightdns=8.8.8.8
    authby=secret
    leftauth=psk
    rightauth=psk

Field notes:

  • leftid — server identity used for PSK lookup. Use @string prefix to avoid being parsed as IP
  • forceencaps=yes — force NAT-T even without NAT, improves traversal
  • ike=...!! means strict match, no fallback negotiation
  • rightsourceip — must be a valid CIDR range, never 0.0.0.0

3. /etc/ipsec.secrets

: PSK "your_strong_password_here"

4. Enable IP Forwarding

echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf
sysctl -p

5. /etc/nftables.conf

#!/usr/sbin/nft -f
flush ruleset

table inet filter {
    chain input {
        type filter hook input priority 0; policy accept;
        udp dport { 500, 4500 } accept
    }

    chain forward {
        type filter hook forward priority 0; policy drop;

        # IKEv2 clients (routed through enp1s0, no tun device)
        iif "enp1s0" oif "enp1s0" ip saddr 10.10.30.0/24 accept
        iif "enp1s0" oif "enp1s0" ip daddr 10.10.30.0/24 ct state established,related accept

        # ICMP diagnostics
        icmp type { time-exceeded, destination-unreachable, echo-reply } accept
    }

    chain postrouting {
        type nat hook postrouting priority 100;
        ip saddr 10.10.30.0/24 oif "enp1s0" masquerade
    }
}

Replace enp1s0 with your actual interface (ip link to verify).

6. Start Services

systemctl restart strongswan-starter
systemctl enable strongswan-starter
systemctl restart nftables
systemctl enable nftables

Note: On Ubuntu 22.04+, the service is strongswan-starter, not strongswan


Client Configuration

Huawei (EMUI/HarmonyOS)

Huawei only supports the three built-in IKEv2 types — no OpenVPN or WireGuard native support.

Settings → Wi-Fi & Network → VPN → Add

Field Value
Type IKEv2/IPSec PSK
Server server public IP
IPSec identifier same as server leftid (e.g. myserver)
IPSec pre-shared key your PSK password

macOS

System Settings → VPN → Add VPN Configuration → IKEv2

Field Value
Server server public IP
Remote ID same as server leftid
Local ID leave blank
Authentication Shared Secret
Shared Secret your PSK password

Troubleshooting

Check Logs

journalctl -u strongswan-starter -f

Common Errors

Log Message Cause Fix
AUTH_FAILED PSK mismatch or leftid mismatch Verify leftid matches client Remote ID; check ipsec.secrets for typos
DH group unacceptable Client proposes different DH group Normal — strongSwan sends INVAL_KE and renegotiates, connection proceeds
no config found No matching conn for client Check rightid=%any and auto=add
Stuck at "Connecting" Packet not reaching server Check cloud security group (UDP 500/4500), verify with ss -ulnp | grep -E "500|4500"

Verify Listening Ports

ss -ulnp | grep -E "500|4500"

Verify nftables

nft list ruleset

Reload Config Without Restart

ipsec reload
ipsec statusall

Notes

  • Cloud security groups (Alibaba Cloud, AWS, etc.) must allow UDP 500 and 4500 inbound — nftables alone is not enough
  • leftid as plain number will be parsed as IP address by charon. Always use @prefix for non-IP identifiers
  • DPD vs UDP timeout: if using a UDP proxy (e.g. v2ray dokodemo-door), set dpddelay lower than the proxy's UDP session timeout (default 60s), e.g. dpddelay=50s
  • IKEv2 routing mode (no tun device) is more efficient than OpenVPN (userspace) — kernel-level forwarding with AES-NI hardware acceleration