Libreswan server and client configuration

Libreswan server and client configuration

"Perilous to us all are the devices of an art deeper than we possess ourselves." -- Gandalf

Revised 2023-07-27. Corrections/complaints contact Hugh Sparks

What's all this?

This is a mindless walk-through that shows one way of setting up an IKEv2 certificate-based VPN server on Linux that works with Windows, iOS, Android, Linux, and MacOS clients. It's not a tutorial or explanation of the technology. References are cited where you can learn more.

This procedure has been tested on Fedora 32-37 with Libreswan 4.11.x and OpenSSL 3. It works with all the clients described as of the revision date displayed at the top of this page. (Recipes like this tend to be ephemeral, so take note of that date.)

Recent news

The arrival of openss 3.x caused problems with Apple products: The regular method of exporting .p12 certificates produced files that couldn't be imported on iOS and MacOS. (Don't worry about this if this is your first visit to this site.)

Contents

Caviats

This kind of VPN is easy to configure and easy to use. No nagging about passwords once it's all working. And unless you've made some special friends at the NSA, it's reasonably secure.

But this security and ease of use comes at a price: If someone steals your laptop or phone and manages to log in, they can waltz around your LAN like they own the place. It's your responsibility to secure all mobile devices because there's no other barrier to your home or office.


Server Configuration

We begin on the VPN server which is named "magoo."

Install Libreswan

dnf install libreswan

Certificates and the VPN server

More than one server process is involved when hosting a VPN. The service that deals with cryptography and the associated certificates is called "ipsec" - The ipsec service expects to find its certificates in an sqlite database located in:

/var/lib/ipsec/nss/cert9.db

The filename "cert9.db" is conventional for nss certificate databases. A typical Linux system will have several cert9.db files associated with other packages.

Working with certificates

We will be working with two utiltites: "certutil" and "pk12util". Both need to know where the certificate database is located. Deal with that by defining an environment variable that points to this directory:

export mynss=/var/lib/ipsec/nss

Initialize the NSS database

List contents of $mynss. If you don't see a cert9.db file, initialize the database using:

certutil -N -d $mynss

Create a certificate signing authority

In this example, "MagooCorp" is the organization name. "Magoo CA" is the common name of the certificate authority.

certutil -S -x -n "Magoo CA" -s "O=MagooCorp,CN=Magoo CA" \
    -z <(head -c 1024 /dev/urandom) \
    -k rsa -g 4096 -v 120 \
    -d $mynss \
    -t "CT,," -2

You will be prompted for answers:

Q: Is this a CA certificate?
A: y

Q: Enter the path length constraint, enter to skip...
A: <Enter> 

Q: Is this a critical extension?
A: N

Using the server IP number instead of an IP name

The following configuration uses the IP number of the VPN server instead of the IP name. My VPN server is also my internet gateway and it provides services to clients on the LAN that I don't want to make publicly available. The DNS server (bind) is configured to use "views" - My server IP name resolves to its local IP address when used by internal clients and to the public IP number for external clients. The firewall is more permissive for numbers on the internal LAN. But the VPN and associated certificates need a consistent way of refering to the server. So I used the number.

Assign a temporary shell variable for the server's public IP address

PUBLIC_IP=12.34.56.68

This is only needed when creating the server certificate.

Create a server certificate signed by the CA

certutil -S -c "Magoo CA" -n "Magoo VPN" -s "O=MagooCorp,CN=Magoo VPN" \
    -z <(head -c 1024 /dev/urandom) \
    -k rsa -g 4096 -v 120 \
    -d $mynss \
    -t ",," \
    --keyUsage digitalSignature,keyEncipherment \
    --extKeyUsage serverAuth \
    --extSAN "ip:$PUBLIC_IP,dns:$PUBLIC_IP"

Configure a server connection

Create the file:

/etc/ipsec.d/magoo.conf

That contains:

conn magoo
    left=12.34.56.68
    leftcert="Magoo VPN"
    leftid=%fromcert
    leftsendcert=always
    leftsubnet=0.0.0.0/0
    leftrsasigkey=%cert
    leftmodecfgserver=yes
    right=%any
    rightid=%fromcert
    rightaddresspool=192.168.1.200-192.168.1.254
    rightca=%same
    rightrsasigkey=%cert
    rightmodecfgclient=yes
    modecfgdns=192.168.1.2
    narrowing=yes
    dpddelay=30
    dpdtimeout=120
    dpdaction=clear
    auto=add
    ikev2=insist
    rekey=no
    fragmentation=yes
    mobike=yes
    authby=rsasig
    pfs=no

Note: On my system, the "left" parameter can't be %defaultroute. I think this is because I have multiple IP numbers on the same adapter. It also can't be the IP name of the server because that gets resolved to a local IP inside the LAN. So the number must be used literally.

Configure the firewall:

Using iptables:

The following expressions need to be worked into your existing firewall script. I assume you have a functioning firewall that lets your LAN clients access the internet. If you're creating a firewall from scratch, these rules are not sufficient.

Do not execute these commands if your system uses firewalld. See references at the end of this article for complete examples using iptables or firewalld.

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
iptables -A INPUT -p ah  -j ACCEPT
iptables -I INPUT -m policy --pol ipsec --dir in -j ACCEPT
iptables -I FORWARD -m policy --pol ipsec --dir in -j ACCEPT
iptables -t nat -I POSTROUTING -m policy --pol ipsec --dir out -j ACCEPT

Enable port forwarding in your firewall script with the line:

echo 1 > /proc/sys/net/ipv4/ip_forward

This could also be done by adding another kernel parameter:

net.ipv4.ip_forward = 1

Configure kernel parameters

Installing the libreswan package takes care of setting many kernel parameters. I found it necessary to add two more:

Create this file:

/etc/sysctl.d/51-libreswan.conf

Containing:

net.ipv4.conf.all.rp_filter = 2
net.ipv4.conf.default.rp_filter = 2

These expressions set the reverse path forwarding filter policy to "loose=2". The default setting in Fedora 31 is "strict=1". The VPN described here won't work with that setting.

See: Need for loose reverse path filtering

Assign the new kernel parameters without rebooting by executing:

sysctl -p /etc/sysctl.d/51-libreswan.conf

Start the VPN server daemon

systemctl enable --now ipsec

The server is now ready to accept connections.


Exporting client certificates

The following sections show how to connect clients to your new VPN. For each client, we must create a certificate, copy it to the client and install it in the client's certificate store. Details of this procedure vary depending on the client's operating system.


iOS client configuration

In this example, the phone belongs to "Hugh."

Create a client certificate:

certutil -S -c "Magoo CA" -n "Hugh iPhone to MagooVPN" -s "O=MagooCorp,CN=Hugh iPhone to MagooVPN" \
    -z <(head -c 1024 /dev/urandom) \
    -k rsa -g 4096 -v 120 \
    -d $mynss \
    -t ",," \
    --keyUsage digitalSignature,keyEncipherment \
    --extKeyUsage serverAuth,clientAuth -8 "Hugh iPhone to MagooVPN"

Export the client certificate:

As of openssl 3, iOS won't import certificates exported with the standard linux utility pk12util.

The standard way: (does not work for any Apple product)

pk12util -d $mynss -n "Hugh iPhone to MagooVPN" -o hugh_iphone.p12

The new way (requires this short script):

pk12extract -d $mynss -n "Hugh iPhone to MagooVPN" -o hugh_iphone.p12

You will be prompted for a password. The iPhone won't import a certificate with a null password.

Export the CA certificate as a .cer file:

certutil -L -d $mynss -n "Magoo CA" -a -o magoo_ca.cer

Note: Only iOS needs a separate CA file. The .p12 file contains this information but Apple "thinks different."

