Creating site-to-site IPSec tunnel using StrongSwan

Folks at Oracle being nice bros offer you some very generous Always Free Resources including a 4-core ARM instance with 4Gbps network bandwidth. This is perfect for a VPN server. So let's do it.

My server environment

Oracle Cloud Infrastructure
OS: Ubuntu-22.04 LTS

Step 1 - Install everything we need

First update the repo

sudo apt update

Install strongswan and certbot

sudo apt install strongswan certbot libcharon-extra-plugins libcharon-extauth-plugins libstrongswan-extra-plugins

strongswan package is the strongswan software itself.
certbot is a tool for getting certificate from Let's encrypt
Other packages provide more authentication method and more encryption algorithm like elliptic curve cryptography

Step 2 - Open ports on firewall

Open ports for http(80/tcp) and https(443/tcp) as well as 500, 4500/udp used by VPN in the firewall. The default firewall manager on Ubuntu is ufw

sudo ufw allow http
sudo ufw allow https
sudo ufw allow 500,4500/udp
sudo ufw reload

Normally for a cloud instance you also need to configure security rules for your cloud platform. On OCI this is done by Security Group.

Step 3 - Get certificate

Get certificate using certbot

Simply run certbot using this command. It will spin up a temporary web server and handle certificate issuance automatically. (That why we need to open both 80 and 443 TCP port)

sudo certbot certonly --rsa-key-size 4096 --standalone --agree-tos --no-eff-email --email <[email protected]> -d <your-domain.com>

I got the following output:

$ sudo certbot certonly --rsa-key-size 4096 --standalone --agree-tos --no-eff-email --email [email protected] -d vpn.nonaharry.com

Saving debug log to /var/log/letsencrypt/letsencrypt.log
Requesting a certificate for vpn.nonaharry.com

Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/vpn.nonaharry.com/fullchain.pem
Key is saved at:         /etc/letsencrypt/live/vpn.nonaharry.com/privkey.pem
This certificate expires on 2024-03-13.
These files will be updated when the certificate renews.
Certbot has set up a scheduled task to automatically renew this certificate in the background.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
If you like Certbot, please consider supporting our work by:
 * Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
 * Donating to EFF:                    https://eff.org/donate-le
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

In Linux symlink is a reference to a file.
Because these certificates need regularly updated, this make sure strongswan always use the latest certificate.
More about certificate

sudo ln -s /etc/letsencrypt/live/vpn.nonaharry.com/fullchain.pem /etc/ipsec.d/certs
sudo ln -s /etc/letsencrypt/live/vpn.nonaharry.com/chain.pem /etc/ipsec.d/cacerts
sudo ln -s /etc/letsencrypt/live/vpn.nonaharry.com/privkey.pem /etc/ipsec.d/private

Step 4 Configuring StrongSwan

StrongSwan comes with a default configuration file containing some sample configurations. However, we'll need to configure most of it by ourselves. Start by backing up the existing configuration file for reference:

sudo mv /etc/ipsec.conf{,.original}

Next, create a new blank configuration file using your preferred text editor, such as nano:

sudo nano /etc/ipsec.conf

Note: In IPSec VPNs, "left" generally refers to the local system (here, the server) you're configuring, and "right" refers to remote clients, such as phones and computers.

Begin by configuring StrongSwan to log daemon statuses for debugging purposes and to allow duplicate connections. Insert the following lines into your configuration file:

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

Now, add a section for your VPN configuration. This section will set up IKEv2 VPN Tunnels and ensure StrongSwan loads this configuration on startup. Append these lines:

conn ikev2-vpn
    auto=add
    compress=no
    type=tunnel
    keyexchange=ikev2
    fragmentation=yes
    forceencaps=yes

Include dead-peer detection to clear any unexpected disconnections. Add these settings:

dpdaction=clear
dpddelay=300s
rekey=no

Next, configure the server's "left" side IPSec parameters. These ensure the server accepts and correctly identifies incoming client connections. Add the following settings:

Include these in the file as shown:

left=%any
leftid=@server_domain_or_IP
leftcert=server-cert.pem
leftsendcert=always
leftsubnet=0.0.0.0/0

Note: For leftid, use @ only if identifying the server by a domain name. Use the IP address directly if identifying by IP.

Configure the client's "right" side IPSec parameters:

Add these lines:

right=%any
rightid=%any
rightauth=eap-mschapv2
rightsourceip=10.10.10.0/24
rightdns=8.8.8.8,8.8.4.4
rightsendcert=never

Instruct StrongSwan to request user credentials from clients upon connection:

eap_identity=%identity

Finally, add support for various client types by specifying key exchange, hashing, authentication, and encryption algorithms:

ike=chacha20poly1305-sha512-curve25519-prfsha512,aes256gcm16-sha384-prfsha384-ecp384,aes256-sha1-modp1024,aes128-sha1-modp1024,3des-sha1-modp1024!
esp=chacha20poly1305-sha512,aes256gcm16-ecp384,aes256-sha256,aes256-sha1,3des-sha1!

After ensuring all lines are correctly added, save and close the file. In nano, do this with CTRL + X, Y, then ENTER.

The final config looks like this:

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

conn ikev2-vpn
    auto=add
    compress=no
    type=tunnel
    keyexchange=ikev2
    fragmentation=yes
    forceencaps=yes
    dpdaction=clear
    dpddelay=300s
    rekey=no
    left=%any
    leftid=@server_domain_or_IP
    leftcert=server-cert.pem
    leftsendcert=always
    leftsubnet=0.0.0.0/0
    right=%any
    rightid=%any
    rightauth=eap-mschapv2
    rightsourceip=10.10.10.0/24
    rightdns=8.8.8.8,8.8.4.4
    rightsendcert=never
    eap_identity=%identity
    ike=chacha20poly1305-sha512-curve25519-prfsha512,aes256gcm16-sha384-prfsha384-ecp384,aes256-sha1-modp1024,aes128-sha1-modp1024,3des-sha1-modp1024!
    esp=chacha20poly1305-sha512,aes256gcm16-ecp384,aes256-sha256,aes256-sha1,3des-sha1!

Configuring VPN Authentication

We need to indicate the private key for the server and create list of users and credentials.
This information are stored in ipsec.secrets file.

sudo nano /etc/ipsec.secrets

Add these two lines.

: RSA "server-key.pem"
your_username : EAP "your_password"

Something I learned

The certificates generated by Certbot belong to user root by default which can not be accessed by other user.

Running ipsec in the foreground reviews the problem.

ubuntu@owncloud:/var/log$ sudo ipsec stop
Stopping strongSwan IPsec...
ubuntu@owncloud:/var/log$ sudo ipsec start --nofork
Starting strongSwan 5.9.5 IPsec [starter]...
00[DMN] Starting IKE charon daemon (strongSwan 5.9.5, Linux 5.15.0-1049-oracle, aarch64)
00[LIB] providers loaded by OpenSSL: legacy default
00[NET] using forecast interface enp0s6
00[LIB]   opening '/etc/ipsec.d/private/privkey.pem' failed: Permission denied
00[LIB] building CRED_PRIVATE_KEY - RSA failed, tried 11 builders
00[LIB] loaded plugins: 

Solution is simple. Change the ownership of the file to the group the user is in, in my case ubuntu, and make is readable by users in the group.

sudo chgrp ubuntu /etc/letsencrypt/live/vpn.nonaharry.com/privkey.pem
sudo chmod 0640 /etc/letsencrypt/live/vpn.nonaharry.com/privkey.pem

Reference

Thanks to these awesome articles.