How to Install and Configure OpenVPN Server on Ubuntu 22.04

A Virtual Private Network (VPN) allows you to access the internet by masking your location, allowing you to access the internet safely on untrusted networks and circumvent geographical restrictions and censorship. OpenVPN is an open-source Transport Layer Security (TLS) VPN solution to achieve this goal.

In our tutorial, we will install OpenVPN on a Ubuntu 22.04 server, configure it to be accessible from a client machine, and create a VPN connection between them to redirect all the traffic from the client through the OpenVPN server.

Prerequisites

  • A server running Ubuntu 22.04 supporting both IPv4 and IPv6 connections. We will refer to this as the OpenVPN server. The uncomplicated Firewall(UFW) is enabled and running on it.

  • A server running Ubuntu 22.04 supporting both IPv4 and IPv6 connections. We will set this up as a private Certificate Authority (CA), which we will refer to as the CA server.

  • A non-root user with sudo privileges on both OpenVPN and the CA server.

  • A client machine to connect to the OpenVPN server. You can use your local device as the client machine. OpenVPN has clients for Windows, Linux, macOS, Android, and iOS. You can use either of them to connect. We will use a Ubuntu 22.04 client PC for the tutorial.

  • Everything is updated on the OpenVPN and the CA server.

    $ sudo apt update && sudo apt upgrade
    

Step 1 - Setting up the CA server

A Certificate Authority (CA) is an entity responsible for issuing digital certificates to verify identities on the Internet. In this tutorial, we will use a standalone server as the private CA server which will validate the OpenVPN server and client certificates. Your CA server should not run any other services other than importing, signing, and validating certificates.

Step 1.1 - Install Easy-RSA

The first step is to install the easy-rsa set of scripts. easy-rsa is a Certificate Authority management tool used to generate a private key, and public root certificate.

$ sudo apt install easy-rsa

Step 1.2 - Create a Public Key Infrastructure Directory

The next step is to create a skeleton Public Key Infrastructure (PKI) on the CA server.

$ mkdir ~/easy-rsa

Create symbolic links pointing to the installed easy-rsa package files.

