Stunnel + OpenVPN Server on Ubuntu 18.04

My documented journey as I go through the entire process of setting up Stunnel + OpenVPN on Ubuntu 18.04.

Image for post
Image for post

Pre-requisites

The article assumes you already have a Ubuntu 18.04 machine setup somewhere, maybe with one of the cloud providers.

If you are looking to install this on Ubuntu 16.04, look at my previous article. There are quite a number of differences, so if you are looking to use this on 18.04, read on.

Installing OpenVPN and Stunnel

We will be installing these applications. OpenVPN and Stunnel from package repository. Also, we are going to update the latest version of OpenVPN

$ sudo -s$ apt-get install openvpn stunnel4$ wget -O - https://swupdate.openvpn.net/repos/repo-public.gpg|apt-key add -$ echo "deb http://build.openvpn.net/debian/openvpn/stable bionic main" > /etc/apt/sources.list.d/openvpn-aptrepo.list$ apt-get update 
$ apt-get upgrade

Manually Install Latest OpenSSL

So before we go any further, we are going to install the latest version of OpenSSL and unlink our current version of OpenSSL that we have.

1. Download and Unpack

We will build openssl in the /opt folder and unlink the original openssl later.

$ cd /opt$ mkdir openssl$ wget https://www.openssl.org/source/openssl-1.1.1d.tar.gz$ tar xfvz openssl-1.1.1d.tar.gz --directory /opt/openssl$ rm -rf /opt/openssl-1.1.1d.tar.gz

2. Prepare configurations

First check that you have the perl, if not install it.

$ perl --versionThis is perl 5, version 26, subversion 1 (v5.26.1) built for x86_64-linux-gnu-thread-multi
(with 67 registered patches, see perl -V for more detail)
Copyright 1987-2017, Larry WallPerl may be copied only under the terms of either the Artistic License or the
GNU General Public License, which may be found in the Perl 5 source kit.
Complete documentation for Perl, including FAQ lists, should be found on
this system using "man perl" or "perldoc perl". If you have access to the
Internet, point your browser at http://www.perl.org/, the Perl Home Page.

Next, export LD_LIBRARY_PATH .

export LD_LIBRARY_PATH=/opt/openssl/lib

Now, we will run the configuration script

$ cd /opt/openssl/openssl-1.1.1d$ ./config --prefix=/opt/openssl --openssldir=/opt/openssl/ssl

If everything well you should see something close the the following.

Operating system: x86_64-whatever-linux2
Configuring OpenSSL version 1.1.1d (0x1010104fL) for linux-x86_64
Using os-specific seed configuration
Creating configdata.pm
Creating Makefile
**********************************************************************
*** ***
*** OpenSSL has been successfully configured ***
*** ***
*** If you encounter a problem while building, please open an ***
*** issue on GitHub <https://github.com/openssl/openssl/issues> ***
*** and include the output from the following command: ***
*** ***
*** perl configdata.pm — dump ***
*** ***
*** (If you are new to OpenSSL, you might want to consult the ***
*** ‘Troubleshooting’ section in the INSTALL file first) ***
*** ***
**********************************************************************

3. Install and Test

Once you have completed configuring, proceed to run the next few commands. Ensure that you have successfully ran each command without error before proceed to the next command. All test should pass before running make install .

$ make$ make test$ make install

Your passed tests should look like the following

...
All tests successful.
Files=155, Tests=1456, 67 wallclock secs ( 1.14 usr 0.10 sys + 55.00 cusr 7.08 csys = 63.32 CPU)
Result: PASS
make[1]: Leaving directory ‘/opt/openssl/openssl-1.1.1d’

4. Relinking OpenSSL

Once installation has completed, we need to unlink our current version of OpenSSL.

// Rebuilds library cache
$ updatedb
$ locate openssl | grep /opt/openssl/bin
Image for post
Image for post
Shows where the openssl binaries are located

Now, we proceed to move the existing OpenSSL.

$ cd /usr/bin// if there is an openssl, rename it
$ ls -l openssl
$ mv openssl openssl.old

We need to finally add our new openssl to the path environment. To do that we’ll add a openssl.sh file in the /etc/profile.d directory.

$ touch /etc/profile.d/openssl.sh
$ nano /etc/profile.d/openssl.sh

And the contents of the file would be,

#!/bin/sh
export PATH=/opt/openssl/bin:${PATH}
export LD_LIBRARY_PATH=/opt/openssl/lib:${LD_LIBRARY_PATH}

