How to configure IPSEC site-to-site on Linux Ubuntu 24.04 with strongswan

A site-to-site IPsec tunnel between two Ubuntu 24.04 machines, built with strongSwan and a pre-shared key. The configuration below is mirrored on both ends: what is left on one host is right on the other, and nothing else differs.


The Lab


Host A Host B
LAN 192.168.111.1/32 (lo) 192.168.112.1/32 (lo)
WAN 87.121.220.254/30 87.120.223.222/29
| |
+------ IKEv2 / ESP ----+


root@HOST-A:~# ip -br a
lo UNKNOWN 127.0.0.1/8 192.168.111.1/32
vlan11A@if2 UP 87.121.220.254/30


root@HOST-B:~# ip -br a
lo UNKNOWN 127.0.0.1/8 192.168.112.1/32 ::1/128
enp1s0 UP 87.120.223.222/29 fe80::5054:ff:fe47:e98e/64
enp7s0 DOWN


Install strongSwan


On both hosts:

sudo apt update
sudo apt install -y strongswan strongswan-starter


strongswan-starter is the package that provides the classic ipsec command together with /etc/ipsec.conf and /etc/ipsec.secrets. Without it, Ubuntu 24.04 gives you only the newer swanctl stack and the files below are never read.


Host A Configuration


cat /etc/ipsec.conf


config setup
uniqueids=no

conn public-ip-test
keyexchange=ikev2
type=tunnel
auto=start
authby=psk

left=87.121.220.254
leftid=87.121.220.254
leftsubnet=192.168.111.1/32

right=87.120.223.222
rightid=87.120.223.222
rightsubnet=192.168.112.1/32

ike=aes256-sha256-modp2048!
esp=aes256-sha256!
dpdaction=restart
dpddelay=30s
dpdtimeout=120s


left is always the local side and right the remote one. The exclamation mark at the end of the ike and esp proposals makes them strict: strongSwan will not silently fall back to a weaker algorithm the peer offers. auto=start brings the tunnel up as soon as the daemon starts and keeps it up.


The pre-shared key is written local first, remote second:

cat /etc/ipsec.secrets


# Local # Remote : PSK: XXXXXXX
87.121.220.254 87.120.223.222 : PSK "VeryStrongTestKey123!"


The file holds a plaintext secret, so it should belong to root only:

chmod 600 /etc/ipsec.secrets


Host B Configuration


The same file with the two sides swapped:

config setup
uniqueids=no

conn public-ip-test
keyexchange=ikev2
type=tunnel
auto=start
authby=psk

left=87.120.223.222
leftid=87.120.223.222
leftsubnet=192.168.112.1/32

right=87.121.220.254
rightid=87.121.220.254
rightsubnet=192.168.111.1/32

ike=aes256-sha256-modp2048!
esp=aes256-sha256!
dpdaction=restart
dpddelay=30s
dpdtimeout=120s


87.120.223.222 87.121.220.254 : PSK "VeryStrongTestKey123!"


The connection name, the proposals and the PSK must be identical on both ends. The subnets and the addresses are the only values that mirror.


Open IKE and ESP on the Firewall


IPsec needs UDP 500 for IKE, UDP 4500 when either peer sits behind NAT, and IP protocol 50 for the encrypted ESP traffic itself:

iptables -A INPUT -p udp --dport 500 -j ACCEPT
iptables -A INPUT -p udp --dport 4500 -j ACCEPT
iptables -A INPUT -p esp -j ACCEPT


ESP is a protocol, not a port. A firewall that only opens the two UDP ports will show a successfully established IKE session and no traffic through the tunnel at all.


Start the Tunnel


On both hosts:

ipsec start


To survive a reboot, enable the service instead:

sudo systemctl enable --now strongswan-starter


After editing ipsec.conf, reload the configuration and bring the connection up again:

ipsec reload
ipsec up public-ip-test


Verify the Tunnel


Host A:

root@HOST-A:~# ipsec status
Security Associations (1 up, 0 connecting):
public-ip-test[100]: ESTABLISHED 61 minutes ago, 87.121.220.254[87.121.220.254]...87.120.223.222[87.120.223.222]
public-ip-test{110}: INSTALLED, TUNNEL, reqid 1, ESP SPIs: c486c5d2_i ca921c9f_o
public-ip-test{110}: 192.168.111.1/32 === 192.168.112.1/32


Host B:

root@HOST-B:~# ipsec status
Security Associations (1 up, 0 connecting):
public-ip-test[28]: ESTABLISHED 61 minutes ago, 87.120.223.222[87.120.223.222]...87.121.220.254[87.121.220.254]
public-ip-test{110}: INSTALLED, TUNNEL, reqid 1, ESP SPIs: ca921c9f_i c486c5d2_o
public-ip-test{110}: 192.168.112.1/32 === 192.168.111.1/32


Two things confirm the tunnel is real. ESTABLISHED on its own only means IKE finished; the line that matters is INSTALLED, TUNNEL, which means a child SA exists and the kernel policy is in place. And the SPIs cross over: the inbound SPI on Host A is the outbound SPI on Host B.


Now send traffic between the two protected addresses:

root@HOST-A:~# ping 192.168.112.1
PING 192.168.112.1 (192.168.112.1) 56(84) bytes of data.
64 bytes from 192.168.112.1: icmp_seq=1 ttl=64 time=28.6 ms
64 bytes from 192.168.112.1: icmp_seq=2 ttl=64 time=28.8 ms
64 bytes from 192.168.112.1: icmp_seq=3 ttl=64 time=29.1 ms
^C
--- 192.168.112.1 ping statistics ---
6 packets transmitted, 6 received, 0% packet loss, time 5008ms
rtt min/avg/max/mdev = 28.480/28.795/29.056/0.194 ms


Only traffic whose source and destination fall inside the configured subnets is encrypted. If the ping leaves with the WAN address as its source it never matches the policy, so force the source when in doubt:

ping -I 192.168.111.1 192.168.112.1


The kernel policies and the negotiated SAs can be inspected directly:

ip xfrm policy
ip xfrm state


Moving to Real Subnets


Replace the /32 addresses with the actual networks on each side:

leftsubnet=192.168.111.0/24
rightsubnet=192.168.112.0/24


Once the hosts route for a network behind them rather than answering for themselves, forwarding has to be enabled:

echo "net.ipv4.ip_forward=1" > /etc/sysctl.d/99-ipsec.conf
sysctl --system


Any NAT rule that masquerades outbound traffic must also exclude the remote subnet, otherwise packets are rewritten to the WAN address before the IPsec policy sees them and leave the tunnel unencrypted.


When It Does Not Come Up


Watch the negotiation live while forcing the connection:

journalctl -u strongswan-starter -f


NO_PROPOSAL_CHOSEN means the ike or esp lines differ between the peers. AUTHENTICATION_FAILED or no matching peer config found points at the PSK or at a leftid that does not match what the other side expects. If the IKE session establishes and traffic still does not pass, ESP is being dropped somewhere in the path.


For the full picture, including the selected proposals and the installed policies:

ipsec statusall