These files will appear in the current directory:

magoo_ca.cer
hugh_iphone.p12

Transfer certificates to the phone:

Enclose them in an email and send it to the iphone owner. There are other ways using the iOS Files application.

Import the certificates on the client:

On the phone, open the mail and click on "magoo_ca.cer" It will transfer the certificate to the certificate store on the phone. The certificate is in a pending state - not yet imported.

Go the the certificate store here:

Settings -> General -> Profiles

Click on the new certificate and click a series of more-or-less obvious buttons to get it "really imported."

Go back to the email and do the same thing with the "hugh_iphone.p12" client certificate. This time while it's in the pending state, it will be displayed as an anonymous "Identity Certificate" - The phone can't read the actual CA name inside until the encryption is removed. You'll be prompted to supply the password when the import step occurs. When the process is finished it will appear in the store as "Hugh iPhone".

It is best to delete the exported certificate files.

Trust the signing certificate

Go to:

Settings -> General -> About -> Certificate Trust Settings ->
ENABLE FULL TRUST FOR ROOT CERTIFICATES

Next to MagooCA, turn on the switch.

Configure a VPN connection

Go to:

Settings -> General -> VPN

Click "Add VPN connection" and fill in the form:

You are ready to make a connection.


Windows client configuration

In this example, the Windows box is named "Asus."

Create a client certificate:

certutil -S -c "Magoo CA" -n "Asus to MagooVPN" -s "O=MagooCorp,CN=Asus to MagooVPN" \
    -z <(head -c 1024 /dev/urandom) \
    -k rsa -g 4096 -v 120 \
    -d $mynss \
    -t ",," \
    --keyUsage digitalSignature,keyEncipherment \
    --extKeyUsage serverAuth,clientAuth -8 "Asus to MagooVPN"

Export .p12 certificate:

pk12util -d $mynss -n "Asus to MagooVPN" -o asus_magoo.p12

Transfer certificate to Windows client

If you're on a LAN, just copy it to the client's desktop. Otherwise mail it as an enclosure and save it on the client desktop.

Import the client certificate:

It's a good idea to delete "asus_magoo.p12" now.

Create a VPN connection

Enable high security encryption

This is not an option: Modern version of Librswan won't work with Microsoft's default 1024 bit encryption.

Create a plain text file:

High Security VPN - Install.reg

Containing:

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\RasMan\Parameters]
"NegotiateDH2048_AES256"=dword:00000001

Right-click on the file and select "Merge"

You are ready to connect.


Android client configuration

In this example, the Android device is named "Hughpad."

Create a client certificate:

certutil -S -c "Magoo CA" -n "Hughpad to MagooVPN" -s "O=MagooCorp,CN=Hughpad to MagooVPN" \
    -z <(head -c 1024 /dev/urandom) \
    -k rsa -g 4096 -v 120 \
    -d $mynss \
    -t ",," \
    --keyUsage digitalSignature,keyEncipherment \
    --extKeyUsage serverAuth,clientAuth -8 "Hughpad to MagooVPN"

Export .p12 certificate:

Newer versions of Android OS that use openssl 3 won't import certificates exported with the standard linux utility pk12util.

The standard way: (does not work for newer Android devices)

pk12util -d $mynss -n "Hugh iPhone to MagooVPN" -o hugh_iphone.p12

The new way (requires this short script):

pk12extract -d $mynss -n "Hughpad to MagooVPN" -o hughpad_magoo.p12

You will be prompted for a password.

Transfer certificate to client

Email it as an attachment and save it on the client. The default location will be something like:

/storage/emulated/0/Download/hughpad_magoo.p12

Install VPN software (Android 4.x to 9.x)

Run the google play store app, search for and install "strongSwan VPN Client"

Create a VPN connection

When you start the connection, a number of nags, warnings and prompts will appear. One will rant about "Unknown beings are intercepting your internet traffic" or something like that. Just say yes to everything.