Finally, source the file

$ chmod +x /etc/profile.d/openssl.sh$ source /etc/profile.d/openssl.sh

Be sure to check that everything is setup correctly by rebooting the server. and check the openssl and paths.

// Should contain /opt/openssl/bin
$ echo $PATH
// Should show /opt/openssl/bin/openssl
$ which openssl
// should show OpenSSL 1.1.1d 10 Sep 2019
$ openssl version

Done, now let’s move on to setup of the OpenVPN server using Easyrsa.

Setup OpenVPN Server with Easyrsa

Ok, I promise to be quick here. So here are all the steps we need to get done.

1. Install easyrsa tools

I chose to use a specific version of the easyrsa tool so I will be installing easyrsa manually as opposed to installing it from the package repository.

The reason behind this choice was because easyrsa together with certain versions of OpenSSL resulted in errors with encryption keys or randomizing vectors not found. This version of easyrsa worked for me after trial and error a couple of different versions.

$ cd /opt$ wget https://github.com/OpenVPN/easy-rsa/releases/download/v3.0.4/EasyRSA-3.0.4.tgz --no-check-certificate$ tar xvf EasyRSA-3.0.4.tgz$ rm -rf EasyRSA-3.0.4.tgz$ mv EasyRSA-3.0.4 easyrsa_3_0_4

2. Initialize all variables for easyrsa

Now you should already have the tool ready, now we have to configure the tool with some of the default options.

First, copy over the example vars file.

$ cd /opt/easyrsa_3_0_4$ cp vars.example vars$ nano vars

We then need to uncomment the next lines of options and set them as needed.

set_var EASYRSA_REQ_COUNTRY     "US"
set_var EASYRSA_REQ_PROVINCE "California"
set_var EASYRSA_REQ_CITY "San Francisco"
set_var EASYRSA_REQ_ORG "Something Company Organization"
set_var EASYRSA_REQ_EMAIL "your@company.email"
set_var EASYRSA_REQ_OU "Organization Unit Name"

Finally to before we initialize the variables, lets comment one line out from the openssl-easyrsa.cnf file.

$ nano openssl-easyrsa.cnf

Comment out the RANDFILE

# RANDFILE = $ENV::EASYRSA_PKI/.rnd

This should fix the problems when you are trying to run build-ca later with the following error

error:2406F079:random number generator:RAND_load_file:Cannot open file:../crypto/rand/randfile.c:88:Filename=/opt/easyrsa_3_0_4/pki/.rnd

Initialize the tool with the options you configured.

$ ./easyrsa init-pki

If everything worked as expected, you should see the following message

Note: using Easy-RSA configuration from: ./varsinit-pki complete; you may now create a CA or requests.
Your newly created PKI dir is: /opt/easyrsa_3_0_4/pki

3. Build Certificate Authority

To generate Certificate Authority, use the following command, the default name is fine, or if you want you can use any name for “Common Name”

$ ./easyrsa build-ca nopass

4. Generate and Sign Server keys

Note that the format “server” here means the filename and the hostname. You can set this as anything, but you would have to change accordingly for the next steps, in this case, we’ll just use server.

$ ./easyrsa gen-req server nopass

Finally check that no errors have been reported before proceeding. Something that is without errors would look like,

Note: using Easy-RSA configuration from: ./vars
Generating a RSA private key
……….+++++
……………………………………………………+++++
writing new private key to ‘/opt/easyrsa_3_0_4/pki/private/server.key.NlR0LUfTzS’
— — -
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: /opt/easyrsa_3_0_4/pki/reqs/server.req
key: /opt/easyrsa_3_0_4/pki/private/server.key

Next, let’s have the request signed. Note that the first “server” argument is the request type. The second is the filename. Change the second argument if you made some changes in the earlier step.

$ ./easyrsa sign-req server server

Note: While the recommended approach is to have a CA server and OpenVPN server separate, I’ll keep this more concise by keeping everything on one server, if you’re interested to setup your CA and OpenVPN server separately, look at the links at the end of this article from Digital Ocean.

Once completed, you should have your self-signed server certificates.

5. Generate Diffie-Hellman Key and HMAC Signature

Generate the Diffie-Hellman key with the following command

$ ./easyrsa gen-dh

The output should be similar to the following

