How to be anonymous on the outbound connections of your nas

Challenge

You want some privacy on the internet highway for outbound connections of your nas. The reasons can be varied. Some want to just have some privacy.
Some want to download the movies and series they can not get legally. Some want to test stuff with different IP addresses. Lots of reasons out there

This article explains how you can make this happen on a Synology Nas and the reason why
this solution was chosen above other possible solutions…

Be warned! This is a very technical article and not for the faint of heart :-)

Solutions

  • anonymising vpn (e.g. purevpn, hidemyass, IPVanish, etc)
  • anonymising proxy
  • other… but in this article I will concentrate on the two mentioned

why is this a problem?

The problem with most vpn’s these days is that services like Netflix actively block anonymising vpn’s.

So if you setup your router for maximum internet freedom you cannot watch Netflix anymore.

Well if you want total anonymity you might have to forgo services like Netflix, but I’m not like that.
I don’t mind that Netflix knows its me :-) but I do like my privacy and hate all the trackers around these days.
As I’m of the opinion that internet freedom is a base right, I want to have the option to have it.

You can set up your separate devices with the VPN software and only run your machine in the secure mode when wanted
but not all devices provide the option of running VPN software (think of Apple TV or Smart TV’s or older devices).

If you use a Synology NAS you can setup the nas with VPN credentials quite easily, but again the problem is that all traffic
is then over the VPN and the nas becomes difficult to access from outside. So if you run a website on the nas and have
the VPN active it will not be accessible.

My choice of a complete solution

Well my solution is multifold… On my personal computer I install the VPN software and run in freedom mode when I want, but
On my Synology NAS I want the incoming connections to be normal but my outgoing connections to be private.

For this reason I wanted to setup a proxy server with with a VPN behind it. If the proxy is used to connect to the internet it
will in effect go the “the other side” through the VPN and become anonymous.

So what to do…

I’ve tried the solution to add a vpn to my router and let all internet traffic be anonymous but as I said some essential services
refused to work anymore. So I disabled that again.

So the solution I’m going to work out in this article is creating a docker image with OpenVPN on it and a squid
proxy and running that on my Synology NAS. This way I can point stuff to that proxy and have freedom.

This article describes the process I went through…

What to accomplish

  • Base image with OpenVPN client installed and working ivonet/openvpn
  • Squid proxy image based on the ivonet/openvpn image
  • PureVPN image based on the ivonet/openvpn-proxy image (my current vpn provider) or maybe even more generic and enable more providers (not sure yet)
  • other optimizations:
    • easy config
    • rotate IP’s by switching at interval (at least at every startup)
    • (possibly more…)

Base OpenVPN image (ivonet/openvpn)

After some research and stubbornness I came up with the following Dockerfile:

Dockerfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
FROM ubuntu:17.04
MAINTAINER IvoNet <webmaster@hardcoder.nl>

# Evironment variables
ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update \
&& apt-get install --no-install-recommends -y openvpn iptables wget curl unzip net-tools \
&& wget --no-check-certificate https://github.com/Yelp/dumb-init/releases/download/v1.2.0/dumb-init_1.2.0_amd64.deb \
&& dpkg -i dumb-init_*.deb \
&& apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /dumb-init_*.deb

ADD bin/ /usr/local/bin/
ADD entrypoint.sh /entrypoint.sh

