IPSEC Tunnels using strongSwan

IPSEC (Internet Protocol Security) is a framework of open standards for ensuring private, secure communications over Internet Protocol (IP) networks through cryptographic security services. It provides essential security features such as authentication, integrity, and confidentiality, making it a crucial component in the establishment of secure virtual private networks (VPNs) and the protection of data transmitted over potentially insecure networks like the internet.

StrongSwan is an open-source IPSEC-based VPN solution that implements the IPsec protocol suite and Internet Key Exchange (IKE) protocols. Known for its robustness, flexibility, and extensive feature set, StrongSwan supports a wide range of platforms and is widely used in enterprise environments to secure network traffic, manage encryption keys, and facilitate secure remote access.

The following guide provides details on setting up stronSwan gateway host with dynamic clients connecting remotely.

strongSwan Gateway Host Installation and Configuration

The strongSwan gateway is deployed on Ubuntu 22.04 using builtin repos.

Install strongSwan and related packages using apt install.

$ sudo apt update
$ sudo apt install strongswan strongswan-swanctl charon-systemd \
libstrongswan-standard-plugins libstrongswan-extra-plugins \
libcharon-extra-plugins libcharon-extauth-plugins 

Configure Firewall and NAT rules

Configure the firewall rules to allow SSH from specific hosts and IPSEC standard ports on the gateway.

$ sudo ufw allow from 172.16.26.10 to any port 22 proto tcp
$ sudo ufw allow 500,4500/udp comment “IPSEC standard ports”

To allow the clients to route traffic via IPSEC tunnel and towards outside hosts we will configure NAT rules for the private subnet (192.168.254.0/24) we will allocate for the clients. Edit the /etc/ufw/before.rules file and configure “*nat” “*mangle” rules before the “*filter”. We also need to allow forwarding of ESP packets so that the IPSEC clients are able to connect.

# /etc/ufw/before.rules
*nat
-A POSTROUTING -s 192.168.254.0/24 -o ens3 -m policy --pol ipsec --dir out -j ACCEPT
-A POSTROUTING -s 192.168.254.0/24 -o ens3 -j MASQUERADE
COMMIT

*mangle
-A FORWARD --match policy --pol ipsec --dir in -s 192.168.254.0/24 -o ens3 -p tcp -m tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1361:1536 -j TCPMSS --set-mss 1360
COMMIT

# Don't delete these required lines, otherwise there will be errors
*filter
:ufw-before-input - [0:0]
:ufw-before-output - [0:0]
:ufw-before-forward - [0:0]
:ufw-not-local - [0:0]
# End required lines

-A ufw-before-forward --match policy --pol ipsec --dir in --proto esp -s 192.168.254.0/24 -j ACCEPT
-A ufw-before-forward --match policy --pol ipsec --dir out --proto esp -d 192.168.254.0/24 -j ACCEPT

.
.
.

Disable and Enable the firewall so that the rules get applied.

$ sudo ufw disable
$ sudo ufw enable

We need to forwarding of IP packets in the file /etc/sysctl.conf

# /etc/sysctl.conf
.
.
net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1

Apply the sysctl configurations

$ sudo sysctl -p

Create Certificate Authority and Generate Certificates

The IPSEC IKEv2 server requires certificates to authenticate itself to the clients. We will create our own certificate authority and generate required server and client certificates using the custom CA.

Generate a private key and use the key to create root certificate for the own CA.

$ ipsec pki --gen --size 4096 --type rsa --outform pem > ca.key.pem

$ ipsec pki --self --in ca.key.pem --type rsa --dn "CN=VPN Server CA" \
--ca --lifetime 3650 --outform pem > ca.cert.pem

Next generate a private key for the IPSEC gateway host and use the CA certificate created above to issue a certificate for the gateway host.

$ ipsec pki --gen --size 4096 --type rsa --outform pem > server.key.pem

$ ipsec pki --pub --in server.key.pem --type rsa | \
ipsec pki --issue --lifetime 2750 --cacert ca.cert.pem --cakey ca.key.pem \
--dn "CN=vpn.domain.com" --san="vpn.domain.com" \
--flag serverAuth --flag ikeIntermediate --outform pem > server.cert.pem

Move the certificate and key files to the relevant directories.

$ mv ca.key.pem /etc/swanctl/private/
$ mv ca.cert.pem /etc/swanctl/x509ca/

$ mv server.key.pem /etc/swanctl/private/
$ mv server.cert.pem /etc/swanctl/x509/

Gateway Host Configuration with Certificate authentication

In the following steps we will configure the Gateway and the client to authenticate with each other using certificates.

First we will create a certificate for the client named berry using the CA generated above.

$ ipsec pki --gen --size 4096 --type rsa --outform pem > berry.key.pem