Note: using Easy-RSA configuration from: ./vars
Generating DH parameters, 2048 bit long safe prime, generator 2
This is going to take a long time
........................................................................................................................++*++*++*++*
DH parameters of size 2048 created at /opt/easyrsa_3_0_4/pki/dh.pemDH parameters of size 2048 created at /opt/easyrsa_3_0_4/pki/dh.pem

Next, generate HMAC signature with

$ openvpn --genkey secret ta.key

6. Copy keys

Finally copy everything we generated to the openvpn folder. We will need these files when we configure the OpenVPN server.conf .

$ mkdir /etc/openvpn/server/keys
$ cd /opt/easyrsa_3_0_4
$ cp pki/ca.crt /etc/openvpn/server/keys/
$ cp pki/private/server.key /etc/openvpn/server/keys/
$ cp pki/issued/server.crt /etc/openvpn/server/keys/
$ cp pki/dh.pem /etc/openvpn/server/keys/
$ cp ta.key /etc/openvpn/server/keys/

7. Setup IP forwarding

Now, we need to setup IP forwarding by uncommenting out a line in /etc/sysctl.conf. Change #net.ipv4.ip_forward=1 such that it looks like net.ipv4.ip_forward=1 finally run sysctl -p .

$ nano /etc/sysctl.conf// uncomment or add this line: net.ipv4.ip_forward=1
Image for post
Image for post
How the /etc/sysctl.conf should look like
Image for post
Image for post
Result of running “sysctl -p”

8. Setup NAT and persist ip rules

Set up the iptables with the command below. Ensure that we set persist our IP settings so that the next time we reboot, it’s always there.

Note: Remember to change eth0 to your own network interface, use ifconfig to check.

$ iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE$ apt-get install iptables-persistent// choose "yes" twice, to keep current IPv4 and IPv6 rules

9. Configure server.conf

Create a new server.conf in /etc/openvpn/ . The contents of this files should be as follows. Replace out the bold text with your own settings. The important lines that needs to be changed are bolded as below.

x.x.x.x needs to be your IP address of the server where you are currently SSH-ed into.

Note: I recently noticed OpenVPN disconnects every hour, and this happens because by default TLS issues a renegotiation every 60mins. To stop this from happening, I decided to add thereneg-sec 0 directive. Feel free to remove or change this to something that fits your use-case.

$ nano /etc/openvpn/server.conf// Contents of /etc/openvpn/server.confcipher AES-256-CBC
auth SHA256

port 1194
proto tcp
dev tun
tun-mtu 1500
tun-mtu-extra 32
mssfix 1410
tls-auth /etc/openvpn/server/keys/ta.key 0
key-direction 0

ca /etc/openvpn/server/keys/ca.crt
cert /etc/openvpn/server/keys/server.crt
key /etc/openvpn/server/keys/server.key
dh /etc/openvpn/server/keys/dh.pem
server 10.8.0.0 255.255.255.0push "redirect-gateway def1 bypass-dhcp"
push "route x.x.x.x 255.255.255.255 net_gateway"
push "dhcp-option DNS 1.1.1.1"
push "dhcp-option DNS 8.8.8.8"
push "dhcp-option DNS 8.8.4.4"
keepalive 10 60
reneg-sec 0
persist-tun
status /var/log/openvpn.log
verb 3
socket-flags TCP_NODELAY
push "socket-flags TCP_NODELAY"

10. Restart OpenVPN server

Finally, restart the openvpn server to check everything was configured correctly.

$ service openvpn restart

Create OpenVPN Client Keys

Next, since we are here, we will also create client keys. I will run through the steps of creating just one client. But you can also repeat the process to create multiple client settings. Incase you need one for each Windows, MacOS, Android.

1. Generate and sign the keys

Generate and sign the keys for your clients. Change the “client” with a name that is suitable, remember to change accordingly the subsequent steps.

$ sudo -s 
$ cd /opt/easyrsa_3_0_4/
$ ./easyrsa gen-req client nopass

If everything ran without error the output should look like something similar

Note: using Easy-RSA configuration from: ./vars
Generating a RSA private key
.............................................................................+++++
writing new private key to '/opt/easyrsa_3_0_4/pki/private/client.key.nyBgLqUEAP'
-----
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) [client]:
Keypair and certificate request completed. Your files are:
req: /opt/easyrsa_3_0_4/pki/reqs/client.req
key: /opt/easyrsa_3_0_4/pki/private/client.key

Now, sign the client request with the following command, but remember to change the bold text with your own client name.

