First of all you need to rent any cheap VPS server from Data Center located in desired country. It could be any VPS, for example we tested it on AWS EC2 in us-east-1 Data Center (USA).

This guide is compatible with providing IPv6 protocol via tunnel out of the box: on each client it creates routes for both IPv4 and IPv6 addresses, however to use IPv6 you have to make sure your server has IPv6 address too.

On new host, install openvpn:

sudo apt install openvpn

Open file in editor:

sudo nano /etc/openvpn/server.conf

Paste/replace content with next:

port 888

# transmit encapsulated tunnel traffic over TCP or UDP? 
;proto tcp
proto udp

dev tun

# SSL/TLS root certificate (ca), certificate (cert), and private key (key).  
# Each client and the server must have their own cert and key file. 
# The server and all clients will use the same ca file.
# See the "easy-rsa" directory for a series of scripts for generating RSA certificates
# and private keys.  Remember to use a unique Common Name for the server
# and each of the client certificates.
ca keys/ca.crt
cert keys/server.crt

# This file should be kept secret
key keys/server.key  

# Diffie hellman parameters. we don't use it because use EC instead
dh none

# Network topology. Should be subnet (addressing via IP)
# unless Windows clients v2.0.9 and lower have to be supported 
# (then net30, i.e. a /30 per client). Defaults to net30 (not recommended)
topology subnet

# Configure server mode and supply a VPN subnet for OpenVPN to draw client addresses
# from. The server will take 10.8.0.1 for itself, the rest will be made available 
# to clients. Each client will be able to reach the server on 10.8.0.1. 
# Comment this line out if you are ethernet bridging. See the man page for more info.
server 10.8.0.0 255.255.255.0

# IPv6 subnet
server-ipv6 2001:db8:0:123::/64

crl-verify keys/crl.pem

# Push routes to the client to allow it to reach other private subnets behind
# the server.  Remember that these private subnets will also need to know to route 
# the OpenVPN client address pool (10.8.0.0/255.255.255.0) back to the OpenVPN server.
;push "route 192.168.10.0 255.255.255.0"
;push "route 192.168.20.0 255.255.255.0"

# To assign specific IP addresses to specific clients or if a connecting client 
# has a private subnet behind it that should also have VPN access, use subdirectory 
# "ccd" for client-specific configuration files (see man page for more info).


# Uncomment this directive if multiple clients # might connect with the same certificate/key # files or common names. This is recommended # only for testing purposes. For production use, # each client should have its own certificate/key # pair. # # IF YOU HAVE NOT GENERATED INDIVIDUAL # CERTIFICATE/KEY PAIRS FOR EACH CLIENT, # EACH HAVING ITS OWN UNIQUE "COMMON NAME", # UNCOMMENT THIS LINE OUT. duplicate-cn
# If enabled, this directive will configure # all clients to redirect their default # network gateway through the VPN, causing # all IP traffic such as web browsing and # and DNS lookups to go through the VPN # (The OpenVPN server machine may need to NAT # or bridge the TUN/TAP interface to the internet # in order for this to work properly). push "redirect-gateway ipv6 def1 bypass-dhcp" # The addresses below refer to the public DNS servers push "dhcp-option DNS 8.8.8.8" push "dhcp-option DNS 8.8.4.4"
push "dhcp-option DNS 2001:4860:4860:0:0:0:0:8888" push "dhcp-option DNS 2001:4860:4860:0:0:0:0:8844" # The keepalive directive causes ping-like messages to be sent back and forth over # the link so that each side knows when the other side has gone down. # Ping every 10 seconds, assume that remote peer is down if no ping received during # a 40 second time period. keepalive 10 40 # For extra security beyond that provided by SSL/TLS, create an "HMAC firewall" # to help block DoS attacks and UDP port flooding. Generate with: #   openvpn --genkey --secret ta.key # # The server and each client must have a copy of this key. # The second parameter should be '0' on the server and '1' on the clients. ;tls-auth ta.key 0 # This file is secret tls-crypt keys/ta.key # Select a cryptographic cipher. This config item must be same in client config cipher BF-CBC        # Blowfish (default)   ;cipher DES-EDE3-CBC  # Triple-DES ;cipher AES-256-GCM # AES 256 - for openvpn version 2.4+ auth SHA256 # Enable compression on the VPN link. If you enable it here, you must also # enable it in the client config file. comp-lzo # It's a good idea to reduce the OpenVPN daemon's privileges after initialization. # You can comment this out on Windows systems. user nobody group nogroup # The persist options will try to avoid accessing certain resources on restart # that may no longer be accessible because of the privilege downgrade. persist-key persist-tun # Output a short status file showing current connections, truncated # and rewritten every minute. status /tmp/openvpn-status.log 3 # Set the appropriate level of log file verbosity. # # 0 is silent, except for fatal errors # 4 is reasonable for general usage # 5 and 6 can help to debug connection problems # 9 is extremely verbose verb 3 tun-mtu             1500 mssfix             