$ ipsec pki --pub --in berry.key.pem --type rsa | \
ipsec pki --issue --lifetime 2750 \
--cacert  /etc/swanctl/x509ca/ca.cert.pem \
--cakey /etc/swanctl/private/ca.key.pem \
--dn "CN=berry.domain.com" --san="berry.domain.com" \
--flag serverAuth --flag ikeIntermediate --outform pem > berry.cert.pem

We need to copy the CA certificate and berry.domain.com key and certificate to the berry client machines in following locations.

/etc/swanctl/x509ca/ca.cert.pem
/etc/swanctl/private/berry.key.pem
/etc/swanctl/x509/berry.cert.pem

Edit the configuration file /etc/swanctl/swanctl.conf on the gateway host. Configure the local and remote hosts to authenticate via public key which is verified by the CA on both hosts. We will create a virtual IP pool (192.168.254.0/24) for the clients. We allow a server subnet “192.0.2.0/24” and gateway host IP 10.20.20.99 in the traffic selector for the clients. For cipher selection we will use aes128-sha256-modp3072 both for phase 1 and phase 2 negotiation.

# Section defining IKE connection configurations.
connections {

    # Section for an IKE connection named <conn>.
    rw {
        # IKE major version to use for connection.
        version = 2      
  
        # Comma separated proposals to accept for IKE.
        proposals = aes256-sha256-modp3072

        pools = rw_pool

        # Section for a local authentication round.
        local {

            # Authentication to perform locally (pubkey, psk, 
            # xauth[-backend] or eap[-method]).
            auth = pubkey

            # Comma separated list of certificate candidates to use 
            # for authentication.
            certs = server.cert.pem

            # IKE identity to use for authentication round.
            id = vpn.domain.com
        }

        # Section for a remote authentication round.
        remote {
            # Authentication to expect from remote (pubkey, psk, 
            # xauth[-backend] or eap[-method]).
            auth = eap-pubkey
        }

        children {
            # CHILD_SA configuration sub-section.
            rw {
                # ESP proposals to offer for the CHILD_SA.
                esp_proposals = aes128-sha256-modp3072

                # Local traffic selectors to include in CHILD_SA.
                local_ts  = 192.0.2.0/24,10.20.20.99/32
            }
        }
        # Send certificate requests payloads (yes or no).
        send_certreq = no
    }
}

# Section defining named pools.
pools {
    # Section defining a single pool with a unique name.
    rw_pool {
        # Addresses allocated in pool.
        addrs = 192.168.254.0/24
    }
}

Enable the strongSwan service on the gateway and start the service.

$ sudo systemctl enable strongswan
$ sudo systemctl start strongswan

Client configuration with certificate authentication

Edit the /etc/swanctl/swanctl.conf file on the client berry and make the following changes. Define the certificate with which the client will authenticate with the gateway host. The client requests a virtual IP for the gateway. Also we need to configure the remote IP subnets allowed in traffic selector.

# Section defining IKE connection configurations.
connections {

    # Section for an IKE connection named <conn>.
    home {
        # Remote address(es) to use for IKE communication, comma separated.
        remote_addrs = vpn.domain.com

        # Virtual IPs to request in configuration payload / Mode Config.
        vips = 0.0.0.0

        # Section for a local authentication round.
        local {
            auth = pubkey
            certs = berry.cert.pem
            id = berry.domain.com
        }

        # Section for a remote authentication round.
        remote {
            auth = pubkey
            id = vpn.domain.com
        }

        children {

            # CHILD_SA configuration sub-section.
            home {
                # Remote selectors to include in CHILD_SA.
                remote_ts  = 192.0.2.0/24,10.20.20.99/32

                # Action to perform after loading the configuration 
                # (none, trap, start).
                start_action = start

                # Action to perform on DPD timeout (clear, trap or restart).
                dpd_action = restart

                # Action to perform after a CHILD_SA gets closed 
                # (none, trap, start).
                close_action = trap
            }
        }
    }
}

Enable the strongSwan service on the client and start the service.

$ sudo systemctl enable strongswan
$ sudo systemctl start strongswan

Verify the IPSEC tunnel is up with the gateway.

$ sudo swanctl --list-sas

home: #5, ESTABLISHED, IKEv2, 1286da4a8598c5f5_i 381d0ff9a727ff92_r*
  local  'berry.domain.com' @ 10.130.130.4[4500] [192.168.254.1]
  remote 'vpn.domain.com' @ 192.0.2.66[4500]
  AES_CBC-128/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/CURVE_25519
  established 4182s ago, rekeying in 10205s
  home: #19, reqid 2, INSTALLED, TUNNEL-in-UDP, ESP:AES_CBC-128/HMAC_SHA2_256_128
    installed 554s ago, rekeying in 2730s, expires in 3406s
    in  c49d35a9, 207280 bytes,  3110 packets,     0s ago
    out c90f62e2, 297577 bytes,  3203 packets,     0s ago
    local  192.168.254.1/32

Gateway Host Configuration with EAP authentication

Another method to authenticate clients is to use password based EAP authentication. The gateway host authenticates itself with certificate validated via CA certificate. For clients we will generate pre-shared keys. The openssl rand command can be used to generate a PSK with >256 bit entropy converted to base64 format:

$ openssl rand -base64 33
bjsqbPZB3Isk/RHMCH6Pe9G+TDc4t.........

Edit the configuration file /etc/swanctl/swanctl.conf. Define the remote client to use eap-md5 authentication. We need to configure the secrets for the clients with the PSK generated with the above command.

# Section defining IKE connection configurations.
connections {

    # Section for an IKE connection named <conn>.
    rw {
        # IKE major version to use for connection.
        version = 2

        # Comma separated proposals to accept for IKE.
        proposals = aes256-sha256-modp3072

        pools = rw_pool

        # Section for a local authentication round.
        local {
            # Authentication to perform locally (pubkey, psk, 
            # xauth[-backend] or eap[-method]).
            auth = pubkey

            # Comma separated list of certificate candidates to use 
            # for authentication.
            certs = server.cert.pem

            # IKE identity to use for authentication round.
            id = vpn.domain.com
        }

        # Section for a remote authentication round.
        remote {
            # Authentication to expect from remote (pubkey, psk, 
            # xauth[-backend] or eap[-method]).
            auth = eap-md5
        }

        children {
            # CHILD_SA configuration sub-section.
            rw {
                # ESP proposals to offer for the CHILD_SA.
                esp_proposals = aes128-sha256-modp3072

                # Local traffic selectors to include in CHILD_SA.
                local_ts  = 192.0.2.0/24,10.20.20.99/32
            }
        }

        # Send certificate requests payloads (yes or no).
        send_certreq = no
    }
}

# Section defining named pools.
pools {
    # Section defining a single pool with a unique name.
    rw_pool {
        # Addresses allocated in pool.
        addrs = 192.168.254.0/24
    }
}

# Section defining secrets for IKE/EAP/XAuth authentication and private key decryption.
secrets {
    eap-berry {
      id = berry@domain.com
      secret = bjsqbPZB3Isk/RHMCH6Pe9G.....................
    }
    eap-dumper {
      id = dumper@domain.com
      secret = v8SKNjziori+KSywgeFz0T9.....................
    }
}

Client configuration with EAP authentication

Edit the /etc/swanctl/swanctl.conf file on the client berry and make the following changes. Configure the PSK generated for the client as the secret.

# Section defining IKE connection configurations.
connections {

    # Section for an IKE connection named <conn>.
    home {
        # Remote address(es) to use for IKE communication, comma separated.
        remote_addrs = vpn.domain.com

        # Virtual IPs to request in configuration payload / Mode Config.
        vips = 0.0.0.0

        # Section for a local authentication round.
        local {
            auth = eap
            id = berry@domain.com
        }

        # Section for a remote authentication round.
        remote {
            auth = pubkey
            id = vpn.domain.com
        }

        children {

            # CHILD_SA configuration sub-section.
            home {
                # Remote selectors to include in CHILD_SA.
                remote_ts  = 192.0.2.0/24,10.20.20.99/32

                # Action to perform after loading the configuration 
                # (none, trap, start).
                start_action = start

                # Action to perform on DPD timeout (clear, trap or restart).
                dpd_action = restart

                # Action to perform after a CHILD_SA gets closed 
                # (none, trap, start).
                close_action = trap
            }
        }
    }
}

# Section defining secrets for IKE/EAP/XAuth authentication and private key decryption.
secrets {
    # EAP secret section for a specific secret.
    eap-berry {
        # Identity the EAP/XAuth secret belongs to.
        id = berry@domain.com

        # Value of the EAP/XAuth secret.
        secret =  bjsqbPZB3Isk/RHMCH6Pe9....................
    }
}

Verify the IPSEC tunnel is up with the gateway.

$ sudo swanctl --list-sas

home: #5, ESTABLISHED, IKEv2, 1286da4a8598c5f5_i 381d0ff9a727ff92_r*
  local  'berry@domain.com' @ 10.130.130.4[4500] [192.168.250.1]
  remote 'vpn.domain.com' @ 192.0.2.66[4500]
  AES_CBC-128/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/CURVE_25519
  established 5466s ago, rekeying in 8921s
  home: #19, reqid 2, INSTALLED, TUNNEL-in-UDP, ESP:AES_CBC-128/HMAC_SHA2_256_128
    installed 1838s ago, rekeying in 1446s, expires in 2122s
    in  c49d35a9, 666903 bytes, 10109 packets,     0s ago
    out c90f62e2, 954978 bytes, 10421 packets,     0s ago
    local  192.168.254.1/32
    remote 10.20.20.99/32 192.0.2.0/24

References

https://docs.strongswan.org/docs/5.9/config/quickstart.html
https://docs.strongswan.org/docs/5.9/config/IKEv2.html
https://www.digitalocean.com/community/tutorials/how-to-set-up-an-ikev2-vpn-server-with-strongswan-on-ubuntu-22-04

Leave a Reply