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.

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

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


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, useifconfig
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 the
reneg-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.pemserver 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 0persist-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/stunnelTimeoutSec=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

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
- https://askubuntu.com/questions/1126893/how-to-install-openssl-1-1-1-and-libssl-package
- https://www.digitalocean.com/community/tutorials/how-to-set-up-an-openvpn-server-on-ubuntu-18-04
- https://www.webservertalk.com/setup-openvpn-ubuntu-18-04/
- https://security.stackexchange.com/questions/177509/purpose-of-randfile-in-openssl/177512#177512
- https://github.com/wazuh/wazuh/issues/3395
- https://community.openvpn.net/openvpn/wiki/OpenvpnSoftwareRepos?__cf_chl_jschl_tk__=63fca5db0904f07cf7b3da3759282a0acdedf394-1578893704-0-ASBA9RGvRxEXSVLN6tdySrq0IYXpbchBzGRPPiq5DSWVK6vBzbdjuzrE-VPu99t-EdaR0sxs20omSGecj8TuK6spp2opAeajFaYFWjcfczq6s_dGsOhNUZYfm4aQmaSKiYdCIfO7WxnoRrhlmssep0e-q_Q2x7IFvTVghlXqddGAROpmHZWybGZWELbUfomk_gzy3fMvN2094Eo10GHaEHu6V2pIYBg5c2nmCMv75f5g3M_ryKScDVEJAqSCJWASAepxBhblPnZBA2RQumQfl0LL6knSmcVtyLTJJIUXkxYPxpb4I669G0DD4NuMzZz5GA