Run your own VPN with Libreswan
A Virtual Private Network (VPN) creates a unique, private network within a different network. This fact means that your users can work remotely from home or a different office location, but log in to your official LAN and use all of its services (file shares, printers, internal wikis, and so on) just as if they were physically sitting in the same room.
VPN is a generic term, and there are many different VPN software packages available. Red Hat Enterprise Linux 8 (RHEL 8) comes with the open source IPsec Libreswan software already installed. IPsec is a complex suite of protocols, but it mainly manages the moving of encrypted data between two peers.
On modern Linux, IPsec support is included in the kernel, so all you have to do to configure the tunnel is set up an encryption key, and define which IP addresses to protect on both hosts. For encryption keys to authenticate to one another, and for the algorithms to be negotiated, an Internet Key Exchange (IKE) daemon is required. The IKE resolves authentication and then dynamically generates the keys used by the kernel. It also sets an expiry time for those keys and generates new ones before old channels die, so data can continue to flow seamlessly.
Libreswan is an IKE daemon. It’s a peer-to-peer technology, so there isn’t a differentiation between client and server, which means that the Libreswan package provides everything you need to set it up. However, the
NetworkManager-libreswan package enables users running Linux to easily connect to your VPN in road warrior mode (asymmetric encryption intended for remote workers).
There are too many different ways to configure a network, a computer, and a VPN to cover in one article. This article introduces you to IPsec concepts and provides a basic configuration to create a VPN between two machines. After you successfully connect two hosts, you will have a working knowledge of how IPsec functions, and so you can adapt what you learned here to a more complex topography.
Securing your VPN
The general assumption when setting up a VPN is that you either have two or more separate networks separated by an untrusted network (usually the internet), or one network and one or more users separated by an untrusted network (usually the internet).
You would not have a maximum amount of security if a remote user (a road warrior in Libreswan terminology) logged on to your corporate network with no way of knowing whether the server being accessed was yours, or just the nearest server on the local network posing as your server. Likewise, you could not consider your network secure if there was no way for you to be sure that a user logging onto your network was a verifiable employee, or if they were just someone who got hold of a valid password.
Your choice of a VPN software should ensure that the gateways into each network can be verified as the correct and true gateways, and that remote users can be verified as authentic users. Libreswan provides mechanisms for these scenarios and more.
Going left or right?
The traditional concept of server and client doesn’t exist in IPsec. Generally, all setup commands must be run on both computers, whether they are two network gateways, or a gateway and a road warrior.
Libreswan uses the terms left and right to refer to each system involved in any given connection. Which system is left and which is right is completely arbitrary, as long as you stay consistent whilst configuring that particular connection. Use this convention when naming configuration files.
If you designated the Example, Inc. corporate gateway as left and the remote worker as right, then the configuration file might be named
example-road with the name of the corporation on the left of the dash symbol and the term
road on the right. Using consistent terms (such as the corporate name, or the city that the physical gateway is located in, and
warrior for the concept of remote workers) is also recommended.
First, if it’s not already installed, download and install Libreswan with your package manager. On RHEL 7, RHEL 8, and Fedora (
yum redirects to
dnf as needed):
$ sudo yum install libreswan
Libreswan uses a local database to keep track of authentication keys and identity certificates, so initialize the key database on each computer:
$ sudo ipsec initnss
If one already exists, Libreswan does not overwrite it, so it’s safe to run this command if you’re not sure. The database structure is stored in
/etc/ipsec.d, so you can check to see if they are already present.
On the other hand, if you want to start fresh no matter what, delete the .db files in
/etc/ipsec.d and then initialize new ones.
Generating RSA keys
Each gateway to each network must be able to verify the identity of the other. IPsec is a flexible system, so there are different options for authentication, but the default is public key authentication based on the asymmetric RSA algorithm, which you may also know from SSH keys.
Each RSA key is a matched pair: You generate a public key and a private key. The public key, you intentionally publicize so that it can be used to correctly identify your private key. The theory is that only one public key matches your private key, and only you have access to your private key, meaning that you can be identified by many copies of a public key because you own the one copy of your private key.
Generate a key for each machine:
$ sudo ipsec newhostkey --hostname `hostname`
If you are creating a VPN according to Federal Information Processing Standards (FIPS), you must secure your host key with a password using the
--password option (this article does not claim FIPS compliance, so seek professional consultation if you are unsure about FIPS requirements).
A host-to-host configuration confirms connectivity between two Libreswan endpoints and is simple to create. All you need are the host keys for each machine.
On the left machine, view the contents of the key database:
$ sudo ipsec showhostkey --list <1> RSA keyid: AwEAAaE88 ckaid: edf4de29e0c73d04df00c3c9a2425165f8717a08
Obtain the left key using the CKAID value:
$ sudo ipsec showhostkey \ --ckaid edf4de29e0c73d04df00c3c9a2425165f8717a08 \ --left | grep 'leftrsasigkey' leftrsasigkey=0sAwEAAaE88Kq7c5hi5KroZLXN/PHzojF0HH9\ y0y7qUWOa6g85eLCei8jtyGIWDNllaSaOkB+GR92bzn8XxhSaki\ [...] m9Zz2jPM6WSPiR9PbgN9pEu/wcTRzbLKtWcCaw==
leftrsasigkey is the left key.
On the right machine, view the contents of the key database:
$ sudo ipsec showhostkey --list <1> RSA keyid: AwEAAeOKP ckaid: 4b4ebf03adb048f6447f9f8047f843414a57a69f
Obtain the right key using the CKAID value:
$ sudo ipsec showhostkey \ --ckaid 4b4ebf03adb048f6447f9f8047f843414a57a69f \ --right | grep 'rightrsasigkey' rightrsasigkey=0sAwEAAeOKPkZXgLr56E9WIJZ7xd2zegbK553hP\ Q+BZipLe63iK4PHPPf/uecDvwl4XfCQG1eRH4ObwuDwzu8n11ZLz8a\ [...] /ef/W0xNwPOREgpunN9LEy9aLJLHHqGlOiaqFrQcACTFLh7sOi5E5P\ er7cnSIkmT8Dm8CZN4vYaDbGBaNsUFE8cdLLFDDzww==
rightrsasigkey is the right key.
The configuration file
The default configuration file for Libreswan is
/etc/ipsec.conf, which has some useful generic settings, plus a rule to include any modular configuration files found in the
/etc/ipsec.d/ directory. In this case, because authentication is the same on both sides, the configuration file is the same for both the left and right systems.
A configuration consists of a
conn definition, followed by key and value pairs. Empty lines define new sections, so if you want white space, use a
# comment character. First, create a
conn entry with a unique but sensible name, and then enter the public IP addresses of both the left and right machines. These IP addresses help Libreswan on each system determine whether it is the left or the right, so the IP addresses must be accurate and valid.
conn host-host left=192.168.1.16 right=10.1.1.8
Next, define the subnets accessible to each side. This step isn’t strictly required for a purely host-to-host configuration, but if you use public IP addresses for site connectivity, you must define which subnets the tunnel you’re creating may access:
Set what happens at IPsec startup. Valid options are
add to add a connection but not start it,
ondemand to load a connection,
start to add and start a connection, and
ignore to do nothing. The default is
ignore, so unless you set an automatic action, your configuration is dormant:
Each system requires both an RSA public key and a unique identifier. The RSA keys you have already generated and the unique identifiers are the names that each system uses during connection negotiation. As an identifier, you may use the machine’s IP address, a resolvable domain name, or a fully qualified domain name (FQDN) preceded by
@ to prohibit it from being resolved
It is a convention to use the @FQDN form because it is the most human-readable. Avoid using domain names from non-existent domains, domains you do not own, or domains that conflict with machine names in your domain. An example of a sensible naming scheme is to use DNS names for your gateways (
gateway.example.com, for example), and add a road label to identifiers for your road warriors (for instance,
Use your unique identifier scheme to invent the IDs, and use the output of
ipsec showhostkey (which you obtained earlier) to define the public keys (they keys in this example are truncated for readability; in the actual configuration, use the full keys):
email@example.com firstname.lastname@example.org leftrsasigkey=0sAwEAAeOKPkZXgLr56E9WIJZ7xd2zegbK553hP[...] TFLh7sOi5E5Pder7cnSIkmT8Dm8CZN4vYaDbGBaNsUFE8cdLLFDDzww== rightrsasigkey=0sAwEAAbVM04T2VAMHqI8hgWaWwoGXvD3fd/Ay[...] ZQyzFxurQ8zGZFyyyFotqSFw/qxn2icomAp8ByJrwCM1dwfNC3SN8XoU=
Finally, include some settings for the Dead Peer Detection (DPD) protocol:
dpddelay=5 dpdtimeout=30 dpdaction=restart
Your configuration is complete. Copy the file to both sides of the VPN, with the file name
Opening the firewall doors
At the very least, you must open ports 4500 and 500 (UDP), and protocols 50 and 51 on each machine. First, get your current active zone:
$ sudo firewall-cmd --get-active-zone libvirt interfaces: virbr1 virbr0 public interfaces: enp0s31f6
Open the appropriate ports and protocols in that zone. Add the
--permanent flag to make these changes persist:
$ sudo firewall-cmd --zone=trusted --add-port=500/udp --permanent $ sudo firewall-cmd --zone=trusted --add-port=4500/udp --permanent $ sudo firewall-cmd --zone=trusted --add-protocol=50 --permanent $ sudo firewall-cmd --zone=trusted --add-protocol=51 --permanent
Your network configuration may require further changes, depending on the firewalls you have in place at your organization. For more information, read my article on how to Secure your Linux network with firewall-cmd.
Starting and verifying IPsec
Now that you have a basic host-to-host connection configured, you can start the IPsec service. On both machines:
$ sudo systemctl start ipsec
You can verify that your computer is configured correctly for IPsec with the
ipsec verify command. This command reports a health check for the computer, alerting you of any potential problems that you may need to resolve before continuing. For instance:
$ sudo ipsec verify Verifying installed system and configuration files Version check and ipsec on-path [OK] Libreswan 3.25 (netkey) on 3.10.0-957.12.2.el7.x86_64 Checking for IPsec support in kernel [OK] NETKEY: Testing XFRM related proc values ICMP default/send_redirects [OK] ICMP default/accept_redirects [OK] XFRM larval drop [OK] Pluto ipsec.conf syntax [OK] Two or more interfaces found, checking IP forwarding [OK] Checking rp_filter [ENABLED] /proc/sys/net/ipv4/conf/enp0s31f6/rp_filter [ENABLED] rp_filter is not fully aware of IPsec and should be disabled Checking that pluto is running [OK] Pluto listening for IKE on udp 500 [OK] Pluto listening for IKE/NAT-T on udp 4500 [OK] Pluto ipsec.secret syntax [OK] Checking 'ip' command [OK] Checking 'iptables' command [OK] Checking 'prelink' command does not interfere with FIPS [OK] Checking for obsolete ipsec.conf options [OK]
In this example, there is a warning that
rp_filter is enabled, but should be disabled. Before continuing, you must disable it in whatever manner you use for kernel parameters. For instance, you might use sysctl to adjust it for your current login session:
$ sudo sysctl -w net.ipv4.conf.enp0s31f6.rp_filter=0
ipsec verify again, and repeat this troubleshooting process until every check returns
Verifying the VPN tunnel
With IPsec started on both sides, you have created a VPN tunnel, but it can be difficult to tell in this test environment. Install
tcpdump to monitor the tunnel’s activity. Using this command, with the
-i option set to the interface you want to monitor, you can view activity being broadcast over the UDP ports you permitted through your firewall:
$ tcpdump -n -i enp0s31f6 esp or udp port 500 or udp port 4500 23:54:50.696797 IP 10.1.1.8.isakmp > 192.168.1.16.isakmp: isakmp: phase 1 I ident 23:54:58.705814 IP 10.1.1.8.isakmp > 192.168.1.16.isakmp: isakmp: phase 1 I ident 23:55:14.719236 IP 10.1.1.8.isakmp > 192.168.1.16.isakmp: isakmp: phase 1 I ident
To stop monitoring, press Ctrl+C:
^C 98 packets captured 98 packets received by filter 0 packets dropped by kernel
If you see no output from
tcpdump, here are some troubleshooting tips:
- Check your logs with
journalctl --grep IPsecfor IPsec errors.
- Load your configuration with
ipsec auto --add host-host.confand then start it with
ipsec auto --up host-host.conf.
- Check your firewall settings on both systems, and any firewalls between the two systems.
ipsec verifyon both systems, especially if you have rebooted since the initial configuration.
Climbing onward and upward
You have now configured a basic VPN tunnel between two hosts. From this point, take a look at your actual network, determine how you want to route your tunnel, and then set about configuring the gateways involved. Once you have connectivity, move on to your preferred authentication scheme.
Libreswan can do everything from two-factor authentication to pre-shared keys (PSK), and it can use PAM, LDAP, OpenShift, Azure, and many other technologies to help you obtain the network layout you want. VPN is the backbone of the remote work craze. Take time to learn how to configure it and to secure it, and, as always, do it on open source.