Skip to content

OpenVPN management

Foreword

This document is a "quick/dumb" guide for quick but complete setup. If you want to know why we're doing most things, make sure to check this guide out, as it's the one I used.

This document will make use of 3 terminals:

  • A disconnected/local CA server
  • The OpenVPN server (world endpoint)
  • A "user" (OpenVPN client)

It'll be split into those 3 categories:

  • CA server management
  • OpenVPN server setup
  • Client management

For shell examples, every shell block will start with # <server>, where <server> is one of those 3 choices:

  • CA server will be CA
  • OpenVPN server will be VPN
  • Client will be CLIENT

Note that, if I moved into a folder I created during the guide, it'll be noted by suffixing CA/VPN/CLIENT with "in ./"

CA server management

Create a dedicated user with restricted access rights on a disconnected machine, for best security (also recommended to use LUKS-encrypted partitions to store generated keys).

From now on, I'll assume you're ready and in the user folder (or a folder in which you want to set up everything).

EasyRSA installation

We'll use EasyRSA for certificates management.

In that sense, we need to download it.

Head over to the EasyRSA project releases page and fetch the latest version.

At time of writing this guide, the version is 3.0.4, and I'm then running the following commands.

1
2
3
4
5
# CA
$ wget https://github.com/OpenVPN/easy-rsa/releases/download/v3.0.4/EasyRSA-3.0.4.tgz
$ tar xzf EasyRSA-3.0.4.tgz
# Moving the folder into a bearable name
$ mv EasyRSA-3.0.4 easyrsa

EasyRSA configuration

Now, we'll configure a bit EasyRSA on our CA server, to simplify our life.

1
2
3
4
5
# CA
$ cd easyrsa
$ cp vars.example vars
# Edit the vars file with whichever text editor you want to use
$ nano vars

The file is filled with a lot of documentation and configurations, but only a few are of interest for us.

Search for the following block, uncomment it and fill it out.

1
2
3
4
5
6
#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"

As an example, this is what my local setup would look like.

1
2
3
4
5
6
set_var EASYRSA_REQ_COUNTRY    "FR"
set_var EASYRSA_REQ_PROVINCE   "PACA"
set_var EASYRSA_REQ_CITY       "Sophia-Antipolis"
set_var EASYRSA_REQ_ORG        "Artemis Org."
set_var EASYRSA_REQ_EMAIL      "contact@fake-email.org"
set_var EASYRSA_REQ_OU         "Artemis Org. VPN Provider"

Save and close the file.

PKI Initialization

First step is now to initialize the public key infrastructure, most commonly called PKI.

1
2
# CA in ./easyrsa
$ ./easyrsa init-pki

CA certificate generation

Now, we'll generate the CA certificate.

There's an optional setting to not password-protect the CA certificate, but this is your most absolute certificate authority (as the name stands), so you must protect it as much as possible. In that sense, I'll omit it here.

1
2
# CA in ./easyrsa
$ ./easyrsa build-ca

It'll prompt you for a pass phrase, choose a strong one.

The script will generate two important files: ca.crt and ca.key.

This is pretty much it, your CA machine is ready to sign certificates!

OpenVPN server setup

We'll now start taking care of our OpenVPN server.

OpenVPN and EasyRSA installation

For OpenVPN, make sure your package list is up to date (apt update), then install openvpn.

1
2
# VPN
$ apt install openvpn

For EasyRSA, you'll have to repeat steps described in the previous part, CA server management > EasyRSA installation.

PKI Initialization

We'll also need to init a PKI on our OpenVPN server.

1
2
# VPN in ./easyrsa
$ ./easyrsa init-pki

Server keys generation

This section will do a bit of back-and-forth with the CA server, pay attention.

We'll now generate our server certificate/key pair.

Note that, unlike for the CA server, we won't set up a passphrase, to avoid permission issues on automated VPN service start (as the daemon manager can't unlock the certificate).

1
2
# VPN in ./easyrsa
$ ./easyrsa gen-req server nopass

