Ubuntu 18.04 set up OpenVPN server with CA in Vbox

Mainly follow How To Set Up an OpenVPN Server on Ubuntu 18.04[1]

Environment

  • Server – Ubuntu 18.04 desktop named zed-ubuntu18
  • CA – Ubuntu 18.04 virtual machine in Vbox
  • Client – mac
  • Sever connect to a router while the route connecting to a modem

Configure CA

Why I use Vbox to build CA?

As CA, the most important attribution is security.

  • CA should be used only for signing request and should be shutdown for the rest of time.
  • CA should connect to network only for updating and installing necessary packages.

As for me, use another machine as CA is a waste of resource.
In other word, use virtual machine as CA is the most economic and secure method.

How CA communicates with Server?

CA needs to communicate with Server to import, sign requests and send back certificates. That means CA virtual machine needs a bidirectional communication.

There are several options:

  • ssh – needs to expose port, generate and transfer keys on both side, which is complicated to configure
  • drag and drop – GUI solution
  • shared folder – CLI solution

My decision is to use shared folder.

configure shared folder

on server, mkdir ca_shared

on Vbox setting, add this folder as shared folder.

on CA[2]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# need to specify the uid to your user and gid to your group, otherwise the mount device would belong to root:root
# That means you would have permission problem to access this shared folder
# When you import req from this root owned shared folder, you would see this error:

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

# Easy-RSA error:

# The input file does not appear to be a certificate request. Aborting import.
# Offending file: /home/akb/ca_shared/mac.req

# sudo umount ~/ca_shared
mkdir ca_shared
sudo mount -t vboxsf ca_shared ~/ca_shared -o uid=akb,gid=akb

Note: akb is my CA username

Install OpenVPN

On Server

1
2
sudo apt update
sudo apt install openvpn

Install EasyRSA

On Ubuntu 18.04 official package source, easy-rsa version is 2.2.2-2[3]
Here we would install easy-rsa version 3.0.4 from github.

On both Server and CA

1
2
3
4
wget https://github.com/OpenVPN/easy-rsa/releases/download/v3.0.4/EasyRSA-3.0.4.tgz -O EasyRSA.tgz && \
mkdir EasyRSA && \
tar xvf EasyRSA.tgz -C ~/EasyRSA --strip-components 1 && \
rm EasyRSA.tgz

Configure the EasyRSA Variables and Build the CA

On CA

Update variables for new certificates

1
2
3
cd ~/EasyRSA
cp vars.example vars
vim vars

Uncomment these lines and update values to whatever you’d prefer.
Of course you can use the default values and change nothing.

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"

Build the CA

1
2
3
4
5
# initiate the public key infrastructure
./easyrsa init-pki

# nopass-- don't use password to interact with CA
./easyrsa build-ca nopass

Two important files would be created — ca.crt and ca.key — which make up the public and private sides of an SSL certificate.

  • ca.crt is the CA’s public certificate file which, in the context of OpenVPN, the server and the client use to inform one another that they are part of the same web of trust and not someone performing a man-in-the-middle attack. For this reason, your server and all of your clients will need a copy of the ca.crt file.

  • ca.key is the private key which the CA machine uses to sign keys and certificates for servers and clients. If an attacker gains access to your CA and, in turn, your ca.key file, they will be able to sign certificate requests and gain access to your VPN, impeding its security. 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.

Create the Server Certificate, Key, and Encryption Files

On Server, generate server key and request file

1
2
3
4
5
6
7
8
9
10
11
# initiate the public key infrastructure
./easyrsa init-pki

# This will create a private key for the server and a certificate request file called server.req
./easyrsa gen-req zed-ubuntu18 nopass

# Copy the server key to the /etc/openvpn/ directory:
sudo cp ~/EasyRSA/pki/private/zed-ubuntu18.key /etc/openvpn/

# transfer the server req file to your CA machine:
cp zed-ubuntu18.req ~/ca_shared/

On CA, sign the request

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Using a secure method to transfer the server.req file to your CA machine:
cd ~/EasyRSA

# import request
./easyrsa import-req ~/ca_shared/zed-ubuntu18.req zed-ubuntu18

# sign request
./easyrsa sign-req server zed-ubuntu18

# Next, transfer the signed certificate back to your VPN server using a secure method:
cp pki/issued/zed-ubuntu18.crt ~/ca_shared/

# Also transfer the ca.crt file to your server as well
cp pki/ca.crt ~/ca_shared/

On Server, collect certificate, key and encryption files for OpenVPN Server

1
2
3
4
5
6
7
8
9
10
sudo cp ~/ca_shared/{server.crt,ca.crt} /etc/openvpn/