$ ln -s /usr/share/easy-rsa/* ~/easy-rsa/

Restrict access to the PKI directory.

$ chmod 700 /home/<username>/easy-rsa

Initialize the PKI.

$ cd ~/easy-rsa
$ ./easyrsa init-pki

You will get the following output.

init-pki complete; you may now create a CA or requests.
Your newly created PKI dir is: /home/<username>/easy-rsa/pki

Step 1.3 - Create a Certificate Authority

Before you can create your CA's private key and certificate, you need to configure the organization information for it. Create vars file to store the information inside the easy-rsa directory and open it for editing.

$ cd ~/easy-rsa
$ nano vars

Paste the following code in it.

set_var EASYRSA_REQ_COUNTRY    "US"
set_var EASYRSA_REQ_PROVINCE   "NewYork"
set_var EASYRSA_REQ_CITY       "New York City"
set_var EASYRSA_REQ_ORG        "Howtoforge"
set_var EASYRSA_REQ_EMAIL      "[email protected]"
set_var EASYRSA_REQ_OU         "Community"
set_var EASYRSA_ALGO           "ec"
set_var EASYRSA_DIGEST         "sha512"

Save the file by pressing Ctrl + X and entering Y when prompted.

Run the following command to create the root public and private key pair for your Certificate Authority.

$ ./easyrsa build-ca

You will be prompted to enter a passphrase for your key pair. Choose a strong passphrase, and note it for later. You will also be asked for the Common Name (CN) for your CA. You can enter any string but for simplicity stake, press ENTER to accept the default name.

Using SSL: openssl OpenSSL 3.0.2 15 Mar 2022 (Library: OpenSSL 3.0.2 15 Mar 2022)

Enter New CA Key Passphrase:
Re-Enter New CA Key Passphrase:
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/<username>/easy-rsa/pki/ca.crt

If you don't want to be prompted for a password every time you interact with your CA, you can use the following command instead.

$ ./easyrsa build-ca nopass

This will create two files:

  • ~/easy-rsa/pki/ca.crt is the CA's public certificate file. Every user and the OpenVPN server will need a copy of this file.
  • ~/easy-rsa/pki/ca.key is the private key used by the CA to sign certificates for the OpenVPN server and client. If an attacker gains access to your CA and, in turn, your ca.key file, you will need to destroy your CA. This is why your ca.key file should only be on your CA machine and that, ideally, your CA machine should be kept offline when not signing certificate requests as an extra security measure.

Step 2 - Installing OpenVPN and Easy-RSA on the OpenVPN server

Log in to your OpenVPN server and install OpenVPN and Easy-RSA packages.

$ sudo apt install openvpn easy-rsa

Create the directory ~/easy-rsa.

$ mkdir ~/easy-rsa

Create a symbolic link from the easy-rsa script that we installed just like on the CA server.

$ ln -s /usr/share/easy-rsa/* ~/easy-rsa/

Restrict access to the directory.

$ chmod 700 ~/easy-rsa

Step 3 - Creating a PKI for OpenVPN Server

Create a vars file inside the ~/easy-rsa directory to store the required information to create the PKI and open it for editing.

$ cd ~/easy-rsa
$ nano vars

Paste the following lines in it.

set_var EASYRSA_ALGO "ec"
set_var EASYRSA_DIGEST "sha512"

Since we are not using this server as the CA, these are the only values we need. This configures your OpenVPN & CA servers to use ECC which means when a client and server attempt to establish a shared symmetric key, they use Elliptic Curve algorithms to do their exchange. It is significantly faster than using plain Diffie-Hellman with the classic RSA algorithm since the numbers are much smaller and the computations are faster.

The next step is to create the PKI directory by using the init-pki option. Although you already ran this command on the CA server as part of the prerequisites, it’s necessary to run it here because your OpenVPN server and CA server have separate PKI directories.

$ ./easyrsa init-pki

The PKI on the OpenVPN server is used as a centralized place to store certificate requests and public certificates.

Step 4 - Create OpenVPN Server Certificate Request and Private Key

Switch to the ~/easy-rsa directory on the OpenVPN server.

$ cd ~/easy-rsa

The next step is to generate a private key and Certificate Signing Request (CSR) on your OpenVPN server. Run the easy-rsa command with the gen-req option followed by a Common Name (CN) for the server. For our tutorial, we will use server as the CN for the OpenVPN server. We will also use the nopass option to avoid any permission issues.

$ ./easyrsa gen-req server nopass

You will get the following output.

Using SSL: openssl OpenSSL 3.0.2 15 Mar 2022 (Library: OpenSSL 3.0.2 15 Mar 2022)
..........+.....+.+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*....................+.+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*..+..+......+.......+...+..+.+.....+....+..+...+............+.+...+.....+....+...........+......+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.....+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*...+...+......+.....+....+...........+...+..........+..+.+..+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*............+.+..+....+..............+.....................+....+......+..............+.+.....+....+............+..+............+....+..+...+.......+.....+....+...+..+.........+.+.........+..+...+.+..............+.+.........+...........+.+.....+.........+...+......+.+......+.....+..........+...+..............+.+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-----
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/<username>/easy-rsa/pki/reqs/server.req
key: /home/<username>/easy-rsa/pki/private/server.key

This creates a private key for the server and a certificate request file called server.req. Copy the server key to the /etc/openvpn/server directory.

$ sudo cp /home/<username>/easy-rsa/pki/private/server.key /etc/openvpn/server/

The Certificate Signing Request (CSR) is now ready for signing by the CA.

Step 5 - Signing the OpenVPN Server's CSR

The next step is to copy the CSR file to the CA server for signing. If your servers have password authentication enabled, you can simply use the following command to copy the file.

$ scp /home/username/easy-rsa/pki/reqs/server.req username@your_ca_server_ip:/tmp

If you don't want to use password authentication, you will need to generate an SSH keypair for each server, then add the OpenVPN Server’s public SSH key to the CA machine’s authorized_keys file and vice versa.

If you don't want to go through all this, you can simply copy the files. Open the file on the OpenVPN server, copy its contents, and then create the file on the CA server and paste the contents.

Log back into the CA Server, and switch to the ~/easy-rsa directory and import the CSR file.

$ cd ~/easy-rsa
$ ./easyrsa import-req /tmp/server.req server

You will get the following output.

Using SSL: openssl OpenSSL 3.0.2 15 Mar 2022 (Library: OpenSSL 3.0.2 15 Mar 2022)

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.

Sign the request using the following command. Since we are signing the OpenVPN server's CSR, we will use its Common Name (CN).

$ ./easyrsa sign-req server server

You will be prompted to verify if the request comes from a trusted source. Type yes then press ENTER key to confirm.

Using SSL: openssl OpenSSL 3.0.2 15 Mar 2022 (Library: OpenSSL 3.0.2 15 Mar 2022)


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 825 days:

subject=
    commonName                = server


Type the word 'yes' to continue, or any other input to abort.
  Confirm request details: yes

Next, you will be prompted for the CA private key passphrase which you set up earlier.

Using configuration from /home/<username>/easy-rsa/pki/easy-rsa-13476.9IC4QC/tmp.lPVwQo
Enter pass phrase for /home/<username>/easy-rsa/pki/private/ca.key:
40975B6A677F0000:error:0700006C:configuration file routines:NCONF_get_string:no value:../crypto/conf/conf_lib.c:315:group=<NULL> name=unique_subject
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 May  8 12:41:46 2025 GMT (825 days)

Write out database with 1 new entries
Data Base Updated

Certificate created at: /home/<username>/easy-rsa/pki/issued/server.crt

The resulting certificate contains the OpenVPN server's public encryption key as well as the signature from the CA server. Copy the certificates back to the OpenVPN server.

$ scp pki/issued/server.crt username@your_vpn_server_ip:/tmp
$ scp pki/ca.crt username@your_vpn_server_ip:/tmp

On your OpenVPN server, copy the files to the /etc/openvpn/server directory.

$ sudo cp /tmp/{server.crt,ca.crt} /etc/openvpn/server

Step 6 - Configure OpenVPN Cryptographic Material

We will add an extra shared secret key that the server and all clients will use with OpenVPN's tls-crypt directive. This ensures that the OpenVPN server is able to cope with unauthenticated traffic, port scans, and Denial of Service attacks. It also makes it harder to identify OpenVPN network traffic.

Switch to the ~/easy-rsa directory.

$ cd ~/easy-rsa

Generate the tls-crypt pre-shared key. This will create a file called ta.key

$ openvpn --genkey secret ta.key

Copy the ta.key to the /etc/openvpn/server directory.

$ sudo cp ta.key /etc/openvpn/server

Step 7 - Generate a Client Certificate and Key Pair

Create a directory to store the client certificate and key files.

$ mkdir -p ~/client-configs/keys

Restrict the permissions on the directory to protect it.

$ chmod -R 700 ~/client-configs

Next, switch to the ~/easy-rsa directory.

$ cd ~/easy-rsa

Generate a client key with client1 as the Common Name for the client. You can use any CN for the client.

$ ./easyrsa gen-req client1 nopass

Press ENTER to confirm the common name. You will get the following output.

Using SSL: openssl OpenSSL 3.0.2 15 Mar 2022 (Library: OpenSSL 3.0.2 15 Mar 2022)
.......+.+.....+.+........+.+...+...+........+....+..+....+.........+....................+......+.......+...+.....+.+.........+...............+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*............+......+.........+......+.+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*...+........+...+...................+...........+....+...........+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
..........+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*.+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*...........+......+.......+..+..........+.....+.......+.....+....+.....+....+...+.....+...+....+...........+....+...+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-----
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) [client1]:

Keypair and certificate request completed. Your files are:
req: /home/username/easy-rsa/pki/reqs/client1.req
key: /home/username/easy-rsa/pki/private/client1.key

Next, copy the client1.key file to the ~/client-configs/keys directory.

$ cp pki/private/client1.key ~/client-configs/keys/

Transfer the client1.req file to the CA server.

$ scp pki/reqs/client1.req username@your_ca_server_ip:/tmp

Log back into the CA server and import the server request.

$ cd ~/easy-rsa
$ ./easyrsa import-req /tmp/client1.req client1

You will get the following output.

Using SSL: openssl OpenSSL 3.0.2 15 Mar 2022 (Library: OpenSSL 3.0.2 15 Mar 2022)

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

Sign the request using the following command. We are using client as the request type.

$ ./easyrsa sign-req client client1

When prompted, enter yes to confirm to sign the request and that it came from a trusted source.

Using SSL: openssl OpenSSL 3.0.2 15 Mar 2022 (Library: OpenSSL 3.0.2 15 Mar 2022)


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 client certificate for 825 days:

subject=
    commonName                = client1


Type the word 'yes' to continue, or any other input to abort.
  Confirm request details: yes

You will be prompted for the CA passphrase.

Using configuration from /home/<username>/easy-rsa/pki/easy-rsa-13573.uI2Vi8/tmp.q1RnLo
Enter pass phrase for /home/<username>/easy-rsa/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:'client1'
Certificate is to be certified until May  8 12:50:23 2025 GMT (825 days)

Write out database with 1 new entries
Data Base Updated

Certificate created at: /home/username/easy-rsa/pki/issued/client1.crt

Transfer the created certificate back to the OpenVPN server.

$ scp pki/issued/client1.crt username@your_server_ip:/tmp

On the OpenVPN server, copy the client certificate to the ~/client-configs/keys directory.

$ cp /tmp/client1.crt ~/client-configs/keys/

Copy the ca.crt and ta.key files to the ~/client-configs/keys directory, and set appropriate permissions for your currently logged-in server.

$ cp ~/easy-rsa/ta.key ~/client-configs/keys/
$ sudo cp /etc/openvpn/server/ca.crt ~/client-configs/keys/
$ sudo chown username.username ~/client-configs/keys/*

Step 8 - Configure OpenVPN

Copy the sample server.conf file as a starting point to configure OpenVPN.

$ sudo cp /usr/share/doc/openvpn/examples/sample-config-files/server.conf /etc/openvpn/server/

Open the file for editing.

$ sudo nano /etc/openvpn/server/server.conf

Find the HMAC section of the file by looking for the tls-auth directive. Comment out the line by adding a semi-colon (;) at the beginning of the line. Add a new line below it as shown.

;tls-auth ta.key 0 # This file is secret
tls-crypt ta.key

Next, change the cryptographic cipher value by looking for the cipher lines. The default value is set to AES-256-CBC. Comment out the default value and add another line with the AES-256-GCM encryption which offers a better level of encryption, and performance as shown.

;cipher AES-256-CBC
cipher AES-256-GCM

Right below, add the auth directive to select the HMAC message digest algorithm.

auth SHA256

Since we are using Elliptic Curve Cryptography, we need to turn off the Diffie-Hellman encryption. Comment out the dh dh2048.pem line and add dh none below it.

;dh dh2048.pem
dh none

OpenVPN should run with no privileges once it has started. To enable this, find and uncomment the user nobody and group nobody lines and change them as shown.

user nobody
group nogroup

Ubuntu has nogroup Group and not nobody which is available on the CentOS distribution.

Redirect All Traffic through the VPN

The settings above create the VPN connection between the client and server, but won't force any connections to use the tunnel. To do so, start by finding the push "redirect-gateway def1 bypass-dhcp" line. This line tells the client to redirect all its traffic through the OpenVPN server. Uncomment the line to enable the functionality.

push "redirect-gateway def1 bypass-dhcp"

Find the dhcp-option section below this line. Remove the semi-colon from the beginning of both lines. This tells the client to use the OpenDNS resolvers.

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

Change Port and Protocol

OpenVPN uses port 1194 and the UDP protocol by default to accept client connections. You can change the port depending on your needs. If you are not hosting web content on your OpenVPN server, you can use port 443.

Find the line port 1194 and change its value.

# Optional!
port 443

Find the proto udp line and comment it out by adding a semi-colon in front of it. And, uncomment the proto tcp line by removing the semi-colon as shown.

proto tcp
;proto udp

Since we are using the TCP protocol, we need to change the value of the explicit-exit-notify directive from 1 to 0, as this directive is only used by UDP.

explicit-exit-notify 0

Point to Non-Default Credentials

If you selected a different name during the ./easy-rsa gen-req server command earlier, you need to modify the cert and the key lines so that they point to the appropriate .crt and .key files. Since we are using the default server name, the default value is correct.

cert server.crt
key server.key

Once finished, save the file by pressing Ctrl + X and entering Y when prompted.

Step 9 - Adjust OpenVPN Server Networking Configuration

The next step is to configure the server's networking configuration to enable OpenVPN to route traffic correctly. The first thing we need to configure is Port forwarding.

Open the /etc/sysctl.conf file for editing.

$ sudo nano /etc/sysctl.conf

Add the following line at the bottom of the file.

net.ipv4.ip_forward = 1

Save the file by pressing Ctrl + X and entering Y when prompted.

To read the file and load the new values for the current session, use the following command.

$ sudo sysctl -p
net.ipv4.ip_forward = 1

This configuration will route all web traffic from your client via your server’s IP address, and your client’s public IP address will effectively be hidden.

Step 10 - Configure Firewall

To allow OpenVPN through the firewall, you need to enable masquerading, an iptables concept that provides on-the-fly dynamic network address translation (NAT) to correctly route client connections.

Before opening the firewall configuration file to add the masquerading rules, first find the public network interface of your machine using the following command.

$ ip route list default

You will receive a similar output.

default via 64.225.64.1 dev eth0 proto static

This tells us that the interface name is eth0. Open the /etc/ufw/before.rules file for editing.

$ sudo nano /etc/ufw/before.rules

These rules are read and put in place before the conventional UFW rules are loaded. Add the following lines at the start of the file as shown.

#
# rules.before
#
# Rules that should be run before the ufw command line added rules. Custom
# rules should be added to one of these chains:
#   ufw-before-input
#   ufw-before-output
#   ufw-before-forward
#
 
# START OPENVPN RULES
# NAT table rules
*nat
:POSTROUTING ACCEPT [0:0]
# Allow traffic from OpenVPN client to eth0 (change to the interface you discovered!)
-A POSTROUTING -s 10.8.0.0/8 -o eth0 -j MASQUERADE
COMMIT
# END OPENVPN RULES
 
# Don't delete these required lines, otherwise there will be errors
*filter
. . .

Save the file by pressing Ctrl + X and entering Y when prompted.

Next, we need to configure UFW to allow forwarded packets by default. Open the /etc/default/ufw file for editing.

$ sudo nano /etc/default/ufw

Find the DEFAULT_FORWARD_POLICY directive and change its value from DROP to ACCEPT.

DEFAULT_FORWARD_POLICY="ACCEPT"

Save the file by pressing Ctrl + X and entering Y when prompted.

And finally, open port 443 which you configured earlier for the OpenVPN server.

$ sudo ufw allow 443/tcp

Disable and enable the firewall to apply the new configuration.

$ sudo ufw disable
$ sudo ufw enable

Step 11 - Start OpenVPN

Enable the OpenVPN service to start at boot.

$ sudo systemctl -f enable [email protected]

Start the OpenVPN service.

$ sudo systemctl start [email protected]

Check the status of the service.

$ sudo systemctl status [email protected]

You will receive a similar output.

? [email protected] - OpenVPN service for server
     Loaded: loaded (/lib/systemd/system/[email protected]; enabled; vendor preset: enabled)
     Active: active (running) since Fri 2023-02-03 13:40:28 UTC; 32s ago
       Docs: man:openvpn(8)
             https://community.openvpn.net/openvpn/wiki/Openvpn24ManPage
             https://community.openvpn.net/openvpn/wiki/HOWTO
   Main PID: 15492 (openvpn)
     Status: "Initialization Sequence Completed"
      Tasks: 1 (limit: 1116)
     Memory: 1.8M
        CPU: 29ms
     CGroup: /system.slice/system-openvpn\x2dserver.slice/[email protected]
             ??15492 /usr/sbin/openvpn --status /run/openvpn-server/status-server.log --status-version 2 --suppress-timestamps --config server.conf

Feb 03 13:40:28 vpnserver openvpn[15492]: Listening for incoming TCP connection on [AF_INET][undef]:443
Feb 03 13:40:28 vpnserver openvpn[15492]: TCPv4_SERVER link local (bound): [AF_INET][undef]:443
Feb 03 13:40:28 vpnserver openvpn[15492]: TCPv4_SERVER link remote: [AF_UNSPEC]
Feb 03 13:40:28 vpnserver openvpn[15492]: GID set to nogroup
Feb 03 13:40:28 vpnserver openvpn[15492]: UID set to nobody
Feb 03 13:40:28 vpnserver openvpn[15492]: MULTI: multi_init called, r=256 v=256
Feb 03 13:40:28 vpnserver openvpn[15492]: IFCONFIG POOL IPv4: base=10.8.0.4 size=62
Feb 03 13:40:28 vpnserver openvpn[15492]: IFCONFIG POOL LIST
Feb 03 13:40:28 vpnserver openvpn[15492]: MULTI: TCP INIT maxclients=1024 maxevents=1028
Feb 03 13:40:28 vpnserver openvpn[15492]: Initialization Sequence Completed

Step 12 - Create Client Configuration

Before testing a client, we need to create configuration files for the client we will use. Create a directory for storing client configuration files.

$ mkdir -p ~/client-configs/files

Copy the example client configuration file to the directory.

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

Open the configuration file for editing.

$ nano ~/client-configs/base.conf

Find the remote directive and set it to point to your OpenVPN Server's public IP address. Also, change the port to match the port you chose before.

. . .
# The hostname/IP and port of the server.
# You can have multiple remote entries
# to load balance between the servers.
remote your_server_ip 443
. . .

Set the protocol you chose earlier.

proto tcp

Uncomment the user and group directives by removing the semi-colon in front of them. Also, change the value of the group directive to nogroup.

# Downgrade privileges after initialization (non-Windows only)
user nobody
group nogroup

Find the ca, cert, and key directives and comment them out by putting a semi-colon in front of them. This is because we will add the certs and keys within the client configuration file.

# SSL/TLS parms.
# See the server config file for more
# description. It's best to use
# a separate .crt/.key file pair
# for each client. A single ca
# file can be used for all clients.
;ca ca.crt
;cert client.crt
;key client.key

Comment out the tls-auth directive as we will add the ta.key directly into the client configuration file.

# If a tls-auth key is used on the server
# then every client must also have the key.
;tls-auth ta.key 1

Match the cipher and auth settings that you set in the `/etc/openvpn/server/server.conf file.

cipher AES-256-GCM
auth SHA256

Add the key-direction directive and set it to 1 for the VPN to function correctly.

key-direction 1

Next, add a few commented out lines to handle different methods used by VPN clients for DNS resolution. Add the following set of lines for clients that don't use systemd-resolved but rely on the resolvconf utility to manage DNS.

; script-security 2
; up /etc/openvpn/update-resolv-conf
; down /etc/openvpn/update-resolv-conf

Add the following set of lines for clients that use systemd-resolved for DNS resolution.

; script-security 2
; up /etc/openvpn/update-systemd-resolved
; down /etc/openvpn/update-systemd-resolved
; down-pre
; dhcp-option DOMAIN-ROUTE .

Save the file by pressing Ctrl + X and entering Y when prompted.

Create a script to compile the base configuration with the relevant certificate, key, and encryption files, and then copy the generated configuration file into the ~/client-configs/files directory.

Create and open the file make_config.sh within the ~/client-configs directory.

$ nano ~/client-configs/make_config.sh

Paste the following code in it.

#!/bin/bash
 
# First argument: Client identifier
 
KEY_DIR=~/client-configs/keys
OUTPUT_DIR=~/client-configs/files
BASE_CONFIG=~/client-configs/base.conf
 
cat ${BASE_CONFIG} \
    <(echo -e '<ca>') \
    ${KEY_DIR}/ca.crt \
    <(echo -e '</ca>\n<cert>') \
    ${KEY_DIR}/${1}.crt \
    <(echo -e '</cert>\n<key>') \
    ${KEY_DIR}/${1}.key \
    <(echo -e '</key>\n<tls-crypt>') \
    ${KEY_DIR}/ta.key \
    <(echo -e '</tls-crypt>') \
    > ${OUTPUT_DIR}/${1}.ovpn

Save the file by pressing Ctrl + X and entering Y when prompted.

Make the file executable and restrict permissions to it.

$ chmod 700 ~/client-configs/make_config.sh

This script makes a copy of the base.conf file, collect all the certificate and key files, extract their content, append them to the base configuration file, and export all that to create a new client configuration file. Every time you add a new client, you need to generate new keys and certificates for it and then run this script to create a client configuration file.

We have already created the client certificate and key files in step 7. Let us create the configuration file for the same.

Switch to the ~/client-configs directory.

$ cd ~/client-configs

Run the script to make the client configuration file.

$ ./make_config.sh client1

This will create a file named client1.ovpn. Change the parameter if you want a different filename. Check the directory contents.

$ ls ~/client-configs/files
client1.ovpn

This is the configuration file you need to transfer to the client which will be then used to connect to the OpenVPN server. You can use any SFTP protocol/program to transfer the file to the client.

Step 13 - Install and Test Client Connection

For our tutorial, we are using a Ubuntu 22.04 client machine. Install OpenVPN via the following command.

$ sudo apt install openvpn

Check if your system is using systemd-resolved to manage DNS queries by running the following command.

$ cat /etc/resolv.conf

You will get a similar output.

Output
# This is /run/systemd/resolve/stub-resolv.conf managed by man:systemd-resolved(8).
# Do not edit.
. . .

nameserver 127.0.0.53
options edns0 trust-ad
search .

If the system is using systemd-resolved, then the IP address above will be 127.0.0.53. Also, the comment on top of the file will confirm the same. If the IP address is anything other than 127.0.0.53, then the system is not using systemd-resolved and you need to follow the steps mentioned later.

For Clients with systemd-resolved

Install the openvpn-systemd-resolved package which provides scripts that force systemd-resolved to use the VPN server for DNS.

$ sudo apt install openvpn-systemd-resolved

Open the Client configuration file for editing.

$ nano client1.ovpn

Uncomment the following lines in the file by removing the semi-colon in front of them.

script-security 2
up /etc/openvpn/update-systemd-resolved
down /etc/openvpn/update-systemd-resolved
down-pre
dhcp-option DOMAIN-ROUTE .

Save the file by pressing Ctrl + X and entering Y when prompted.

For Clients with update-resolv-conf

First, confirm that your distribution is using update-resolv-conf.

$ ls /etc/openvpn
client server update-resolv-conf

If your system contains the update-resolv-conf file, then open the Client configuration file for editing.

$ nano client1.ovpn

Uncomment the following lines in the file by removing the semi-colon in front of them.

script-security 2
up /etc/openvpn/update-resolv-conf
down /etc/openvpn/update-resolv-conf

If you are using a CentOS based distribution, change the group directive from nogroup to nobody.

group nobody

Save the file by pressing Ctrl + X and entering Y when prompted.

Connect Client

Run the following command to connect the client to the VPN server.

$ sudo openvpn --config client1.ovpn

You should get a similar output.

2023-02-06 15:18:37 OpenVPN 2.5.5 x86_64-pc-linux-gnu [SSL (OpenSSL)] [LZO] [LZ4] [EPOLL] [PKCS11] [MH/PKTINFO] [AEAD] built on Jul 14 2022
2023-02-06 15:18:37 library versions: OpenSSL 3.0.2 15 Mar 2022, LZO 2.10
2023-02-06 15:18:37 NOTE: the current --script-security setting may allow this configuration to call user-defined scripts
2023-02-06 15:18:37 Outgoing Control Channel Encryption: Cipher 'AES-256-CTR' initialized with 256 bit key
2023-02-06 15:18:37 Outgoing Control Channel Encryption: Using 256 bit message hash 'SHA256' for HMAC authentication
2023-02-06 15:18:37 Incoming Control Channel Encryption: Cipher 'AES-256-CTR' initialized with 256 bit key
2023-02-06 15:18:37 Incoming Control Channel Encryption: Using 256 bit message hash 'SHA256' for HMAC authentication
2023-02-06 15:18:37 TCP/UDP: Preserving recently used remote address: [AF_INET]64.225.66.226:443
2023-02-06 15:18:37 Socket Buffers: R=[131072->131072] S=[16384->16384]
2023-02-06 15:18:37 Attempting to establish TCP connection with [AF_INET]64.225.66.226:443 [nonblock]
2023-02-06 15:18:38 TCP connection established with [AF_INET]64.225.66.226:443
2023-02-06 15:18:38 TCP_CLIENT link local: (not bound)
2023-02-06 15:18:38 TCP_CLIENT link remote: [AF_INET]64.225.66.226:443
2023-02-06 15:18:38 NOTE: UID/GID downgrade will be delayed because of --client, --pull, or --up-delay
2023-02-06 15:18:38 TLS: Initial packet from [AF_INET]64.225.66.226:443, sid=b6459c4e 0e23d362
2023-02-06 15:18:38 VERIFY OK: depth=1, CN=Easy-RSA CA
2023-02-06 15:18:38 VERIFY KU OK
2023-02-06 15:18:38 Validating certificate extended key usage
2023-02-06 15:18:38 ++ Certificate has EKU (str) TLS Web Server Authentication, expects TLS Web Server Authentication
2023-02-06 15:18:38 VERIFY EKU OK
2023-02-06 15:18:38 VERIFY OK: depth=0, CN=server
2023-02-06 15:18:38 Control Channel: TLSv1.3, cipher TLSv1.3 TLS_AES_256_GCM_SHA384, peer certificate: 2048 bit RSA, signature: RSA-SHA256
2023-02-06 15:18:38 [server] Peer Connection Initiated with [AF_INET]64.225.66.226:443
2023-02-06 15:18:39 SENT CONTROL [server]: 'PUSH_REQUEST' (status=1)
2023-02-06 15:18:39 PUSH: Received control message: 'PUSH_REPLY,redirect-gateway def1 bypass-dhcp,dhcp-option DNS 208.67.222.222,dhcp-option DNS 208.67.220.220,route 10.8.0.1,topology net30,ping 10,ping-restart 120,ifconfig 10.8.0.6 10.8.0.5,peer-id 0,cipher AES-256-GCM'
2023-02-06 15:18:39 OPTIONS IMPORT: timers and/or timeouts modified
2023-02-06 15:18:39 OPTIONS IMPORT: --ifconfig/up options modified
2023-02-06 15:18:39 OPTIONS IMPORT: route options modified
2023-02-06 15:18:39 OPTIONS IMPORT: --ip-win32 and/or --dhcp-option options modified
2023-02-06 15:18:39 OPTIONS IMPORT: peer-id set
2023-02-06 15:18:39 OPTIONS IMPORT: adjusting link_mtu to 1626
2023-02-06 15:18:39 OPTIONS IMPORT: data channel crypto options modified
2023-02-06 15:18:39 Outgoing Data Channel: Cipher 'AES-256-GCM' initialized with 256 bit key
2023-02-06 15:18:39 Incoming Data Channel: Cipher 'AES-256-GCM' initialized with 256 bit key
2023-02-06 15:18:39 net_route_v4_best_gw query: dst 0.0.0.0
2023-02-06 15:18:39 net_route_v4_best_gw result: via 10.0.2.2 dev enp0s3
2023-02-06 15:18:39 ROUTE_GATEWAY 10.0.2.2/255.255.255.0 IFACE=enp0s3 HWADDR=08:00:27:84:e2:0b
2023-02-06 15:18:39 TUN/TAP device tun0 opened
2023-02-06 15:18:39 net_iface_mtu_set: mtu 1500 for tun0
2023-02-06 15:18:39 net_iface_up: set tun0 up
2023-02-06 15:18:39 net_addr_ptp_v4_add: 10.8.0.6 peer 10.8.0.5 dev tun0
2023-02-06 15:18:39 /etc/openvpn/update-systemd-resolved tun0 1500 1626 10.8.0.6 10.8.0.5 init
<14>Feb  6 15:18:39 update-systemd-resolved: Link 'tun0' coming up
<14>Feb  6 15:18:39 update-systemd-resolved: Adding DNS Routed Domain .
<14>Feb  6 15:18:39 update-systemd-resolved: Adding IPv4 DNS Server 208.67.222.222
<14>Feb  6 15:18:39 update-systemd-resolved: Adding IPv4 DNS Server 208.67.220.220
<14>Feb  6 15:18:39 update-systemd-resolved: SetLinkDNS(5 2 2 4 208 67 222 222 2 4 208 67 220 220)
<14>Feb  6 15:18:39 update-systemd-resolved: SetLinkDomains(5 1 . true)
2023-02-06 15:18:39 net_route_v4_add: 64.225.66.226/32 via 10.0.2.2 dev [NULL] table 0 metric -1
2023-02-06 15:18:39 net_route_v4_add: 0.0.0.0/1 via 10.8.0.5 dev [NULL] table 0 metric -1
2023-02-06 15:18:39 net_route_v4_add: 128.0.0.0/1 via 10.8.0.5 dev [NULL] table 0 metric -1
2023-02-06 15:18:39 net_route_v4_add: 10.8.0.1/32 via 10.8.0.5 dev [NULL] table 0 metric -1
2023-02-06 15:18:39 GID set to nogroup
2023-02-06 15:18:39 UID set to nobody
2023-02-06 15:18:39 WARNING: this configuration may cache passwords in memory -- use the auth-nocache option to prevent this
2023-02-06 15:18:39 Initialization Sequence Completed

Open another terminal to check the DNS settings.

$ systemd-resolve --status tun0

You will get the following output where you can see the DNS servers we configured before. This ensures that the VPN is working correctly.

Link 4 (tun0)
    Current Scopes: DNS
         Protocols: +DefaultRoute +LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
Current DNS Server: 208.67.222.222
       DNS Servers: 208.67.222.222 208.67.220.220
        DNS Domain: ~.

You can further verify the connection by visiting the URL https://whatismyip.com in your browser and you will see your IP address and the location of your OpenVPN server.

WhatisMyIP Output

You can also verify further by performing a standard test on https://www.dnsleaktest.com/ website.

DNS Leak Standard Test

You can see the OpenDNS hostnames which we configured before and the location of the server matches the location of the OpenVPN server.

The above command runs OpenVPN in the foreground which means the terminal will be blocked. To close the connection, you need to press Ctrl + C. However, you can run OpenVPN in the background as well. Use the following command to do that.

sudo openvpn --config client1.ovpn --daemon

To shut down the connection, find the process ID of the process.

$ ps aux | grep openvpn

You will get a similar output.

nobody      4357  0.3  0.0  13468  8432 ?        Ss   15:35   0:00 openvpn --config client1.ovpn --daemon
username    4406  0.0  0.0  17732  2432 pts/0    S+   15:35   0:00 grep --color=auto openvpn

As you can see, the process ID corresponding to the OpenVPN process is 4357. Shut down the VPN connection using the following command to kill the process.

$ sudo kill -9 4357

OpenVPN offers GUI clients for Windows, macOS, Android, and iOS platforms where you can import the client configuration file to connect.

Step 14 - Revoking Client Certificates

If you want to revoke a client's certificate to prevent access, you can do so by logging onto the CA server.

Switch to the ~/easy-rsa directory.

$ cd ~/easy-rsa

Run the script with the revoke option followed by the client name.

$ ./easyrsa revoke client1

You will be prompted if you want to revoke the certificate.

Please confirm you wish to revoke the certificate with the following subject:

subject=
    commonName                = client1


Type the word 'yes' to continue, or any other input to abort.
  Continue with revocation: yes
. . .
Revoking Certificate 8348B3F146A765581946040D5C4D590A
. . .

This revokes the certificate on the CA server. However, the OpenVPN server has no way to check the revocation status. For that, we need to generate a Certificate Revocation List (CRL) and transfer it to the OpenVPN server.

Generate the CRL.

$ ./easyrsa gen-crl

You will be prompted for your CA passphrase. The above command generates a crl.pem file.

Copy this file to the OpenVPN server.

$ scp ~/easy-rsa/pki/crl.pem username@your_server_ip:/tmp

Log back into the OpenVPN server and copy the CRL file to the /etc/openvpn/server directory.

$ sudo cp /tmp/crl.pem /etc/openvpn/server/

Open the OpenVPN server configuration file for editing.

$ sudo nano /etc/openvpn/server/server.conf

Add the following line at the bottom of the file. This instructs the OpenVPN server to check the revocation list for clients that have been restricted.

crl-verify crl.pem

Save the file by pressing Ctrl + X and entering Y when prompted.

Restart the OpenVPN server to apply the changes.

sudo systemctl restart [email protected]

The client cannot connect to the OpenVPN server anymore.

Conclusion

This concludes our tutorial on installing and configuring the OpenVPN server on a Ubuntu 22.04 server. We also connected the OpenVPN Linux client to the server. If you have any questions, post them in the comments below.

Share this page:

5 Comment(s)