How to setup a OpenVPN server on Ubuntu 20.04

Ubuntu 20.04 Focal Fossa is the last long term support of one of the most used Linux distributions. In this tutorial we will see how to use this operating system to create an OpenVPN server and how to create an .ovpn file we will use to connect to it from our client machine.

In this tutorial you will learn:

  • How to generate a Certificate Authority
  • How to generate server andl client certificate and key
  • How to sign a certificate with the Certificate Authority
  • How to create Diffie-Hellman parameters
  • How to generate a tls-auth key
  • How to configure the OpenVPN server
  • How to generate an .ovpn file to connect to the VPN
How to setup a OpenVPN server on Ubuntu 20.04

How to setup a OpenVPN server on Ubuntu 20.04

Software Requirements and Conventions Used

Software Requirements and Linux Command Line Conventions
Category Requirements, Conventions or Software Version Used
System Ubuntu 20.04 Focal Fossa
Software openvpn, ufw, easy-rsa
Other Root permissions to perform administrative tasks
Conventions # – requires given linux commands to be executed with root privileges either directly as a root user or by use of sudo command
$ – requires given linux commands to be executed as a regular non-privileged user

Scenario setup

Before proceeding with the actual VPN configuration, let’s talk about the conventions and setup we will adopt in this tutorial.

We will use two machines, both powered by Ubuntu 20.04 Focal Fossa. The first one, camachine will be used to host our Certificate Authority; the second, openvpnmachine will be the the one we will setup as the actual VPN server. It is possible to use the same machine for both purposes, but it would be less secure, since a person violating the server, could “impersonate” the Certificate Authority, and use it to sign unwanted certificates (the issue is particularly relevant only if you plan to have more than one server or if you plan to use the same CA for other purposes). To move files between one machine and the other we will use the scp (secure copy) command. The 10 main steps we will be perform are the following:

  1. Generation of the Certificate Authority;
  2. Generation of the server key and certificate request;
  3. Signing of the server certificate request with the CA;
  4. Generation of the Diffie-Hellman parameters on the server;
  5. Generation of tls-auth key on the server;
  6. OpenVPN configuration;
  7. Networking and firewall (ufw) configuration on the server;
  8. Generation of a client key and certificate request;
  9. Signing of the client certificate with the CA;
  10. Creation of the client .ovpn file used to connect to the VPN.

Step 1 – Generation of the Certificate Authority (CA)

The first step in our journey consists into the creation of the Certificate Authority on the dedicated machine. We will work as an unprivileged user to generate the needed files. Before we start, we need to install the easy-rsa package:

$ sudo apt-get update && sudo apt-get -y install easy-rsa

With the package installed, we can use the make-cadir command to generate a directory containing the needed tools and configuration files, in this case we will call it certificate_authority. Once created, we will move inside it:

$ make-cadir certificate_authority && cd certificate_authority


Inside the directory we will find a file called vars. In the file we can define some variables that will be used for the certificate generation. A commented set of these variables can be found at line 91 to 96. Just remove the comment and assign the appropriate values:

set_var EASYRSA_REQ_COUNTRY    "US"
set_var EASYRSA_REQ_PROVINCE   "California"
set_var EASYRSA_REQ_CITY       "San Francisco"
set_var EASYRSA_REQ_ORG        "Copyleft Certificate Co"
set_var EASYRSA_REQ_EMAIL      "me@example.net"
set_var EASYRSA_REQ_OU         "My Organizational Unit"

Once the changes are saved, we can proceed and generate the PKI (Public Key Infrastructure), with the following command which will create a directory called pki:

$ ./easyrsa init-pki

With the infrastructure in place, we can generate our CA key and certificate. After launching the command below, we will be asked to enter a passphrase for the ca key. We will need to provide the same password each time we will interact with the authority. A Common Name for the certificate should also be provided. This can be an arbitrary value; if we just press enter on the prompt, the default one will be used, in this case Easy-RSA CA:

$ ./easyrsa build-ca

Here is the output of the command:

Note: using Easy-RSA configuration from: ./vars

Using SSL: openssl OpenSSL 1.1.1d  10 Sep 2019

Enter New CA Key Passphrase:
Re-Enter New CA Key Passphrase:
Generating RSA private key, 2048 bit long modulus (2 primes)
..........+++++
....................................................................+++++
e is 65537 (0x010001)
Can't load /home/egdoc/certificate_authority/pki/.rnd into RNG
140296362980608:error:2406F079:random number generator:RAND_load_file:Cannot open file:../crypto/rand/randfile.c:98:Filename=/home/egdoc/certificate_authority/pki/.rnd
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Common Name (eg: your user, host, or server name) [Easy-RSA CA]:

CA creation complete and you may now import and sign cert requests.
Your new CA certificate file for publishing is at:
/home/egdoc/certificate_authority/pki/ca.crt

The build-ca command generated two files; their path, relative to our work directory are:

  • pki/ca.crt
  • pki/private/ca.key

The first is the public certificate, the second is the key that will be used to sign the server and clients certificates, so should be kept as safe as possible.

A little note, before we move forward: in the output of the command you may have noticed an error message. Although the error is not armful, a workaround to avoid it is to comment the third line of the openssl-easyrsa.cnf file which is inside the generated work directory. The issue is discussed on the openssl github repository. After the modification, the file should look like this:

# For use with Easy-RSA 3.1 and OpenSSL or LibreSSL

RANDFILE                = $ENV::EASYRSA_PKI/.rnd

This said, let’s move on the machine we will use as OpenVPN server and generate the server key and certificate.

Step 2 – Generation of the server key and certificate request

In this step we will generate the server key and the certificate request that will than signed by the certificate authority. On the machine we will use as OpenVPN server, we must install the openvpn, easy-rsa and ufw packages:

$ sudo apt-get update && sudo apt-get -y install openvpn easy-rsa ufw

To generate the server key and certificate request, we perform the same procedure we used on the machine hosting the Certificate Authority:

  1. We generate a working directory with the make-cadir command, and move inside it.
  2. Setup the variables contained in the vars file that will be used for the certificate.
  3. Generate the Public Key Infrastructure with the ./easyrsa init-pki command.

After these preliminary steps, we can issue the command to generate the server certificate and key file:

$ ./easyrsa gen-req server nopass

This time, since we used the nopass option, we will not be prompted to insert a password during the generation of the server key. We will be still asked to enter a Common Name for the server certificate. In this case the default value used is server. That’s what we will use in this tutorial:

Note: using Easy-RSA configuration from: ./vars

Using SSL: openssl OpenSSL 1.1.1d  10 Sep 2019

Generating a RSA private key
....................+++++
.................+++++
writing new private key to '/home/egdoc/openvpnserver/pki/private/server.key.9rU3WfZMbW'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Common Name (eg: your user, host, or server name) [server]:

Keypair and certificate request completed. Your files are:
req: /home/egdoc/openvpnserver/pki/reqs/server.req
key: /home/egdoc/openvpnserver/pki/private/server.key

A certificate sign request and a private key will be generated:

  • /home/egdoc/openvpnserver/pki/reqs/server.req
  • /home/egdoc/openvpnserver/pki/private/server.key.

The key file must be moved inside the /etc/openvpn directory:

$ sudo mv pki/private/server.key /etc/openvpn

The certificate request, instead, must be sent to the certificate authority machine, to be signed. We can use scp command to transfer the file:

$ scp pki/reqs/server.req egdoc@camachine:/home/egdoc/

Let’s move back to camachine and authorize the certificate.

Step 3 – Signing the server certificate with the CA

On the Certificate Authority machine we should find the file we copied in the previous step in the $HOME directory of our user:

$ ls ~
certificate_authority  server.req

The first thing we do is to import the certificate request. To accomplish the task, we use the import-req action of the easyrsa script. Its syntax its the following:

import-req <request_file_path> <short_basename>

In our case, this translates to:

$ ./easyrsa import-req ~/server.req server


The command will generate the following output:

Note: using Easy-RSA configuration from: ./vars

Using SSL: openssl OpenSSL 1.1.1d  10 Sep 2019

The request has been successfully imported with a short name of: server
You may now use this name to perform signing operations on this request.

To sign the request, we use the sing-req action, which takes the type of the request as first argument (server, in this case), and the short_basename we used in the previous command (server). We run:

$ ./easyrsa sign-req server server

We will be asked to confirm we want to sign the certificate and to provide the password we used for the Certificate Authority key. If everything goes as expected the certificate will be created:

Note: using Easy-RSA configuration from: ./vars

Using SSL: openssl OpenSSL 1.1.1d  10 Sep 2019


You are about to sign the following certificate.
Please check over the details shown below for accuracy. Note that this request
has not been cryptographically verified. Please be sure it came from a trusted
source or that you have verified the request checksum with the sender.

Request subject, to be signed as a server certificate for 1080 days:

