====== Install StrongSwan on Ubuntu 24.04 ======
===== Introduction =====
* When installing StrongSwan on a server, the item that takes the most preparation is the setup of the PKI.
* Although it is easy once you figured it out, because StrongSwan has been around for such a long time there are lots of outdated or over complicated documentation out there.
* Even when I asked one of the AI engines for instructions it provided me with old outdated instructions.
* The following instructions should work well on any of the recent versions of StrongSwan
-----------
===== Install StrongSwan =====
* As stated on the StrongSwan Primer wiki page, StrongSwan has gone through an aggressive redesign and you should take care when installing it not to install the older legacy version.
* The natural behavior would be to install the StrongSwan meta package. This should **not** be done
* Instead we install **charon-systemd** and **strongswan-swanctl**
#Make sure the old StrongSwan versions are not installed or running
sudo systemctl disable strongswan.service
sudo systemctl stop strongswan
sudo apt-get remove strongswan-starter
sudo apt-get remove strongswan-charon
#Install the new style StrongSwan
sudo apt-get install charon-systemd strongswan-swanctl
#Enable its startup
sudo systemctl enable strongswan.service
#This will link the strongswan-swanctl
#Created symlink /etc/systemd/system/strongswan-swanctl.service → /usr/lib/systemd/system/strongswan.service.
#Created symlink /etc/systemd/system/multi-user.target.wants/strongswan.service → /usr/lib/systemd/system/strongswan.service.
#Start it up
sudo systemctl start strongswan-swanctl.service
#=== Or for the same result alternatively ===
sudo systemctl start strongswan
sudo service strongswan start
#Check Its status
sudo systemctl status strongswan-swanctl.service
#=== Or for the same result alternatively ===
sudo systemctl status strongswan
sudo service strongswan status
* This is the result of the status command on our server:
● strongswan.service - strongSwan IPsec IKEv1/IKEv2 daemon using swanctl
Loaded: loaded (/usr/lib/systemd/system/strongswan.service; enabled; preset: enabled)
Active: active (running) since Sun 2026-01-11 10:17:49 UTC; 11min ago
Main PID: 1777399 (charon-systemd)
Status: "charon-systemd running, strongSwan 5.9.13, Linux 6.8.0-90-generic, x86_64"
Tasks: 17 (limit: 1107)
Memory: 4.6M (peak: 20.0M)
CPU: 2.445s
CGroup: /system.slice/strongswan.service
└─1777399 /usr/sbin/charon-systemd
* As you can see it has the **charon-systemd** program running.
------
===== Create PKI =====
* Next we will create the PKI.
* We need to install a helper package that is part of StrongSwan first.
sudo apt-get install strongswan-pki
* We issue the following commands.
mkdir -p ~/pki/{ca,certs,private}
chmod 700 ~/pki
cd ~/pki
#Create the CA Certificate
pki --gen --type rsa --size 4096 --outform pem > ca/ca.key
pki --self --ca --lifetime 3650 --in ca/ca.key --type rsa --dn "CN=VPN Root CA" --outform pem > ca/ca.crt
#Create the server certificate:
pki --gen --type rsa --size 4096 --outform pem > private/server.key
pki --issue --lifetime 825 --in private/server.key --type rsa --cacert ca/ca.crt --cakey ca/ca.key --dn "CN=cloud.radiusdesk.com" --san cloud.radiusdesk.com --flag serverAuth --flag ikeIntermediate --outform pem > certs/server.crt
#Create a client certificate
pki --gen --type rsa --size 4096 --outform pem > private/carol.key
pki --issue --lifetime 825 --in private/carol.key --type rsa --cacert ca/ca.crt --cakey ca/ca.key --dn "CN=Carol" --san carol@strongswan.org --flag clientAuth --outform pem > certs/carolCert.pem
#View it
pki --print --in certs/carolCert.pem
* StrongSwan has some very good documentation on managing certificates
* https://docs.strongswan.org/docs/latest/pki/pkiQuickstart.html
--------
===== Configure Swanctl =====
* The way we configure StrongSwan is again a bit different compared to the 'traditional' way of configuring services running on a server.
* With StrongSwan we have the **/etc/swanctl** folder.
* Inside this folder are various sub-folders that are pre-installed.
* This setup relies strong on convention where **swanctl** expect certain items to be located under certain folders.
* We also have the **swanctl.conf** config file which we will cover in this section.
---------
==== Config File ====
* The config file has a JSON like structure.
* Please note that it is not valid JSON but rather a JSON like structure.
* Below is our demo server's config:
connections {
xfrm-gw {
local_addrs = %any
remote_addrs = %any
pools = rw_pool
# XFRM interface binding - CRITICAL
if_id_in = 100
if_id_out = 100
version = 2
proposals = aes128-sha1-modp2048
local {
auth = pubkey
certs = server.crt
id = cloud.radiusdesk.com
}
remote {
auth = pubkey
}
children {
xfrm-gw {
local_ts = 0.0.0.0/0
remote_ts = 0.0.0.0/0
if_id_in = 100
if_id_out = 100
esp_proposals = aes128-sha1-modp2048
start_action = start
dpd_action = restart
}
}
send_cert = always
dpd_delay = 30s
rekey_time = 1h
#send_certreq = no
}
}
pools {
rw_pool {
addrs = 10.3.1.0/16
}
}
------------
==== Cert and Key Files ====
* As we stated there are some pre-installed sub folders under the **/etc/swanctl** folder.
* The following should be used to contain the CA, server cert and server key files.
* Copy the **ca.crt** file to **/etc/swanctl/x509ca**.
* Copy the **server.key** file to **/etc/swanctl/private**.
* Copy the **server.crt** file to **/etc/swanctl/x509**.
----------
===== Xfrm Interface Prep =====
* When a client connects establishes a connection to the StrongSwan server, it is referred to as a Security Association (SA).
* We can use the swanctl command to see if there are any existing SAs.
sudo swanctl --list-sa
xfrm-gw: #11, ESTABLISHED, IKEv2, 8c4f8f2882625d72_i* f423c783942a5006_r
local 'cloud.radiusdesk.com' @ 164.160.89.129[4500]
remote 'carol@strongswan.org' @ 197.64.146.11[4500] [10.3.1.0]
AES_CBC-128/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_2048
established 679s ago, rekeying in 2626s
xfrm-gw: #7, reqid 1, INSTALLED, TUNNEL-in-UDP, ESP:AES_CBC-128/HMAC_SHA1_96/MODP_2048
installed 1372s ago, rekeying in 2128s, expires in 2588s
in c9f9936f (-|0x00000064), 2645 bytes, 29 packets, 139s ago
out c33bfaa6 (-|0x00000064), 22938 bytes, 38 packets, 139s ago
local 0.0.0.0/0
remote 0.0.0.0/0
* We implement a route based IPsec VPN (The other option is policy based).
* Route based IPsec specify a **if_id_in** and **if_id_out**.
* This is used to tag traffic inside a SA.
* With Wireguard, you can have multiple instances running on different ports.
* With StrongSwan there is one instance, but you can have multiple connections defined in the config file each using a unique **if_id_in** and **if_id_out**.
* The **if_id_in** and **if_if_out** in tern have to terminate into a **xfrm** interface.
* We will create a startup script that prepare this interface for us **BEFORE** we start StrongSwan.
* Create the file **/usr/local/sbin/xfrm-up.sh** with the following contents.
* We assume eth0 is the interface name where the server gets it Internet from. Please adapt if your server is different.
#!/bin/sh
set -e
#
IFACE=xfrm0
IF_ID=100
ADDR=10.3.0.1/32
SUBNET=10.3.0.0/24
# Create XFRM interface
ip link show "$IFACE" >/dev/null 2>&1 || \
ip link add "$IFACE" type xfrm if_id "$IF_ID"
# Assign IP
ip addr show "$IFACE" | grep -q "$ADDR" || \
ip addr add "$ADDR" dev "$IFACE"
# Bring interface up
ip link set "$IFACE" up
# Route for remote side
ip route show "$SUBNET" | grep -q "$IFACE" || \
ip route add "$SUBNET" dev "$IFACE"
# ---- NAT via nftables ----
# Create table if missing
nft list table ip nat >/dev/null 2>&1 || \
nft add table ip nat
# Create postrouting chain if missing
nft list chain ip nat postrouting >/dev/null 2>&1 || \
nft add chain ip nat postrouting { type nat hook postrouting priority 100\; }
# Add SNAT/MASQUERADE rule (idempotent)
nft list chain ip nat postrouting | grep -q "$IFACE" || \
nft add rule ip nat postrouting oifname "$IFACE" masquerade
nft add rule ip nat postrouting oifname "eth0" masquerade
* Create a startup script called **/etc/systemd/system/xfrm0.service** which calls the script above.
[Unit]
Description=XFRM Interface xfrm0
Before=strongswan.service
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/xfrm-up.sh
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
* Enable the startup script and start it up
systemctl enable xfrm0
systemctl start xfrm0
* Everything is now prepared on the server side.
* We can now configure clients in APdesk and MESHdesk to route certain traffic through the IPsec tunnel.
* These are covered in a dedicated Wiki page.