RUN chmod +x /entrypoint.sh \
&& chmod +x /usr/local/bin/*

WORKDIR /etc/openvpn
VOLUME ["/config","/credentials"]
ENTRYPOINT ["/usr/bin/dumb-init", "/entrypoint.sh"]

This image defines a ubuntu image with openvpn installed on it and with an entrypoint script defined.

entrypoint.sh:

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#!/bin/bash

"$@"

CREDENTIALS=/credentials/openvpn-credentials.txt
if [ -f ${CREDENTIALS} ]
then
echo "Found credentials file..."
chmod 600 ${CREDENTIALS}
else
echo "[ERROR] Credentials file not found..."
echo "[ERROR] please make sure /credentials/openvpn-credentials.txt is created"
echo "[ERROR] username on first line and password on second line (no spaces)"
exit 1
fi

if [ "$(ls -A /config)" ]
then
echo "Found configuration files..."
echo "Copying config files to /etc/openvpn..."
find /config -iname "*.ovpn" -exec cp -v "{}" /etc/openvpn/ \;
find /config -iname "ca.crt" -exec cp -v "{}" /etc/openvpn/ \;
find /config -iname "Wdc.key" -exec cp -v "{}" /etc/openvpn/ \;
cd /etc/openvpn
chmod 600 Wdc.key ca.crt *.ovpn
echo "Adjusting config files to use the credentials provided..."
sed -i "s~auth-user-pass~auth-user-pass ${CREDENTIALS}~g" *.ovpn
else

echo "[ERROR] No configuration found for a ovpn provider."
echo "[ERROR] Please download the ovpn settings and unpack them into the /config folder"
echo "[ERROR] e.g. https://s3-us-west-1.amazonaws.com/heartbleed/linux/linux-files.zip"
echo "[ERROR] contains the config files for PureVPN."
exit 1
fi

if [ -z "$OPENVPN_CONFIG" ]
then
OPENVPN_CONFIG=Netherlands1-tcp.ovpn
echo "[WARNING] No OPENVPN_CONFIG provided, so going for the default [$OPENVPN_CONFIG]."
fi

ORIG_IP=$(curl -s ipecho.net/plain)
echo "Before the vpn has been activated: $ORIG_IP"
NEW_IP="$ORIG_IP"
echo "Starting OpenVPN.."
openvpn --daemon --log /var/log/openvpn --script-security 2 --auth-nocache --config "$OPENVPN_CONFIG"
while [ "$ORIG_IP" == "$NEW_IP" ]
do
sleep 2
NEW_IP=$(curl -s ipecho.net/plain)
done
echo "After the vpn has been activated: $NEW_IP"
exec tail -f -n 0 /var/log/openvpn

This script will run any command provided ("$@") at the beginning of the script and then start the openvpn connection defined
on the commandline. It will check if the IP address has changed. If not it will end up in an endless loop (room for improvement here)

If you want to run this image you need to provide a location with your *.ovpn files and a credentials location. If these are not
provided it will not work. As this image is meant as a base for further development I will not go into it but continue on to the proxy
settings

Docker OpenVPN Proxy image (ivonet/openvpn-proxy)

So now it is time to extend the ivonet/openvpn docker image with proxy software. I choose squid as the proxy.

Dockerfile:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
FROM ivonet/openvpn
MAINTAINER IvoNet <webmaster@hardcoder.nl>

RUN apt-get update \
&& apt-get install --no-install-recommends -y squid3 \
&& mv -f /etc/squid/squid.conf /etc/squid/squid.conf.original \
&& apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

ADD squid/ /etc/squid/

RUN chmod +x /etc/squid/squid-*.sh \
&& mkdir -p /etc/service/squid \
&& ln -s /etc/squid/squid-run.sh /etc/service/squid/run \
&& sed -i 's~\"\$@~service squid start\n\"\$@~g' /entrypoint.sh

EXPOSE 3128

This images extends the ivonet/openvpn image with squid and configures it. See for the squid config files here because they are not relevant for this article.

Notice the sed -i 's~\"\$@~service squid start\n\"\$@~g' /entrypoint.sh command in the Dockerfile. This command replaces the "$@"
in the entrypoint.sh file of the base image and adds service squid start to it.
This is probably not the cleanest way to do it but this article is not about that ;-)

So now we have a squid proxy with openvpn running in the image. Cool! now we need to get it to work properly.
At this point we have an image that has all the ingredients we need. Not necessarily the easiest to work with but all we need.

Here is how to get it to work:

Create a file called openvpn-credentials.txt with the your credentials in it like so:

1
2
USERNAME_HERE
PASSWORD_HERE

During all this I assume you have a VPN provider like IPVanish, purevpn, hidemyass er so… If not get one…

Create a directory (config) containing the *.opvn and key files you can download from your vpn provider’s website
(often a zip with all you need)

Execute the following command in the folder containing the openvpn-credentials.txt file and the config directory:

1
2
3
4
5
6
7
8
9
10
11
12
13
docker run \
-it \
--rm \
--name vpn-proxy \
--cap-add=NET_ADMIN \
--device=/dev/net/tun \
--dns 8.8.8.8 \
--dns 8.8.4.4 \
-v $(pwd)/config:/credentials \
-v $(pwd):/credentials \
-p 3128:3128 \
-e OPENVPN_CONFIG=Switzerland-tcp.ovpn \
ivonet/openvpn-proxy

NOTE: Change options where needed. e.g. Switzerland-tcp.ovpn to a file that exists in your config folder…

if all goes well you get logging sorta like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[...]
'/config/PureVPN/UDP/Yemen-udp.ovpn' -> '/etc/openvpn/Yemen-udp.ovpn'
'/config/PureVPN/UDP/test-udp.ovpn' -> '/etc/openvpn/test-udp.ovpn'
'/config/PureVPN/ca.crt' -> '/etc/openvpn/ca.crt'
'/config/PureVPN/Wdc.key' -> '/etc/openvpn/Wdc.key'
Adjusting config files to use the credentials provided...
Before the vpn has been activated: CURRENT_IP_ADDRESS_HERE
Starting OpenVPN..
After the vpn has been activated: NEW_IP_ADDRESS_HERE
Sat Sep 16 17:32:06 2017 TCP_CLIENT link local: (not bound)
Sat Sep 16 17:32:06 2017 TCP_CLIENT link remote: [AF_INET]AN_IP_ADDRESS_HERE:80
Sat Sep 16 17:32:08 2017 [PureVPN] Peer Connection Initiated with [AF_INET]AN_IP_ADDRESS_HERE:80
Sat Sep 16 17:32:09 2017 TUN/TAP device tun0 opened
Sat Sep 16 17:32:09 2017 do_ifconfig, tt->did_ifconfig_ipv6_setup=0
Sat Sep 16 17:32:09 2017 /sbin/ip link set dev tun0 up mtu 1500
Sat Sep 16 17:32:09 2017 /sbin/ip addr add dev tun0 AN_IP_ADDRESS_HERE/27 broadcast AN_IP_ADDRESS_HERE
RTNETLINK answers: File exists
Sat Sep 16 17:32:11 2017 ERROR: Linux route add command failed: external program exited with error status: 2
Sat Sep 16 17:32:11 2017 Initialization Sequence Completed

Now on my local machine (macbook pro) this worked fine but when I tried this on my Synology I was disappointed because I got
the following error:

1
ERROR: Cannot open TUN/TAP dev /dev/net/tun: No such file or directory (errno=2)

Don’t worry I fixed it but that is for later…

Warnings are to be fixed if possible…

1
2
3
WARNING: file '/credentials/openvpn-credentials.txt' is group or others accessible
WARNING: No server certificate verification method has been enabled. See http://openvpn.net/howto.html#mitm for more info.
WARNING: file 'Wdc.key' is group or others accessible

If you get these warnings you have probably not followed this manually carefully enough :-) or I made a mistake.
Just leave a comment.

Provider specific docker image

I currently use purevpn as my vpn provider and I wanted to have an easier startup…
If you have another proider and also want the ease of use. just make your own image based on the ivonet/openvpn-proxy image
and provide your provider download zip in the wget command. Is should be easy to adjust te Dockerfile below to suit your
needs…

1
2
3
4
5
6
7
8
9
10
11
12
13
FROM ivonet/openvpn-proxy
MAINTAINER IvoNet <webmaster@hardcoder.nl>

#https://support.purevpn.com/linux-openvpn-command
RUN wget --no-check-certificate https://s3-us-west-1.amazonaws.com/heartbleed/linux/linux-files.zip \
&& unzip linux-files.zip \
&& mkdir -p /config \
&& mv -v "./Linux OpenVPN Updated files" /PureVPN \
&& rm -fv linux-files.zip \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

VOLUME ["/credentials"]
CMD ["/bin/cp", "-r", "/PureVPN", "/config/"]

This image just extends the ivonet/openvpn-proxy image and installs the purevpn openvpn setting in the image.
When started the installed files are copied to the /config folder so that the parent image can find them. Now you can leave out
the config folder

1
2
3
4
5
6
7
8
9
10
11
12
13
docker run \
-it \
--rm \
--name purevpn-proxy \
--privileged \
--cap-add=NET_ADMIN \
--device=/dev/net/tun \
--dns 8.8.8.8 \
--dns 8.8.4.4 \
-v $(pwd):/credentials \
-p 3128:3128 \
-e OPENVPN_CONFIG=Netherlands-tcp.ovpn \
ivonet/purevpn-proxy

So you still need the openvpn-credentials.txt file but the config folder is gone…

Synology nas

So now I really have the ingredients I need to get it to my NAS. So here it goes…

Of course you need a Synology that has docker installed.

As you can see in the above provided command it is not possible to start this image through the graphical interface (DSM). Some
options are just not provided by the GUI, but that does not mean that docker is not capable of these options.
You just need to ssh into your nas an perform the command yourself. If you do not know how to ssh into your nas you need
to google it because this article will not tell you.

First find the docker volume on your nas and create a new folder on it called something like vpn-proxy and create
the openvpn-credentials.txt file here as described above.

Now ssh to the nas and goto the vpn-proxy folder (e.g. cd /volume1/docker/vpn-proxy) and execute the following command:

1
2
3
4
5
6
7
8
9
10
11
sudo docker run \
-d \
--restart=always \
--name proxy \
--cap-add=NET_ADMIN \
--device=/dev/net/tun \
--dns 8.8.8.8 \
--dns 8.8.4.4 \
-v $(pwd):/credentials \
-p 3128:3128 \
ivonet/purevpn-proxy

This should start the command in daemon mode with the restart always flag on and it should now be accessible on localhost and
port 3128.

Check for error(s)

You can check if you have errors during startup by executing the following command while the image is running:

1
sudo docker logs proxy

You might not get feedback on errors because the image can get in an endless loop if you do not get a new anonymous IP Address
(startup failed)

Fix error(s)

When I tried all this I got the following message (only on my Nas):

1
ERROR: Cannot open TUN/TAP dev /dev/net/tun: No such file or directory (errno=2)

In order to fix this you have to create the device and load the tun module.
You can do this through the commandline (ssh) on your Synology NAS with root rights (sudo)

1
2
3
mkdir -m 755 /dev/net
mknod /dev/net/tun c 10 200
insmod /lib/modules/tun.ko

If you want to be sure that it is done every time the nas reboots place the following file in /usr/local/etc/rc.d/ folder of your
Synology NAS

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
26
27
28
#!/bin/sh
# Place me in: /usr/local/etc/rc.d/
# chmod 755 me
# https://developer.synology.com/developer-guide/integrate_dsm/run_with_system_boot.html

case $1 in
start)
# Create the necessary file structure for /dev/net/tun
echo "Activating TUN/TAP..."
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
;;
stop)
echo "Stopping TUN/TAP..."
;;
*)
echo "Usage: $0 {start|stop}"
;;
esac

Now it should all work.

Test

Now if you have your proxy image running on your nas and know that all logging is as it should be

you should be able to test it by going to the DSM control panel and activating the proxy settings in network

Now in the ssh console type:

1
curl -s ipecho.net/plain

The IP Address returned should be different than when you type ipecho.net/plain in your own browser on your local machine.

Good luck!

Comments / tweeds / improvements always very welcome :-)

Ivo.