If you prefer run tunnel over TCP instead of UDP change proto udp to proto tcp . But generally UDP is preferable because:

  1. In case of using TCP for tunnel, all TCP traffic on system will be wrapped in TCP twice causing overhead (slowing down all)
  2. When UDP is used for tunnel, system TCP reliability is still same: TCP retries work on top of underlinable protocol
  3. One reason you might want TCP is when provider in your country tries to recognize a VPN traffic. Even if you using custom port then still might catch it. However much less providers expect to see Openvpn over TCP 😉

Read carefully line by line config to understand which keys/certificates to create and how.

🔑 Adding EasyRSA and generating server key

Install easy RSA:

sudo apt install easy-rsa

Easy RSA installs to /usr/share, and we need to copy it to openvpn dir:

cd /etc/openvpn/
sudo cp -r /usr/share/easy-rsa/ .

Now let's start keys generation:

cd /etc/openvpn/easy-rsa
sudo cp vars.example vars
sudo nano vars

Find set_vars section which starts with:

set_var EASYRSA_REQ_COUNTRY

And change to your parameters (Company country, name, email address, for big companies it will help openvpn users find you as administrator). Also found ALGO and DIGEST vars and change them to:

set_var EASYRSA_ALGO "ec"
set_var EASYRSA_DIGEST "sha512"