You are ready to connect.


Linux client configuration

In this example, the Linux box is named "Robor."

Create a client certificate:

certutil -S -c "Magoo CA" -n "Robor2MagooVPN" -s "O=MagooCorp,CN=Robor2MagooVPN" \
    -z <(head -c 1024 /dev/urandom) \
    -k rsa -g 4096 -v 120 \
    -d $mynss \
    -t ",," \
    --keyUsage digitalSignature,keyEncipherment \
    --extKeyUsage serverAuth,clientAuth -8 "Robor2MagooVPN"

Note: When using the NetworkManager GUI, the certificate nickname cannot contain spaces or underscore characters.

Export .p12 certificate:

pk12util -d $mynss -n "Robor2MagooVPN" -o robor_magoo.p12

Transfer the .p12 file to the client machine:

You'll figure something out.

Initialize the NSS database: (if not already present on the client)

ipsec initnss

Import the client certificate:

pk12util -d $mynss -i robor_magoo.p12

You'll be prompted for the password. The certificate contains the nickname specified when the certificate was created.

Create a VPN connection with NetworkManager

From the GUI run:

Network Connections

OR from a console window run:

nm-connection-editor

Connect or disconnect using the network icon on the task bar.

Alternative: Create a VPN connection "by hand"

Create the file:

/etc/ipsec.d/magoo.conf

Containing:

conn magoo
    left=%defaultroute
    leftcert=Robor2MagooVPN
    leftmodecfgclient=yes
    right=12.34.56.68
    rightsubnet=0.0.0.0/0
    rightmodecfgserver=yes
    narrowing=yes
    ikev2=insist
    rekey=yes
    fragmentation=yes
    auto=add    
    mobike=yes

Start the service

systemctl enable --now ipsec

Connect using:

ipsec auto --up magoo

Disconnect using:

ipsec auto --down magoo

MacOS client configuration

In this example, the MacOS box is named "MyMac"

Create a client certificate:

certutil -S -c "Magoo CA" -n "MyMac2MagooVPN" -s "O=MagooCorp,CN=MyMac2MagooVPN" \
    -z <(head -c 1024 /dev/urandom) \
    -k rsa -g 4096 -v 120 \
    -d $mynss \
    -t ",," \
    --keyUsage digitalSignature,keyEncipherment \
    --extKeyUsage serverAuth,clientAuth -8 "MyMac2MagooVPN"

Export .p12 certificate:

Apple can't deal with certificates exported with the standard pk12util. Instead download and use this short script.:

pk12extract -d $mynss -n "MyMac2MagooVPN" -o MyMac2MagooVPN.p12

You will be prompted for a password. MacOS won't import a certificate with a null password.

Transfer the .p12 file to the client machine:

You'll figure something out.

Import the client certificate:

Double-click on the MyMac2MagooVPN.p12 file. You'll be prompted for the certificate password. The Keychain Access utility will launch and display two new certificates in the Login keychain area:

MagooCA
MyMac2MagooVPN

I prefer to drag both of these (one at a time) the the System keychain so they can be shared with all users. When you do that, you will be pestered endlessly for the certificate password and your administrator credentials on the mac. Just persist.

The MagooCA certificate will have a red X on the icon because it's untrusted. To fix that, right-click on "CSparksCA"

Get Info -> Trust -> When using this certificate -> Always trust

You may close the Keychain utility.

Create a VPN connection

Run:

System Preferences -> Network -> "+"

PAY ATTENTION: You might think the Authentication Settings should be "Certificate." You thought wrong. After selecting "None", the window will refresh and display two radio buttons: "Shared Secret" and "Certificate":

In the future, you can connect or disconnect using the VPN icon on the task bar.


Appendix - Diagnostics

Configuration check:

/usr/libexec/ipsec/addconn --config /etc/ipsec.conf --checkconfig

Runtime configuration check:

ipsec verify

Check individual connection configuration:

ipsec auto --add magoo

Monitor ipsec startup and connection progress:

journalctl -f -t pluto

Check I/O counters

ipsec whack --trafficstatus

The trafficstatus command can be used to see if you're actually using the VPN:. Execute the command several times and confirm that the I/O counts are stationary. Then ping an internet site and run the command again. This time, the numbers should go up.

For a continuous view, create a new console window and run the command:

watch -n 1 ipsec whack --trafficstatus

That command will run every second so you can see the I/O counts changing while you browse the web or execute pings in another console window.


Appendix - Certificate operations

Using certutil and pk12util to manage keys

When working with NSS, define a shell variable:

export mynss=/var/lib/ipsec/nss

List all the certificates in the database

certutil -L -d $mynss

List details for a specific certificate

certutil -L -d $mynss -n "Nickname"

Change a certificate nickname

certutnil --rename -n OldNickname --new-n NewNickname -d /etc/ipsec.d

Delete a certificate

certutil -D -d $mynss -n "Nickname"

Export a client certificate in .p12 format:

pk12util -d $mynss -n "Hugh iPhone to MagooVPN" -o hugh_iphone.p12

The utility will prompt for a password.

Import a client certificate from a .p12 file:

pk12util -d $mynss -i robor_magoo.p12

The utitlity will prompt for the password specified when the certificate was exported.

Display contents of a .p12 file:

openssl pkcs12 -nokeys -info -in robor_magoo.p12

Export a CA certificate in .cer format: (Just the public part)

Some clients want the certificate authority certificate as well as the client certificate. (iPhone is an example.)

certutil -L -d $mynss -n "Magoo CA" -a -o magoo_ca.cer

Revoke a client certificate

If someone steals your phone or laptop that has a client certificate, they can access your office LAN if they guess or circumvent the user account login credentials.

The quick and easy thing to do is simply delete the client certificate from the store on the server. But if the phone or laptop is found, a new certificate will have to be installed at both ends.

A more sophisticated approach is to revoke the client certificate. A revoked certificate can be reinstated if the phone or laptop was simply misplaced.

Certificates are revoked by adding them to a certificate revocation list "CRL" - This is an optional feature that must be added to the certificate authority. You only need to do this once: the same CRL can be used to revoke any client certificate signed by the CA.

Create a CRL

For the "Magoo CA":

crlutil -G -d $mynss -n "Magoo CA" -c /dev/null

The output will show some information about the new CRL.

Don't accidentally create another CRL for the same CA: You can check for a CLR using the expression:

crlutil -G -d $mynss -n "Magoo CA" -c /dev/null

If there's no output, the CRL doesn't exist.

Get the certificate serial number

List the store and note the nickname of the client certificate you want to revoke:

certutil -L -d $mynss

Show the certifcate details and note the serial number:

certutil -L -d $mynss -n "Hugh iPhone"

The serial number appears in the first few lines of the dump. It will look like this:

...
Serial Number:
    00:b5:1a:f2:61

Convert the serial number to a decimal value:

echo $((b51af261))

Result: (example)

3038442081
Revoke the certificate

Specify the time you want the revocation to occur in generalized time format YYYYMMDDhhmmssZ - The time is in UTC and the Z at the end is required. Example:

20200624053200Z

Add the client certificate to the CRL:

crlutil -M -d $mynss -n "Magoo CA" <<EOF
addcert 3446275956 20200606220100Z
EOF

Tell ipsec to reload the CRL:

ipsec crls

The deed is done.

After a reasonable time, revoked certificates can be removed from the CRL (so it doesn't get too big) and simply deleted from the server certificate store. At that point there is no way to reinstate them.

Un-revoke a certificate

If you want to reinstate a certificate, simply remove it from the CRL. You need the serial number and the current time:

crlutil -M -d $mynss -n "Magoo CA" <<EOF
rmcert 3446275956 20200606220100Z
EOF

References