cd EasyRSA
# create a strong Diffie-Hellman key to use during key exchange
./easyrsa gen-dh
# generate an HMAC signature to strengthen the server's TLS integrity verification capabilities
openvpn --genkey --secret ta.key

sudo cp ~/EasyRSA/ta.key /etc/openvpn/
sudo cp ~/EasyRSA/pki/dh.pem /etc/openvpn/

Generate a Client Certificate and Key Pair

To streamlines the process of joining the VPN, generating client certificate and key pair on Server rather than on client machine.

On Server, Generate client key and request file

1
2
3
4
5
6
7
8
9
10
11
12
# to store the client certificate and key files
mkdir -p ~/client-configs/keys

# lock down its permissions for security
chmod -R 700 ~/client-configs

cd ~/EasyRSA
./easyrsa gen-req mac nopass

cp ~/EasyRSA/pki/private/mac.key ~/client-configs/keys/
# Next, transfer the req file to your CA machine using a secure method:
cp ~/EasyRSA/pki/reqs/mac.req ~/ca_shared

On CA, sign the request

1
2
3
4
5
6
cd EasyRSA
./easyrsa import-req ~/ca_shared/mac.req mac
./easyrsa sign-req client mac

# Transfer crt file back to the server:
cp ~/EasyRSA/pki/issued/mac.crt ~/ca_shared

On Server, collect certificate, key and encryption files for client

1
2
3
4
5
6
# copy the client certificate
cp ~/ca_shared/mac.crt ~/client-configs/keys/

# Next, copy the ca.crt and ta.key files to the /client-configs/keys/ directory as well:
sudo cp /etc/openvpn/ta.key ~/client-configs/keys/
sudo cp /etc/openvpn/ca.crt ~/client-configs/keys/

Configuring the OpenVPN Service

1
2
3
4
5
6
# copy a sample OpenVPN configuration file into the config directory and then extract it to use it as a basis for your setup:
sudo cp /usr/share/doc/openvpn/examples/sample-config-files/server.conf.gz /etc/openvpn/
sudo gzip -d /etc/openvpn/server.conf.gz

# modify config file
sudo vim /etc/openvpn/server.conf

change the file name accordingly

1
2
3
4
5
ca ca.crt
cert zed-ubuntu18.crt
key zed-ubuntu18.key # This file should be kept secret

dh dh.pem

The tutorial said that we should add this line key-direction 0

key-direction, and needs to be 0 for server and 1 for client.

but in the config file comment

1
2
3
# The second parameter should be '0'
# on the server and '1' on the clients.
tls-auth ta.key 0 # This file is secret

That means key-direction already set, so we do not need this duplicated line.[4]

1
2
3
4
5
6
cipher AES-256-CBC
# add an auth directive to select the HMAC message digest algorithm. For this, SHA256 is a good choice
auth SHA256

user nobody
group nogroup

(Optional) Push DNS Changes to Redirect All Traffic Through the VPN

The settings above will create the VPN connection between the two machines, but will not force any connections to use the tunnel. If you wish to use the VPN to route all of your traffic, you will likely want to push the DNS settings to the client computers.

There are a few directives in the server.conf file which you must change in order to enable this functionality. Find the redirect-gateway section and remove the semicolon “;” from the beginning of the redirect-gateway line to uncomment it:
sudo vim /etc/openvpn/server.conf

1
push "redirect-gateway def1 bypass-dhcp"

Just below this, find the dhcp-option section. Again, remove the “;” from in front of both of the lines to uncomment them:

1
2
3
4
5
6
push "dhcp-option DNS 208.67.222.222"
push "dhcp-option DNS 208.67.220.220"

# or use Google public DNS
# push "dhcp-option DNS 8.8.8.8"
# push "dhcp-option DNS 8.8.4.4"

This will assist clients in reconfiguring their DNS settings to use the VPN tunnel for as the default gateway.

Note: This is a tricky option. It would cause inconvenience to clients once they have a contradictory preference

  • Server push DNS Changes to Redirect All Traffic Through the VPN[5][6][7]
  • Server doesn’t push DNS Changes to Redirect All Traffic Through the VPN[8]

It is interesting that, when Server push DNS Changes to Redirect All Traffic Through the VPN, Ubuntu needs manually configuration to overwrite this setting while Mac can somehow automatically use this connection only for resources on its network

Adjusting the Server Networking Configuration

Enable IP forwarding