EC means much faster Elliptic Curve algorithms used when establishing connection (Standard is Diffie-Hellman's classic RSA).

sudo ./easyrsa init-pki
⚠️ Init PKI removes pki folder and everything that was created there before

Generate pki/ca.crt:

sudo ./easyrsa build-ca nopass
🧠 nopass disables ca.crt file encryption: Certificate Authority (ca.crt) is used to sign other certificates and make them Authoritative. In theory CA could be generated on absolutely different host then your VPN server (and in different PKI environment as a consequence), but then, later, you should supply .req files to CA host, run special command to import them "./easyrsa import-req copied_server.req server" and sign certs there. However after this you will still have to pass ca.crt back to openvpn server so openvpn will be able to verify that they signed by CA provided. In this case we recommend remove nopass in build-ca command

Generate pki/server.key + reqs/server.req:

sudo ./easyrsa gen-req server nopass

Press enter every time when it asks for confirmation.

Sign the server certificate:

sudo ./easyrsa sign-req server server

Now copy signed server key and CA to new keys directory:

sudo mkdir /etc/openvpn/keys
sudo cp pki/private/server.key /etc/openvpn/keys/
sudo cp pki/issued/server.crt /etc/openvpn/keys/
sudo cp pki/ca.crt /etc/openvpn/keys/

Also generate TA:

sudo openvpn --genkey secret /etc/openvpn/keys/ta.key

Also generate cr.pem :

EASYRSA_CRL_DAYS=3650 sudo ./easyrsa gen-crl

And copy it to keys:

sudo cp pki/crl.pem /etc/openvpn/keys/
CRL file used to hold list of revoked certificates. What does revoke mean: since you issued certificate it will be valid until it's expiration time (10 years default). So this works like blacklist: your crl.pem will hold list of restricted certificates, but not generate it manually, check for "Revoke client" at bottom of this hint

Starting OpenVPN server

Start and enable service:

sudo systemctl start openvpn@server
sudo systemctl enable openvpn@server

Edit sysctl.conf:

sudo nano /etc/sysctl.conf

Find net.ipv4.ip_forward and net.ipv6.conf.all.forwarding = 1 and set both to 1. Add or edit these line (most likely they might be just commented):

net.ipv4.ip_forward=1
net.ipv6.conf.all.forwarding = 1

Apply instantly:

sudo sysctl -p

Now we need to find your primary adapter which is used in your current default route (network interface). So now all connections to the internet done via this adapter. Run:

ip route | grep default

Most often it called enpX or ethX or ensX like in my case:

Grepping primary network adapter
Grepping primary network adapter

Now edit the UFW before config:

sudo nano /etc/ufw/before.rules

If your before.rules If the file is empty then add next content "AS IS", otherwise (like on AWS EC2 instances often happens), just add it to the bottom. 

*nat is section name, COMMIT is the end of the section.  

If you already have such a section, you need to merge it by changing ens0 in a POSTROUTING to your adapter name.

# START OPENVPN RULES
# # NAT table rules
*nat
:POSTROUTING ACCEPT [0:0]
# Allow traffic from OpenVPN client to ADAPTER (change to the interface you discovered!)
-A POSTROUTING -s 10.8.0.0/8 -o ens0 -j MASQUERADE
COMMIT
# # END OPENVPN RULES1~
⚠️ Don't forget to replace ens0 in code above with your adapter e.g. ens2 

Also, add a MASQUERADE rule for ipv6 routing:

sudo nano /etc/ufw/before6.rules

Add next:

*nat
:POSTROUTING ACCEPT [0:0]

-A POSTROUTING -s 2001:db8:0:123::/64 -o ens5 -j MASQUERADE

# don't delete the 'COMMIT' line or these rules won't be processed
COMMIT

Also edit:

sudo nano /etc/default/ufw

Find and change DEFAULT_FORWARD_POLICY to accept:

DEFAULT_FORWARD_POLICY="ACCEPT"

Now apply UFW rules:

(if you configured drive tunnel in a config above over TCP then change 888/udp to 888/tcp)

sudo ufw allow 888/udp
sudo ufw allow OpenSSH
sudo ufw disable
sudo ufw enable
sudo ufw reload

Generating client's config and keys

To generate certificates for clients create this file:

sudo nano /etc/openvpn/build-client-key.sh

Change <YOUR_VPN_HOST_OR_IP> to your host or IP address. For example it could be 1.2.3.4 or vpn.host.com

Content:

#!/bin/bash

function build_client_key {
  CLIENT_NAME=$1
  pushd easy-rsa

  export KEY_NAME=$CLIENT_NAME
  echo -ne '\n' | ./easyrsa gen-req $CLIENT_NAME nopass
  echo -ne 'yes' | ./easyrsa sign-req client $CLIENT_NAME
  
  popd

  mkdir -p clients/$CLIENT_NAME/tun_$CLIENT_NAME

  mv easy-rsa/pki/issued/$CLIENT_NAME.crt clients/$CLIENT_NAME/tun_$CLIENT_NAME/
  mv easy-rsa/pki/private/$CLIENT_NAME.key clients/$CLIENT_NAME/tun_$CLIENT_NAME/
  cp keys/ca.crt clients/$CLIENT_NAME/tun_$CLIENT_NAME/
  cp keys/ta.key clients/$CLIENT_NAME/tun_$CLIENT_NAME/

  GREP_BEGIN_END='(?P<dashes>-+)\s?BEGIN\s(?P<name>.+)(?P=dashes)\s(.+\s)+?(?P=dashes)\s?END\s(?P=name)(?P=dashes)'


  cat << EOF > clients/$CLIENT_NAME/tun_$CLIENT_NAME.ovpn            
client
dev                 tun_$CLIENT_NAME
proto               udp
remote              <YOUR_VPN_HOST_OR_IP> 888  # Your IP or host here
cipher              BF-CBC
resolv-retry        infinite
nobind
persist-key
persist-tun
comp-lzo
mute-replay-warnings
verb                3
reneg-sec           900
mute                20
key-direction 1
#status             /var/log/tun_$CLIENT_NAME.status

<ca>
`cat clients/$CLIENT_NAME/tun_$CLIENT_NAME/ca.crt`
</ca>

<cert>
`cat clients/$CLIENT_NAME/tun_$CLIENT_NAME/$CLIENT_NAME.crt`
</cert>

<key>
`cat clients/$CLIENT_NAME/tun_$CLIENT_NAME/$CLIENT_NAME.key`
</key>

<tls-crypt>
`cat clients/$CLIENT_NAME/tun_$CLIENT_NAME/ta.key`
</tls-crypt>
EOF

  pushd clients
  # tar zcvf tun_$CLIENT_NAME.tar.gz $CLIENT_NAME
  popd
}

export -f build_client_key
bash -c "build_client_key $@"

Make file executable:

sudo chmod +x /etc/openvpn/build-client-key.sh

To create a client:

cd /etc/openvpn/
/etc/openvpn/build-client-key.sh johndoe

Where johndoe is your or your colleague which need to work through VPN server. Then find file .ovpn in clients folder:

ls clinets/johndoe

And transfer it from server to user via scp/ftp or in any other way you can to John.

This file already has all certificates built-in so it will be super-easy to transfer this simple text file, especially assuming the fact that in most operations systems .ovpn files could be easily imported in internal network managers.

Revoke client

Once johndoe lefts your organisation you will definitely want to revoke his certificate to restrict his access.

cd /etc/openvpn/easy-rsa/
sudo ./easyrsa revoke johndoe
sudo ./easyrsa gen-crl
sudo cp pki/crl.pem /etc/openvpn/keys/