$ ./easyrsa sign-req client client

2. Download the Client Files

Next, download these client files which will be used for the setup later. If you have problems with step, research a little on the scp command.

// These commands are ran on the server to copy files over to /tmp
$ cp /opt/easyrsa_3_0_4/{ta.key,pki/{ca.crt,private/client.key,issued/client.crt}} /tmp/
// These scp commands are meant to be ran on your local machine
$ scp root@your-hostname:/tmp/{ca.crt,client.key,client.crt,ta.key} ~/Downloads
// Clean up after ourselves. Removing what we copied on the server
$ rm -rf /tmp/{ca.crt,client.*,ta.key}

Setup Stunnel Server + Client

This part will be quick. Here are the steps.

1. Generate Certs and Keys

Proceed to /etc/stunnel and generate the keys and certs. Here, what we are doing is to create a new key.pem and cert.pem with a validity period of 10 years. Next, we concatenate both these files into a stunnel.pem file that we need to use it locally later. Finally, in case we want to use this for Android, we also need to convert this into a stunnel.p12 file.

$ sudo -s
$ cd /etc/stunnel/
$ openssl genrsa -out key.pem 2048
$ openssl req -new -x509 -key key.pem -out cert.pem -days 3650
$ cat key.pem cert.pem >> stunnel.pem
$ openssl pkcs12 -export -out stunnel.p12 -inkey key.pem -in cert.pem

2. Configure the Stunnel Server

Here, we need to create a new stunnel.conf file. Most of the content is the same. Just ensure where the stunnel.pem file is actually where you have that stunnel.pem you created in step 1.

$ nano /etc/stunnel/stunnel.conf

Add the following below into stunnel.conf .

chroot = /var/lib/stunnel4
pid = /stunnel4.pid
setuid = stunnel4
setgid = stunnel4
socket = l:TCP_NODELAY=1
[openvpn]
accept = 443
connect = localhost:1194
cert = /etc/stunnel/stunnel.pem

3. Enabling stunnel to autostart

So for this step, we are essentially going to use 2 different approaches. You can decide how you want to continue, but select only 1 of the 2 different approaches. I prefer the systemd approach because I know that systemd will restart the service if for some reason the process is dead.

3a. Running stunnel as a service

To do this, we need to first create a service configuration file in /lib/systemd/system/.

$ sudo nano /lib/systemd/system/stunnel.service

The contents in the file are,

[Unit]
Description=SSL tunnel for openvpn
After=network.target
After=syslog.target
[Install]
WantedBy=multi-user.target
Alias=stunnel.target
[Service]
Type=forking
ExecStart=/usr/bin/stunnel /etc/stunnel/stunnel.conf
ExecStop=/usr/bin/pkill stunnel
WorkingDirectory=/etc/stunnel
TimeoutSec=600Restart=always
PrivateTmp=false

Finally, enable and start the service.

$ sudo systemctl enable stunnel.service$ sudo systemctl start stunnel.service

3b. Running stunnel with default autostart

We can also use the built in script that will auto start stunnel when the computer boots up.

Make the following changes to /etc/default/stunnel4 . Change ENABLED=0 to ENABLED=1 .

$ nano /etc/default/stunnel4
Image for post
Image for post
Contents of /etc/default/stunnel4

4. Download Client Files

Download /etc/stunnel/stunnel.pem , /etc/stunnel/stunnel.p12 .

$ cp /etc/stunnel/stunnel.{pem,p12} /tmp// These commands run from your own machine
$ scp root@your-hostname:/tmp/stunnel.{pem,p12} ~/Downloads
// Finally clean up the server files
$ rm -rf /tmp/stunnel.{pem,p12}

Reboot server

Finally, run sudo reboot now and wait for server to reboot.

Configuring Clients

Next, depending on what you need on your client end, you can consider visiting any of the articles below to setup your clients respectively. These articles are currently in work in progress. Will update this section once those articles are ready.

Sharing OpenVPN + Stunnel via Router

If you’re interested you can actually also share the VPN on a router by either connecting a rasberry pi or a spare linux machine in the network as a default gateway. The process was a little tricky for me so I documented how I converted my router into a OpenVPN + Stunnel router, in case this might interest you!

Other Articles in Series

This article is part of a series, see how you might be able to connect OpenVPN and Stunnel through these other different clients and approaches.

References

An avid web developer constantly looking for new web technologies to dabble in, more information can be found on bit.ly/jayden-chua

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store