1
2
3
4
5
6
# vim /etc/sysctl.com
# Uncomment this line
# net.ipv4.ip_forward=1
# sudo sed -i '/net.ipv4.ip_forward=1/s/^/#/g' /etc/sysctl.conf
sudo sed -i '/net.ipv4.ip_forward=1/s/^#//g' /etc/sysctl.conf
sysctl -p

POSTROUTING chain – Forward OpenVPN traffic to outside

1
2
3
4
5
6
7
8
9
10
11
12
13
# Your public interface is the string found within this command’s output that follows the word "dev"
export interface=$(ip route | grep default | awk '{print $5}')

# sudo vim /etc/ufw/before.rules
sudo sed -i '10 a \
# 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 '"${interface}"' -j MASQUERADE \
COMMIT \
' /etc/ufw/before.rules

Note: 10.8.0.0/8 is the OpenVPN subnet, the default value is 10.8.0.0/24[9][10][11][12]

ufw rules config

1
2
3
4
5
6
7
8
# sudo vim /etc/default/ufw
# DEFAULT_FORWARD_POLICY="ACCEPT"
sudo sed -i '/DEFAULT_FORWARD_POLICY/s/DROP/ACCEPT/' /etc/default/ufw

sudo ufw allow 1194/udp
sudo ufw allow OpenSSH
sudo ufw disable
sudo ufw enable

Starting and Enabling the OpenVPN Service

1
2
3
4
5
6
sudo systemctl start openvpn@server
sudo systemctl enable openvpn@server

# check
sudo systemctl status openvpn@server
ip addr show tun0

Port forwarding

I mentioned that I have double routers in section Environment.
So I have to set up port forwarding on both router and modem.[13]

  • on modem, set router as DMZ or forwarding port 1194 to router-ip:1194
  • on router, forwading port 1194 to Server-ip:1194

Note: Actually, setting up the router as DMZ means to forward all the router port. In other word, totally expose the router to public

Creating the Client Configuration Infrastructure

1
2
3
4
5
6
# To store client configuration files
mkdir -p ~/client-configs/files

# modify client config file
cp /usr/share/doc/openvpn/examples/sample-config-files/client.conf ~/client-configs/base.conf
# vim ~/client-configs/base.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
proto udp

remote your_server_ip 1194

user nobody
group nogroup

# comment out these lines since we would use script to copy paste these files' content
# ca ca.crt
# cert client.crt
# key client.key

# tls-auth ta.key 1
# since we comment out the above line, we need to add this line:
key-direction 1

cipher AES-256-CBCcipher AES-256-CBC
auth SHA256auth SHA256

# you only need to enable them for Linux clients that ship with an /etc/openvpn/update-resolv-conf file.
# This script uses the resolvconf utility to update DNS information for Linux clients.

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

vim ~/client-configs/make_config.sh write a script that will compile your base configuration with the relevant certificate, key, and encryption files and then place the generated configuration in ~/client-configs/files

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=~/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-auth>') \
${KEY_DIR}/ta.key \
<(echo -e '</tls-auth>') \
> ${OUTPUT_DIR}/${1}.ovpn

lock down its permissions for security
chmod 700 ~/client-configs/make_config.sh

Generating Client Configurations

1
2
cd ~/client-configs
sudo ./make_config.sh mac

This would generate the ovpn file for client.

Issues

Job for openvpn@server.service failed because the control process exited with error code

check the log sudo tail -f /var/log/syslog
make sure the server crt, key, CA crt, ta.key, dh.pem in /etc/openvpn, and the correct file name set in server.conf

Reference

[1] https://www.digitalocean.com/community/tutorials/how-to-set-up-an-openvpn-server-on-ubuntu-18-04
[2] https://help.ubuntu.com/community/VirtualBox/SharedFolders
[3] https://packages.ubuntu.com/bionic/easy-rsa
[4] https://stosb.com/blog/explaining-my-configs-openvpn/
[5] https://community.openvpn.net/openvpn/wiki/IgnoreRedirectGateway
[6] https://www.lshell.com/2015/06/openvpn.html
[7] https://bbs.deepin.org/forum.php?mod=viewthread&tid=155262
[8] https://askubuntu.com/questions/462533/route-all-traffic-through-openvpn
[9] https://community.openvpn.net/openvpn/wiki/HOWTO#Editingtheserverconfigurationfile
[10] https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing#IPv4_CIDR_blocks
[11] https://forums.openvpn.net/viewtopic.php?t=19289
[12] https://www.zhihu.com/question/56895036
[13] https://zhuanlan.zhihu.com/p/30242500
[14] http://vvtommy.github.io/blog/2012/12/01/deploy-openvpn-and-manage-system-on-ubuntu/
[15] http://www.zengyilun.com/openvpn/