This will create a private key and a certificate request file (we'll send to our CA machine to fulfill this certificate request).

You will already move the generated key to the openvpn configuration folder.

1
2
# VPN in ./easyrsa
$ mv pki/private/server.key /etc/openvpn/

Now, from our CA machine, let's grab the certificate request.

1
2
# CA
$ scp <server>:./path/to/easyrsa/pki/reqs/server.req /tmp/server.req

We'll then import it to be able to work with it, then sign it.

We'll provide server as first argument, this argument is either server or client, and is here to determine if the certificate will be made for an OpenVPN server, or a client (here, a server).

1
2
3
# CA in ./easyrsa
$ ./easyrsa import-req /tmp/server.req server
$ ./easyrsa sign-req server server

Type yes, then your CA passphrase on prompt, and that'll sign it.

You now only need to send it back to the VPN server, alongside a copy of the public CA certificate.

1
2
3
# CA in ./easyrsa
$ scp pki/issued/server.crt <server>:/tmp
$ scp pki/ca.crt <server>:/tmp

We'll now move those files into our OpenVPN configuration folder.

1
2
# VPN
$ mv /tmp/{server,ca}.crt /etc/openvpn/

We'll generate a solid Diffie-Hellmann key, and a HMAC signature, to strengthen the server's TLS integrity verification capabilities, before moving both generated files into the OpenVPN configuration folder.

1
2
3
4
# VPN in ./easyrsa
$ ./easyrsa gen-dh
$ openvpn --genkey --secret ta.key
$ mv ta.key dh.pem /etc/openvpn/

At this point, our server has all the necessary keys to function and sign clients.

OpenVPN configuration

We'll start by copying a provided sample config file as base config file.

1
2
3
# VPN in /etc/openvpn
$ cp /usr/share/doc/openvpn/examples/sample-config-files/server.conf.gz .
$ gzip -d server.conf.gz

Then open the configuration file (server.conf) in whichever text editor you want.

Make sure the tls-auth ta.key 0 line is uncommented (not prefixed by ;), then add key-direction 0 just below.

Make sure the cipher section (cipher AES-256-CBC) is uncommented, then add auth SHA256 below.

Search for the dh directive (line starting with dh), and make sure it points to the right file (the file name is relative to /etc/openvpn/).

Search for the cert and key directives and make sure they both point to the server certificate and key (the file name is relative to /etc/openvpn/).

Finally, check that user and group are both uncommented (if they're commented, remove the prefix ;).

This is enough to have a working OpenVPN server at which anyone can connect.

Optional: Configuration to redirect all client traffic through the VPN

The following configuration will have as effect to force everyone to go through the VPN for internet access.

Find the following line, and make sure it's uncommented (if not, uncomment it).

1
push "redirect-gateway def1 bypass-dhcp"

Just below, add the following two lines.

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

Those are the two nameservers every client will use, it's recommended for you to use your own choice, e.g. 1.1.1.1 (CloudFlare) or OpenNIC servers.

Adjust the VPN server's network configuration

Edit the /etc/sysctl.conf file.

Inside, make sure the line net.ipv4.ip_forward=1 is present and uncommented.

Once done, save and close, then hot-update the sysctl session.

1
2
# VPN
$ sysctl -p

Firewall configuration

Here, we'll use ufw, because it's easy to configure but quite powerful.

Before changing our firewall, the first step is to check the public network interface's name.

1
2
# VPN
$ ip route | grep default

You'll get a line similar to the following one.

1
default via 203.0.113.1 dev eth0 onlink

In this example, eth0 is our interface, you need to note that somewhere as we'll use it a few times.

Now's the time to set up our firewall. Open /etc/ufw/before.rules with your favourite text editor, and add the following configuration block to the top of the file.

/!\ Make sure to change <interface> with the public interface name you got when checking it above.

1
2
3
4
5
6
7
8
# START OPENVPN RULES
# NAT table rules
*nat
:POSTROUTING ACCEPT [0:0] 
# Allow traffic from OpenVPN client to <interface>
-A POSTROUTING -s 10.8.0.0/8 -o <interface> -j MASQUERADE
COMMIT
# END OPENVPN RULES

Save and close the file.

Now, we'll change our default policy for data forwarding to ACCEPT. For that, open the file /etc/default/ufw and set DEFAULT_FORWARD_POLICY to ACCEPT (DEFAULT_FORWARD_POLICY="ACCEPT").

Save and close the file.

Now, let's open ports for the OpenVPN server (if you changed ports, make sure to use the right port here).

1
2
# VPN
$ ufw allow 1194/udp

If you installed ufw during this guide, make sure to also open the OpenSSH port. Else, you'll lock yourself out!

The command is ufw allow OpenSSH.

We only need to restart UFW to be ready.

1
2
3
# VPN
$ ufw disable
$ ufw enable

OpenVPN service setup

We're finally ready to start OpenVPN on our server!

Here, I'm using systemd.

1
2
3
4
5
6
# VPN
$ systemctl start openvpn@server
$ systemctl status openvpn@server # is it running?
$ ip addr show tun0 # did openvpn create a network interface?
# If everything's green, enable it for system startup
$ systemctl enable openvpn@server

Client management

Now that both our CA and server are ready, it's finally time to get ready to connect to it!

This section will allow us to build a small configuration and script to easily create and manage users on our VPN server.

Script preparation

We'll create a folder which will contain our client certificates and configurations.

1
2
3
# VPN
$ mkdir -p client-configs/keys
$ mkdir client-configs/files

As this folder contains sensitive data, make sure to lock down its permissions.

1
2
# VPN
chmod -R 700 client-configs

Copy the server's certificate authority and ta.key files to client-configs/keys.

1
2
# VPN in ./client-configs
$ cp /etc/openvpn/{ta.key,ca.crt} keys/

Configuration template

We'll make a base configuration template for our client configurations.

1
2
# VPN in ./client-configs
$ cp /usr/share/doc/openvpn/examples/sample-config-files/client.conf base.conf

Open the file.

Seek the line starting with remote, and fill/replace it with

1
2
remote your_server_ip your_server_port
# e.g. "remote 10.10.10.10 1194"

Next, seek and uncomment user nobody and group nogroup by removing ; at the beginning of each line.

Find and comment the directives ca, cert and key, as we'll automatically fill them later on.

1
2
3
#ca ca.crt
#cert client.crt
#key client.key

Also, comment out tls-auth, as we'll also directly add the ta.key into our client's configuration file.

1
# tls-auth ta.key 1

Copy the cipher and auth settings from the server.conf file.

1
2
cipher AES-256-CBC
auth SHA256

Then, below, add the following line.

1
key-direction 1

Add the three following commented-out lines.

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

You'll only uncomment those lines if you're on a linux client which have a script at the given path (/etc/openvpn/update-resolv-conf).

Save and close the file.

Configuration builder script

Now, create and open a new file called client-configs/make_config.sh, and put the following content in it.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#!/bin/bash

# First argument: Client identifier

KEY_DIR=/path/to/client-configs/keys
OUTPUT_DIR=/path/to/client-configs/files
BASE_CONFIG=/path/to/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-auth>') \
    ${KEY_DIR}/ta.key \
    <(echo -e '</tls-auth>') \
    > ${OUTPUT_DIR}/${1}.ovpn

Make the file executable by running chmod u+x client-configs/make_config.sh.

Per-client configuration

Once you've followed the previous section, you can re-follow this section as much as you want, once per client.

This section will do a bit of back-and-forth with the CA server, pay attention.

The first step is to choose a serious and recognizable, but still basic, name for your client.

Examples:

  • nyx@nyx
  • my_android_phone

For this section, our client's name will be nyx@nyx.

1
2
3
# VPN in ./easyrsa
$ ./easyrsa gen-req nyx@nyx nopass
$ mv pki/private/nyx@nyx.key ../client-configs/keys/

Now, grab the certificate request on your CA server.

You'll import the certificate, sign it as client mode, then send it back to the VPN server.

1
2
3
4
5
# CA in ./easyrsa
$ scp <server>:pki/reqs/nyx@nyx.req /tmp
$ ./easyrsa import-req /tmp/nyx@nyx.req nyx@nyx
$ ./easyrsa sign-req client nyx@nyx
$ scp pki/issued/nyx@nyx.crt <server>:/tmp

Now you can grab back the client certificate and generate the .ovpn profile configuration file!

1
2
3
4
# VPN in ./client-configs
$ mv /tmp/nyx@nyx.crt keys/
$ ./make_config.sh nyx@nyx
# Will create a file at ./files/nyx@nyx.ovpn

Now all you have to do is to download this file on your client device, and install it on your OpenVPN client.

I won't enter into details for that, as it's basically a scp remote:client-configs/files/nyx@nyx.ovpn, then either a openvpn nyx@nyx.ovpn or a windows right-click > import.

Key revocation

In case you need to revoke a client certificate, take the following steps.

1
2
3
4
# CA in ./easyrsa
$ ./easyrsa revoke nyx@nyx # client name to revoke
$ ./easyrsa gen-crl
$ scp pki/crl.pem <server>:/tmp

Then, on your VPN server, update your crl.pem file and restart your VPN service.

1
2
# VPN in /etc/openvpn
$ mv /tmp/crl.pem .

If it's the first time you're revoking a key, edit the /etc/openvpn/server.conf file to make sure you're using this crl.pem file.

For that, add or uncomment the following line inside your file configuration

1
crl-verify crl.pem

Finally, restart your OpenVPN server.

1
2
# VPN
$ systemctl restart openvpn@server