How-to: OpenVPN Server in a Synology Docker Container

​I recently purchased Synology NAS, and wanted to run an OpenVPN server in a container for remote access. Yes, the Synology has a built-in OpenVPN server, but I felt that running a containerized version was more prudent. There are resources such as the Reddit Synology forum covering the Kylemanna OpenVPN Docker container, but I also wanted to harden my system a bit more using the ​Configuring a secure OpenVPN 2.4 server with Docker​​​ resource. ​​My guide builds on information found in Reddit and the above hardening guide, and uses the kylemanna/docker-openvpn container.

I've also included a downloadable script that has all of the commands, so you don't need to try and cut and paste from this blog and possibly mess up some of the long commands that wrap around. I've also parameterized the script a bit, so it's a bit cleaner, IMHO. Even with the script, I prefer to copy/paste each command into a DSM SSH session, so I can better monitor progress and handle any errors.

​Prerequisites

​1. Enable SSH on your Synology, if not already enabled. Go to Control panel -> Terminal & SNMP -> Enable SSH service. For slightly better security use a different port number above 2048. 

2. Install the Docker package on your Synology, if not already installed.

3. Search the Docker registry for OpenVPN and select/download the package by Kylemanna (Kylemanna/openvpn).

Synology TUN Configuration

For containers to be able to properly use OpenVPN, we need to configure a generic TUN device ​on the Synology using a shell script. [Thanks to Tom for these steps, which I've reproduced below.] Note, if you have other Docker containers that use OpenVPN and you've already configured a TUN, skip this section. Once is enough! 

1. Create a folder on the Synology to put the script. I suggest /volume1/docker/tun.

2. If not already installed, install the Text Editor package in DSM. Open the Text editor and go to File -> New.

3. Insert the following text:

#!/bin/sh

# Create the necessary file structure for /dev/net/tun
if ( [ ! -c /dev/net/tun ] ); then
  if ( [ ! -d /dev/net ] ); then
    mkdir -m 755 /dev/net
  fi
  mknod /dev/net/tun c 10 200
fi

# Load the tun module if not already loaded
if ( !(lsmod | grep -q "^tun\s") ); then
  insmod /lib/modules/tun.ko
fi

# Load iptables mangle is not already loaded
if ( !(lsmod |grep -q "^iptable_mangle\s") ); then
  insmod /lib/modules/iptable_mangle.ko
fi

4. Select File -> Save As. Navigate to the folder you created (or already have) and save the script there. 

5. Now let's create a scheduled task to run this script upon boot-up. Open Control Panel and select Task Scheduler.

6. Select Create -> Triggered Task -> User Defined Script.

7. Name the task anything you wish (e.g. "TUN Adapter"). Use 'root' for the username, 'boot-up' event, check the 'enabled' box, and locate the tun.sh file path and enter it on the Task Settings tab. Note that it may take a reboot for the script to properly work, so let's take 5 minutes now and reboot the Synology.

​Downloadable Script

​To make life a little bit easier, I combined all of the shell script code below into a single downloadable file. You can get that file here. Just change the variables and a path, and you should be good to go! I still suggest copying/pasting each line into SSH separately so you can monitor for an errors. Some commands do require interaction. 

​S​hell Variable Configuration

First, we need to configure a few variables for the script that we will be running to configure the Docker OpenVPN container. OVPN_DATA and OVPN_PKI are just volume lables, and can be whatever you want. FQDN is the internet facing FQDN of your VPN end point. PROT is either 'udp' or 'tcp', depending on what you want to do. 'PORT' is the associated external facing port, which is not necessarily the same as what the container is configured ​to use. In my case I want TCP on port 443. DEVICE is the name of the OpenVPN file you will install on each client. Each device you deploy the VPN client on should have a unique file (and thus key). Two clients with the same key can't connect simultaneously. For the fastest VPN speeds, use UDP (which by default uses port 1194). However, it may be less compatible than using TCP over a well known port like 443.

​1. Use your favorite SSH client and open a shell into your Synology. Run the following commands:

2. ​sudo bash

3.
OVPN_DATA="ovpn-data"
OVPN_PKI="openvpn-pki"
FQDN="vpn.domain.com"
PROT="tcp"
PORT="443"
DEVICE="YourComputer"

​OpenVPN Container Deployment

This section will deploy and configure the OpenVPN container. Due to the security hardening, we are using two volumes. One for the sensitive PKI information, and the other for the generic OpenVPN files.

​​1. docker volume create --name $OVPN_DATA
2. docker volume create --name $OVPN_PKI

​3. docker run -v $OVPN_PKI:/etc/openvpn --rm -it kylemanna/openvpn ovpn_genconfig -u $PROT://$FQDN:$PORT -z -C 'AES-256-CBC' -e 'tls-version-min 1.2' -T 'TLS-ECDHE-RSA-WITH-AES-256-GCM-SHA384:TLS-ECDHE-ECDSA-WITH-AES-256-GCM-SHA384:TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA384:TLS-DHE-RSA-WITH-AES-256-GCM-SHA384:TLS-ECDHE-ECDSA-WITH-CHACHA20-POLY1305-SHA256:TLS-DHE-RSA-WITH-CAMELLIA-256-CBC-SHA256'

Note: ​​The -z enables compression. I've also configured a stronger cipher suite than the default. This should work with current OpenVPN clients, but YMMV. If connections fail, you can remove the -C -e and -T parameters. 

​4. docker run -v $OVPN_PKI:/etc/openvpn --rm -it kylemanna/openvpn ovpn_initpki

​Note: This will ask for a 'common name' for your CA. Required, but can be any name you choose. I picked ca.mydomain.com. After the common name, enter a CA phassphrase that is complex. Enter it a total of ​four times, and write it down. It will be needed ​in the future when you want to issue more client OVPN files.

​Client ​OpenVPN File Generation

​Each DEVICE that you want to allow access to the VPN should have ​its own OVPN configuration file and password. Repeat the following two steps as needed for each ​device that needs a VPN connection. ​Devices can share the same OVPN configuration file, however, the server will only ​allow a single connection per key. So the second device attempting a connection (with another device already connected with the same OVPN file) will get immediately disconnected.

​1. ​​docker run -v $OVPN_PKI:/etc/openvpn --rm -it kylemanna/openvpn easyrsa build-client-full $DEVICE
​Note: ​It will prompt you for a PEM passphrase. This passphrase will be required whenever you launch the OpenVPN client to connect. It protects the OpenVPN PKI keys on your client. So make it complex, but something you can also remember and easily type. Some OpenVPN clients may allow you to save the password. Re-enter the CA passphrase when requested. 

​2. ​docker run -v $OVPN_PKI:/etc/openvpn --rm -it kylemanna/openvpn ovpn_getclient $DEVICE > /volume1/​<path>/$DEVICE.ovpn
​Note: ​Change the <path> to whatever share you want the file to be dropped into on your Synology. You will need to copy this file from the Synology onto the device you want to use the VPN on.

​Copying PKI Files

Now we need to copy the needed PKI files from the openvpn-pki volume to our primary openvpn volume. Do note that step 4 is a multi-line command. Likely easier to copy/paste from the downloadable script above, than copy from here.

1.​ docker run -v $OVPN_PKI:/etc/openvpn --rm kylemanna/openvpn cat /etc/openvpn/pki/crl.pem
2. docker run -v $OVPN_DATA:/etc/openvpn --rm kylemanna/openvpn mkdir /etc/openvpn/pki/
3. docker run -v $OVPN_DATA:/etc/openvpn --rm kylemanna/openvpn mkdir /etc/openvpn/pki/{issued,private}

4. ​​for file in /etc/openvpn/pki/crl.pem /etc/openvpn/pki/ca.crt /etc/openvpn/pki/dh.pem /etc/openvpn/pki/issued/$FQDN.crt /etc/openvpn/pki/private/$FQDN.key /etc/openvpn/pki/ta.key
do
  docker run -v $OVPN_PKI:/etc/openvpn --rm kylemanna/openvpn cat "$file" > $(basename "$file")
  docker run -v $OVPN_DATA:/etc/openvpn --rm -i kylemanna/openvpn sh -c "cat > $file && chmod 600 $file" < $(basename "$file")
  docker run -v $OVPN_DATA:/etc/openvpn --rm -i kylemanna/openvpn sh -c "chmod 600 $file"
done

​Launch OpenVPN Server

This command will launch your OpenVPN server container, and assuming no errors, it will continue to run.

​1. ​docker run -v $OVPN_DATA:/etc/openvpn -d -p 1194:1194/$PROT --cap-add=NET_ADMIN kylemanna/openvpn
Note: ​If you want different ports mapped at the container level, you can change them here. However, I prefer to leave these the default and use my externally facing router to do port translation and forwarding. Also, you might want to configure the 'auto restart' option in the Docker GUI for this container. If the container fails to keep running, review the Docker console logs for details on what went wrong.

​Port Forwarding

​Next up you will need to configure your internet router for port forwarding. This procedure will vary from router to router, so I won't include instructions. But just remember to use the same external port number as you used to generate the OVPN configuration file. Hint: The OVPN file is text, so you can open it up and validate which external port it needs. 

​Summary

As you can see, if you use the downloadable script, configuring an OpenVPN server on a Synology NAS using Docker is pretty straight forward. I've included some security best practices in this guide, to better shore up the security of OpenVPN. ​

As for clients, for Windows I would recommend the official OpenVPN client. Or if you want something more fancy, but paid, use Viscosity. If you use a Mac, then the best bets are Tunnelblick (free) or Viscosity (paid). For iOS, use OpenVPN Connect.

Buy me a coffee

Related Posts

Subscribe
Notify of
12 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
scott
June 11, 2019 12:07 am

Im reading your blog with interest. Can you tell me what is the advantage of creating such a docker container upon your NAS ? Does this give you the ability to connect to the drives presented on it whilst at work for instance, or to DSM ? or into your home network ? What use case is there for this ?

vibban
September 4, 2019 9:26 am

Hi, i follow your tutorial but at the last step when iam going to launch the server i get this error in the docker log.

/usr/local/bin/ovpn_run: line 55: /etc/openvpn/ovpn_env.sh: No such file or directory

what have i missed?

Adrian
September 12, 2019 10:12 am

Hi! Thank you very much for this guide! I’ve made it twice, and after the last command, the docker doesn’t run. It gives me this error: “/usr/local/bin/ovpn_run: line 55: /etc/openvpn/ovpn_env.sh: No such file or directory”. Do you know why this can be?

Thanks for your help!

kevin
December 5, 2019 6:29 pm

Thanks for this blog post. I’m interested in accomplishing the same thing as the default OVPN option in Synology is not secure, namely it uses an old insecure version of TLS. As I implemented this by following your instructions, how to I know if the script for creating the TUN worked? I don’t think it did as i can’t find the directory paths the scripts should have created. Also once I got the en end of the script I ended up with three containers off the one image, that doesn’t seem right.

Ryan
January 8, 2020 1:47 pm

Thank you very much for such a thorough walk-thru. I’ve followed your instructions and everything goes to plan until I try to actually start the container using the last docker command. On running it, the container is created but immediately errors out with this in the logs:

/usr/local/bin/ovpn_run: line 55: /etc/openvpn/ovpn_env.sh: No such file or directory

I confirmed that the data volume is created, but the ovpn_env.sh file was apparently never created.

eDoubleoo
November 18, 2020 9:30 am
Reply to  Ryan

Hi Derek and Ryan, I’ve completed the above “How-to” guide, and I’m stuck. Started by downloading the OpenVPN docker image to our Synology Nas, SSH’d into ‘sudo bash’ and was able to complete all your listed steps. Receiving the device ovpn, pem keys and crts.  Upon processing the last docker run request to launch OpenVPN, a generically named version of the image starts with a volume; File/Folder: ovpn-data Mount Path: /etc/openvpn Initially, this would start the container, and a log response would state: /usr/local/bin/ovpn_run: line 55: /etc/openvpn/ovpn_env.sh: No such file or directory I would stop this container to check the… Read more »

Thijs B
June 7, 2021 1:49 pm
Reply to  Ryan

Hi,

I also got stuck trying to solve this for a few days. Then decided to go for one directory only (so rename OVPN KPI to DATA everywhere). Then everything works fine! Probably there is a smarter/better solution from the author but I thought I would mention for others.

PatoV
September 21, 2021 7:32 am
Reply to  Ryan

Hi, ovpn_env.sh is here – OVPN_PKI volume. I changed this line in script:

for file in ….. and added two files – /etc/openvpn/ovpn_env.sh /etc/openvpn/openvpn.conf

for file in /etc/openvpn/ovpn_env.sh /etc/openvpn/openvpn.conf /etc/openvpn/pki/crl.pem /etc/openvpn/pki/ca.crt /etc/openvpn/pki/dh.pem /etc/openvpn/pki/issued/$FQDN.crt /etc/openvpn/pki/private/$FQDN.key /etc/openvpn/pki/ta.key
do
 docker run -v $OVPN_PKI:/etc/openvpn –rm kylemanna/openvpn cat “$file” > $(basename “$file”)
 docker run -v $OVPN_DATA:/etc/openvpn –rm -i kylemanna/openvpn sh -c “cat > $file && chmod 600 $file” < $(basename “$file”)
 docker run -v $OVPN_DATA:/etc/openvpn –rm -i kylemanna/openvpn sh -c “chmod 600 $file”
done

It works now.

Rob
February 29, 2020 4:06 pm

I’m having trouble with ‘docker run -v $OVPN_PKI:/etc/openvpn –rm -it kylemanna/openvpn ovpn_getclient $DEVICE > /volume1/xfer/$DEVICE.ovpn’

/volume1/xfer exists and is writable.

When I run the command, I get the error ‘bash: /volume1/xfer/robleon.ovpn: No such file or directory’.

If I direct the output to the screen using ‘>$(tty)’ it tells me ‘bash: docker: command not found’. I get the same error if I leave out the redirect.

I’m a Docker noob so I’m not sure how to go about debugging this. Any ideas?

Andy
May 18, 2020 1:22 am

Thanks for this! Im just having some trouble, when I run the 9th command on your list (the first docker run command) I get an error saying “Common name not specified, see ‘-u'” then gives me a list of arguments. I must be doing something wrong in the first steps when specifying the variables?

Oronzo
December 14, 2020 10:35 am

Is it possible to run a Wireguard Server / Client on Docker container on Synology NAS?
Thanks