subject=
    commonName                = server


Type the word 'yes' to continue, or any other input to abort.
  Confirm request details: yes
Using configuration from /home/egdoc/certificate_authority/pki/safessl-easyrsa.cnf
Enter pass phrase for /home/egdoc/certificate_authority/pki/private/ca.key:
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
commonName            :ASN.1 12:'server'
Certificate is to be certified until Mar 20 02:12:08 2023 GMT (1080 days)

Write out database with 1 new entries
Data Base Updated

Certificate created at: /home/egdoc/certificate_authority/pki/issued/server.crt

We can now delete the request file we previously transferred from the openvpnmachine. And copy the generated certificate back to our OpenVPN server, together with the CA public certificate:

$ rm ~/server.req
$ scp pki/{ca.crt,issued/server.crt} egdoc@openvpnmachine:/home/egdoc

Back on the openvpnmachine we should find the files on our home directory. We can now move them to /etc/openvpn:

$ sudo mv ~/{ca.crt,server.crt} /etc/openvpn

Step 4 – Diffie-Hellman parameters generation

The next step consists in the generation of a Diffie-Hellman parameters. The Diffie-Hellman key exchange is the method used to transfer crypto keys over a public, insecure channel. The command to generate the key is the following (it could take a while to finish):

$ ./easyrsa gen-dh

The key will be generated inside the pki directory as dh.pem. Let’s move it to /etc/openvpn as dh2048.pem:

$ sudo mv pki/dh.pem /etc/openvpn/dh2048.pem

Step 5 – Generation of the tls-auth key (ta.key)

To improve security, OpenVPN implements tls-auth. Quoting the official documentation:

The tls-auth directive adds an additional HMAC signature to all SSL/TLS handshake packets for integrity verification. Any UDP packet not bearing the correct HMAC signature can be dropped without further processing. The tls-auth HMAC signature provides an additional level of security above and beyond that provided by SSL/TLS. It can protect against:
– DoS attacks or port flooding on the OpenVPN UDP port.
– Port scanning to determine which server UDP ports are in a listening state.
– Buffer overflow vulnerabilities in the SSL/TLS implementation.
– SSL/TLS handshake initiations from unauthorized machines (while such handshakes would ultimately fail to authenticate, tls-auth can cut them off at a much earlier point).

To generate the tls_auth key we can run the following command:

$ openvpn --genkey --secret ta.key

Once generated, we move the ta.key file to /etc/openvpn:

$ sudo mv ta.key /etc/openvpn

Our server keys setup is now complete. We can proceed with the actual server configuration.

Step 6 – OpenVPN configuration

The OpenVPN configuration file doesn’t exist by default inside /etc/openvpn. To generate it, we use a template that ships with the openvpn package. Let’s run this command:

$ zcat \
  /usr/share/doc/openvpn/examples/sample-config-files/server.conf.gz \
  | sudo tee /etc/openvpn/server.conf > /dev/null

We can now edit the /etc/openvpn/server.conf file. The relevant parts are showed below. The first thing we want to do is to verify that the name of the keys and certificates referenced correspond to the ones we generated. If you followed this tutorial it should definitely be the case (lines 78-80 and 85):

ca ca.crt
cert server.crt
key server.key  # This file should be kept secret
dh dh2048.pem

We want to make the OpenVPN daemon run with low privileges, the nobody user and nogroup group. The relevant part of the configuration file is at lines 274 and 275. We just need to remove the leading ;:

user nobody
group nogroup

Another line we want to remove the comment from is 192. This will cause all clients to redirect their default gateway through the VPN:

push "redirect-gateway def1 bypass-dhcp"

Lines 200 and 201 to can also be used to enable the server to push specific DNS servers to clients. The ones in the configuration file are those provided by opendns.com:

push "dhcp-option DNS 208.67.222.222"
push "dhcp-option DNS 208.67.220.220"

At this point the /etc/openvpn directory should contain these files we generated:

/etc/openvpn
├── ca.crt
├── dh2048.pem
├── server.conf
├── server.crt
├── server.key
└── ta.key

Let’s make sure they are all owned by root:

$ sudo chown -R root:root /etc/openvpn

We can proceed to the next step: configuring the networking options.

Step 7 – setup networking and ufw

In order for our VPN to work, we need to enable IP forwarding on our server. To do it, we just uncomment line 28 from the /etc/sysctl.conf file:

# Uncomment the next line to enable packet forwarding for IPv4
net.ipv4.ip_forward=1

To reload the settings:

$ sudo sysctl -p


We also need to allow packet forwarding in the ufw firewall modifying the /etc/default/ufw file, and changing the DEFAULT_FORWARD_POLICY from DROP to ACCEPT (line 19):

# Set the default forward policy to ACCEPT, DROP or REJECT.  Please note that
# if you change this you will most likely want to adjust your rules
DEFAULT_FORWARD_POLICY="ACCEPT"

We now need to add the following rules to the beginning of the /etc/ufw/before.rules file. Here we are assuming the interface used for the connection is eth0:

*nat
:POSTROUTING ACCEPT [0:0]
-A POSTROUTING -s 10.8.0.0/8 -o eth0 -j MASQUERADE
COMMIT

Finally, we must allow incoming traffic for the openvpn service in the ufw firewall manager:

$ sudo ufw allow openvpn

At this point we can restart ufw for the changes to be applied. If your firewall was not enabled at this point, make sure the ssh service is always allowed, otherwise you may be cut out if you are working remotely.

$ sudo ufw disable && sudo ufw enable

We can now start and enable the openvpn.service at boot:

$ sudo systemctl restart openvpn && sudo systemctl enable openvpn

Step 8 – Generation of a client key and certificate request

Our server setup is now finished. The next step consists into the generation of the client key and certificate request. The procedure is the same we used for the server: we just use “client” as the name instead of “sever”, generate the key and the certificate request, then pass the latter to the CA machine to be signed.

$ ./easyrsa gen-req client nopass

Just as before, we will be asked to enter a common name. The following files will be generated:

  • /home/egdoc/openvpnserver/pki/reqs/client.req
  • /home/egdoc/openvpnserver/pki/private/client.key

Let’s copy the client.req to the CA machine:

$ scp pki/reqs/client.req egdoc@camachine:/home/egdoc

Once the file is copied, on camachine, we import the request:

$ ./easyrsa import-req ~/client.req client

Then, we sign the certificate:

$ ./easyrsa sign-req client client

After entering the CA password, the certificate will be created as pki/issued/client.crt. Let’s remove the request file and copy the signed certificate back to the VPN server:

$ rm ~/client.req
$ scp pki/issued/client.crt egdoc@openvpnmachine:/home/egdoc

For convenience, let’s create a directory to hold all client related stuff and move client key and certificate inside it:

$ mkdir ~/client
$ mv ~/client.crt pki/private/client.key ~/client

Good, we almost there. Now, we have to copy the client configuration template, /usr/share/doc/openvpn/examples/sample-config-files/client.conf inside the ~/client directory and modify it to suite our needs:

$ cp /usr/share/doc/openvpn/examples/sample-config-files/client.conf ~/client

Here is the lines we need to change in the file. At line 42 put the actual server IP or hostname in place of my-server-1:

remote my-server-1 1194

On lines 61 and 62 remove the leading ; character to downgrade privileges after initialization:

user nobody
group nogroup

On lines 88 to 90 and 108 we can see that the CA certificate, client certificate, client key and tls-auth key are referenced. We want to comment those lines, since we will put the actual content of the files between a pair of dedicate “tags”:

  • <ca></ca> for the CA certificate
  • <cert></cert> for the client certificate
  • <key></key> for the client key
  • <tls-auth></tls-auth> for the tls-auth key

Once the lines are commented, we append the following content at the bottom of the file:

<ca>
# Here goes the content of the ca.crt file
</ca>

<cert>
# Here goes the content of the client.crt file
</cert>

<key>
# Here goes the content of the client.key file
</key>

key-direction 1
<tls-auth>
# Here goes the content of the ta.key file
</tls-auth>


Once finished editing the file, we rename it with the .ovpn suffix:

$ mv ~/client/client.conf ~/client/client.ovpn

All that remain to do is to import the file in our client application to make it connect to our VPN. If we are using the GNOME desktop environment, for example, we can import the file from Network section of the control panel. In the VPN section just click on the + button, then on “import from file” to select and import the “.ovpn” file you previously transferred to your client machine.


gnome-vpn

GNOME interface to import .ovpn file

Conclusions

In this tutorial we saw how to create a working OpenVPN setup. We generated a Certificate Authority and used to sign server and client certificates we generated together with the corresponding keys. We saw how to configure the server and how to setup networking, allowing packet forwarding and performing the needed modifications to the ufw firewall configuration. Finally, we saw how to generate a client .ovpn file which can be imported from a client application in order to easily connect to our VPN. Enjoy!