IPsec is the standard VPN protocol of the networking world. Office firewalls (FortiGate, pfSense, Cisco, MikroTik, Sophos) and cloud VPN gateways (AWS, Azure, Google Cloud) all speak it. When you need to connect your VPS or dedicated server to such equipment, WireGuard often isn’t an option, and strongSwan, the leading open-source IPsec implementation for Linux, is the tool.

For server-to-server links where you control both ends, WireGuard is simpler. See WireGuard site-to-site.

IPsec in brief

  • IKE (IKEv2) negotiates keys and authenticates the peers, over UDP 500, then UDP 4500 when NAT is detected.
  • ESP encrypts the actual traffic (IP protocol 50, or encapsulated in UDP 4500 behind NAT).
  • Security associations (SAs) define what’s encrypted between which subnets (“traffic selectors”).

Both sides must agree on: IKE version, authentication method, encryption and integrity algorithms, DH group, lifetimes, and the local/remote subnets.

Example plan

Your server (strongSwan) Office firewall
Public IP 203.0.113.10 198.51.100.1
Local subnet 10.0.1.0/24 (or the server’s own IP /32) 192.168.50.0/24
Auth Pre-shared key (or certificates) same
Proposal AES-256-GCM, SHA-256 PRF, DH group 19/20 (ECP) same

Install

sudo apt install strongswan strongswan-swanctl charon-systemd
sudo systemctl enable --now strongswan

(On RHEL-family systems: dnf install strongswan, service strongswan.)

Modern strongSwan is configured with swanctl (/etc/swanctl/swanctl.conf). The older ipsec.conf style still exists, but is deprecated.

Configure the tunnel

/etc/swanctl/conf.d/office.conf:

connections {
    office {
        version = 2
        local_addrs  = 203.0.113.10
        remote_addrs = 198.51.100.1
        proposals = aes256gcm16-prfsha256-ecp256

        local {
            auth = psk
            id = 203.0.113.10
        }
        remote {
            auth = psk
            id = 198.51.100.1
        }
        children {
            office-net {
                local_ts  = 10.0.1.0/24
                remote_ts = 192.168.50.0/24
                esp_proposals = aes256gcm16-ecp256
                start_action = start
                dpd_action = restart
            }
        }
        dpd_delay = 30s
        rekey_time = 8h
    }
}

secrets {
    ike-office {
        id-1 = 203.0.113.10
        id-2 = 198.51.100.1
        secret = "a-very-long-random-pre-shared-key"
    }
}

Generate a strong PSK with openssl rand -base64 48. For many tunnels or higher security, use certificates instead of PSKs (auth = pubkey with certificates in /etc/swanctl/x509/).

Load and start:

sudo swanctl --load-all
sudo swanctl --initiate --child office-net
sudo swanctl --list-sas

Configure the other side

On the firewall or cloud gateway, mirror the settings:

  • IKEv2, the same PSK, IDs = the public IPs (or as configured);
  • identical proposals (encryption, integrity/PRF, DH group) for both phase 1 (IKE) and phase 2 (ESP/child SA);
  • local and remote subnets swapped.

Cloud gateways usually provide a downloadable config for “generic strongSwan”. Use it as the reference.

Firewall and forwarding on Linux

Open the IPsec ports to the peer:

sudo ufw allow from 198.51.100.1 to any port 500,4500 proto udp
# ESP (protocol 50), if no NAT-T is in use:
sudo iptables -A INPUT -p esp -s 198.51.100.1 -j ACCEPT

If the server routes for a subnet behind it, enable forwarding (net.ipv4.ip_forward=1), and make sure NAT rules don’t rewrite traffic headed into the tunnel. Exclude it from masquerading:

sudo iptables -t nat -I POSTROUTING -s 10.0.1.0/24 -d 192.168.50.0/24 -j ACCEPT

See NAT explained.

IPsec on Linux is policy-based by default: there’s no tunnel interface, and the kernel encrypts traffic matching the traffic selectors. For route-based setups (needed for dynamic routing or overlapping policies), strongSwan supports XFRM interfaces (if_id_in/if_id_out).

Test

ping -I 10.0.1.1 192.168.50.10          # source from the local subnet if needed
sudo swanctl --list-sas                 # established IKE SA + installed CHILD SA with traffic counters
sudo ip xfrm policy                     # kernel policies
sudo ip xfrm state                      # kernel SAs

Debugging

sudo journalctl -u strongswan -f

Typical problems:

Log message / symptom Cause
NO_PROPOSAL_CHOSEN Algorithms or DH groups don’t match
AUTHENTICATION_FAILED Wrong PSK, or mismatched IDs
TS_UNACCEPTABLE Traffic selectors (subnets) don’t match the other side
SA up, but no traffic Firewall or NAT rules, missing routes on LAN hosts, overlapping subnets
Tunnel drops every few hours Rekey settings mismatched. Align lifetimes, enable DPD

Increase logging temporarily in /etc/strongswan.d/charon-logging.conf (ike = 2, cfg = 2).

Remote-access IPsec (IKEv2) for devices

strongSwan can also serve IKEv2 VPN clients. Windows, macOS, iOS and Android have built-in IKEv2 clients, so no extra app is needed. It requires certificates (server certificate from a trusted CA) and EAP authentication. It’s more complex than WireGuard, but useful where installing apps isn’t allowed.

Quick reference

Task Command
Load config swanctl --load-all
Start tunnel swanctl --initiate --child NAME
Status swanctl --list-sas
Kernel view ip xfrm state / ip xfrm policy
Logs journalctl -u strongswan
Ports UDP 500, UDP 4500, ESP (50)

Related: OpenVPN vs WireGuard and MTU explained.

  • VPN
  • IPsec
  • Networking