What do you think? Discuss, post comments, or ask questions at the end of this article. [More about me]

Skip to end of metadata
Go to start of metadata

It's not often that I find a very complete setup guide that covers many aspects of what I want to achieve.  However, digitalocean's guide for setting up an openvpn server is excellent and very well written.  It covers many (most) of the things I implement on any openvpn server I setup.

In this writeup I'll simply cover some of the things that aren't in digitalocean's guide and that I usually implement to meet specific use cases.

If you're setting up an openvpn server, please check out their guide and follow along.  Much of what I cover here would depend on a setup similar to what is outlined by digitalocean.

Generating client configurations

This is covered well in the digitalocean guide, but I thought I would note down the commands used here as it's something I use/do often.

First, generate the client key by cd'ing into your openvpn-ca (cert authority folder) and sourcing the variables required:

cd ~/openvpn-ca
source vars

Do NOT run ./clean-all unless you really want to remove all client keys (seriously, I've done this by mistake and you will then need to recreate or restore previous keys).

Then simply generate a client key with

./build-key <client-key-name>

replace <client-key-name> with the name of the client key you want

If you followed the digitalocean guide you'll now be able to generate an ovpn client config that contains the key and everything needed for a client to securely connect to your openvpn server by

cd ~/client-configs
./make_config.sh <client-key-name>

This will create a client key in ~/client-configs/files that can be transferred to the client.

Assigning static IP addresses to particular client configurations

You might want/need to map certain client configurations to a static ip address in the openvpn ip pool.  For this use case you would generally have a ip range for static ip addresses, and a separate (non-conflicting) range for dynamic ip address assignment (i.e. standard clients connect and receive an dynamic ip address subject to availability).

Such a configuration requires several changes to server.conf and the creation of a folder which holds a client-name config file (which contains the static ip address to assign) for each config you wan to assign a static ip address to.

server.conf changes

Here we'll do two things, define an ip address range for dynamic assignment, and enable the folder to hold client static ip address files.

Let's start with changing the dynamic ip range.  For this example we're going to set the dynamic ip range from 10.8.0.100 to 10.8.0.200:

ifconfig-pool 10.8.0.100 10.8.0.200 255.255.255.0

Next, let uncomment the client-config-dir directive:

client-config-dir /etc/openvpn/ccd

You'll note that I give the absolute path to the ccd folder (just my preference).

 Don't forget to actually create the ccd folder:

sudo mkdir -p /etc/openvpn/ccd

Let's now restart openvpn

sudo systemctl restart openvpn@server

Create files for each client you want to assign an ip address to

Here we simply add a file, which must have the same name as the client configuration. 

For example, suppose we have a client configuration named "client1" which has the ovpn file "client1.ovpn", that we want to assign the ip address "10.8.0.1" to.  We would do the following:

sudo vim /etc/openvpn/ccd/client1

and then add the following line

ifconfig-push 10.8.0.1 255.255.255.0

You DO NOT need to restart the openvpn server after adding client configs. Each time a client connects openvpn will check for a corresponding (named) file in the ccd folder.

Overriding DNS settings for server in client configutation

Openvpn server.conf  allows you to define DNS addresses such as addresses for OpenDNS etc.  These addresses with then be pushed to the client to implement when it connects to the server.

However, you might want/need to use different DNS servers for your client.  This can be done easily by rejecting the server's pushed DNS addresses and implementing your own directly in your .ovpn config file.

Add the following to you .ovpn config (replacing x.x.x.x  and y.y.y.y  with your preferred DNS addresses).

# override DNS with my own settings
pull-filter ignore "dhcp-option DNS"
dhcp-option DNS x.x.x.x
dhcp-option DNS y.y.y.y

Replace x.x.x.x  and y.y.y.y  with your preferred DNS addresses above.

If OpenVPN doesn't reconnect after wake

On one of my laptops (running Manjaro) the openvpn client wouldn't reconnect after waking from sleep.  As outlined in the Arch wiki, you can quickly add a systemd service which sends a SIGHUP signal to OpenVPN which forces a reconnect to occur:

/etc/systemd/system/openvpn-reconnect.service
[Unit]
Description=Restart OpenVPN after suspend

[Service]
ExecStart=/usr/bin/pkill --signal SIGHUP --exact openvpn

[Install]
WantedBy=sleep.target

then enable the service:

sudo systemctl enable openvpn-reconnect

Recovering after an accidental ./clean-all

COMING SOON

Using port 443 for OpenVPN & other applications (like a webserver)

If the network environment you're operating in is restrictive then chances are port 1194 (standard openvpn port) will be blocked - which means that you wont' be able to connect to your openvpn server from that environment without making some changes on your end.  One way to get around this is to change your openvpn port to something that will most likely NOT be blocked, like port 443 (https/ssl port).  Doing so is trivial within your openvpn server.conf file.  However, issues arise if you also use said server for other things, like a webserver - especially is you run multiple applications from said server (see Apache reverse-proxy SSL to multiple server applications for a standard setup running multiple web applications with Apache).

In this use case, we are actually going to use port 443 for both OpenVPN and our Apache2 reverse proxy.  Now, we can't bind port 443 simultaneously to OpenVPN and Apache2 but we can share port 443 by fronting these with a SSL/OpenVPN multiplexer like SSLH.

How this works is we'll change our Apache reverse-proxy listening port to something else (like 8443), then bind port 443 to SSLH.  SSLH will analyse traffic incoming to port 443, and if it's standard https then it transparently forwards this traffic to port 8443, and if it's OpenVPN traffic then it forwards it to our standard server-side OpenVPN port 1194.

This approach assumes you've setup an Apache reverse-proxy in which you take care of SSL termination with the relevant SSL certs within said reverse-proxy which listens (is binded) to port 443.  It also assumes that you've setup OpenVPN as outlined previously in this article and are using port 1194 for OpenVPN.

Change OpenVPN to tcp-server mode

First let's make a small change to our OpenVPN by replacing

proto udp 

with: 

proto tcp-server

in /etc/openvpn/server.conf 

Note that with this change you'll also need to change your .ovpn client configuration to use proto tcp-client and port 443 (e.g. the SSLH port instead of the internal OpenVPN port).

Change Apache SSL listening port and update any vhosts directives

Next, we're going to change our Apache SSL listening port to 8443 (you should check that this port is free - if not choose another valid arbitrary port number).

For this we'll need to change several files, the first should be changed to look like:

/etc/apache2/ports.conf
Listen 80

<IfModule ssl_module>
        Listen 4443
</IfModule>

<IfModule mod_gnutls.c>
        Listen 4443
</IfModule>

We'll also need to change any and all site configuration vhost directives from:

<VirtualHost *:443>
    ...
</VirtualHost>

to:

<VirtualHost *:4443>
    ...
</VirtualHost>

Don't restart Apache2 just yet...

Install and configure sslh

Next we're let's install SSLH:

sudo apt install sslh

and select standalone mode when asked to select a mode.

Configuration of SSLH is done in a single file.  Your sslh configuration file should look like:

# Default options for sslh initscript
# sourced by /etc/init.d/sslh

# Disabled by default, to force yourself
# to read the configuration:
# - /usr/share/doc/sslh/README.Debian (quick start)
# - /usr/share/doc/sslh/README, at "Configuration" section
# - sslh(8) via "man sslh" for more configuration details.
# Once configuration ready, you *must* set RUN to yes here
# and try to start sslh (standalone mode only)

RUN=yes

# binary to use: forked (sslh) or single-thread (sslh-select) version
# systemd users: don't forget to modify /lib/systemd/system/sslh.service
DAEMON=/usr/sbin/sslh

DAEMON_OPTS="--user sslh --transparent --listen <INTERNAL-IP-ADDRESS>:443 --ssl <INTERNAL-IP-ADDRESS>:4443 --openvpn <INTERNAL-IP-ADDRESS>:1194 --pidfile /var/run/sslh/sslh.pid"

where <INTERNAL-IP-ADDRESS> should be replaced with your server's internal IP address (e.g. 10.0.0.x or 192.168.1.x).

Note: if MUST be your internal ip address.  SSLH transparent mode does NOT work if you use localhost or 127.0.0.1

For transparent mode to work we also need to modify iptables.  To ease configuration I created a one-off script.  Note that these settings won't survive a reboot so once we're happy we're going to make these settings persistent.

Copy the following to a file, make it executable, and execute with sudo.

#!/bin/bash

iptables -t mangle -N SSLH
iptables -t mangle -A OUTPUT --protocol tcp --out-interface enp0s25 --sport 4443 --jump SSLH
iptables -t mangle -A OUTPUT --protocol tcp --out-interface enp0s25 --sport 1194 --jump SSLH
iptables -t mangle -A SSLH --jump MARK --set-mark 0x1
iptables -t mangle -A SSLH --jump ACCEPT
ip rule add fwmark 0x1 lookup 100
ip route add local 0.0.0.0/0 dev lo table 100

ip6tables -t mangle -N SSLH
ip6tables -t mangle -A OUTPUT --protocol tcp --out-interface enp0s25 --sport 4443 --jump SSLH
ip6tables -t mangle -A OUTPUT --protocol tcp --out-interface enp0s25 --sport 1194 --jump SSLH
ip6tables -t mangle -A SSLH --jump MARK --set-mark 0x1
ip6tables -t mangle -A SSLH --jump ACCEPT
ip -6 rule add fwmark 0x1 lookup 100
ip -6 route add local ::/0 dev lo table 100

NOTE: you'll need to replace enp0s25 with the main network interface of your server.

Restart Apache, start sslh, and test...

Right, it's time to test the setup.  To do so we need to restart Apache, start SSLH:

sudo systemctl restart apache2
sudo systemctl start sslh

If all went well you should be able to still access your webserver AND connect to OpenVPN on port 443.

Finalise configuration

We'll finalise the configuration (once you've tested it) by enabling sslh (so it starts on boot) and by making the iptable rules (above) persistent.

To make SSLH start on boot:

sudo systemctl enable sslh

See the this article to make the current iptable rules (after you've executed the iptable script above) persistent.

References

  1. https://www.digitalocean.com/community/tutorials/how-to-set-up-an-openvpn-server-on-ubuntu-16-04
  2. https://stackoverflow.com/questions/34304022/change-ssl-port-of-apache2-server-err-ssl-protocol-error
  3. http://www.rutschle.net/tech/sslh/README.html