====== 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.