In this guide, we will set up Docker on a Digital Ocean CentOS 7 droplet, then set up OpenVPN. We’ll also discuss how to create a custom SystemD service file so that we can manage our container with
VPNs (virtual private networks) are a great way to secure your internet traffic over untrusted connections. It can provide easy access to your home network file server or other machines that you don’t want to expose to the internet directly. Also, you can use it to circumvent blocked sites or services on a company network, or it can be used as a proxy to access restricted content in your country (like Hulu or Netflix).
Docker allows you to easily deploy and manage Linux containers: isolated, virtualized environments. Their isolation makes them secure and easy to manage, especially for developers who can develop their container, without worrying about which distro or even OS it will be deployed to.
This guide assumes you already have a CentOS 7 server set up. I recommend using Digital Ocean, but any provider which gives you root access will work as well. This guide should also work with any distribution that uses SystemD. I liked the set up so much, that I implemented it on my home Arch server as well.
First, let’s install docker:
sudo yum -y update sudo yum -y install docker docker-registry
Once that’s done, we’ll start Docker and then enable it to start at boot:
sudo systemctl start docker.service sudo systemctl enable docker.service
If you don’t want to have to type
sudo every time you use the
docker command, then you’ll have to add your user to the group ‘docker’. Do so with:
sudo usermod -aG docker $USER newgrp docker
The second command will make your current session aware of your new group.
Now that Docker is up and running, we’ll need to set up busybox and OpenVPN. busybox is a super minimal docker image designed for embedded systems. We just want it for its small footprint. All we’re running is a VPN, so theres no need for extra fluff.
Get it set up with:
sudo docker run --name dvpn-data -v /etc/openvpn busybox docker run --volumes-from dvpn-data --rm kylemanna/openvpn ovpn_genconfig -u udp://$DOMAIN:1194 docker run --volumes-from dvpn-data --rm -it kylemanna/openvpn ovpn_initpki
The first command pulls the busybox image and creates a new container called ‘dvpn-data’. The second command starts a container that will hold the configuration files and certificates. Replace
$DOMAIN with the IP or domain of your server. Take note that port 1194 will need to be opened in your firewall. The thrid command will generate Diffe-Hellman parameters. It will take a long time so just be patient.
To open the required port in firewalld, issue the following command:
sudo firewall-cmd --permanent --zone=public --add-port=1194/udp
Now we need to create the credentials that will allow your client to connect to the VPN.
sudo docker run --volumes-from dvpn-data --rm -it kylemanna/openvpn easyrsa build-client-full $CONNECTION_NAME nopass sudo docker run --volumes-from dvpn-data --rm kylemanna/openvpn ovpn_getclient $CONNECTION_NAME > $CONNECTION_NAME.ovpn
Replace $CONNECTION_NAME with whatever you want to call your VPN connection. I named mine after my server name. You will be asked to create a password during the process, just pick one. It will take a while to do some crypto stuff, but eventually you’ll get an ovpn file in your current director. This is what will allow you to add the connection to your client. You will need to securely move this to the machine that will be connecting to your vpn.
scp are good options. You could even use a usb thumb drive.
Since the first machine I used this VPN for was a Mac I use at work, I chose Tunnelblick for my client. After it’s installed, double clicking on the ovpn file is all the set up that was needed to add the connection to Tunnelblick on Mac. Consult your client’s documentation if this doesn’t work for you.
Now that we’ve got all of the docker stuff out of the way, let’s create a custom systemd service file so we can manage our new container with the
systemctl command. SystemD service files are like init or Upstart scripts, but can be more robust and even take the place of Cron.
In CentOS 7 and Arch, these files are kept in
/etc/systemd/system/ so we’ll put our’s their too. Fire up your text editor of choice, for me it’s
sudo vim /etc/systemd/system/dvpn.service, and paste in the following:
[Unit] Description=OpenVPN Docker Container Requires=docker.service After=docker.service [Service] Restart=always ExecStart=/usr/bin/docker run --name vpn --volumes-from dvpn-data --rm -p 1194:1194/udp --cap-add=NET_ADMIN kylemanna/openvpn ExecReload=/usr/bin/docker stop && /usr/bin/docker run --name vpn --volumes-from dvpn-data --rm -p 1194:1194/udp --cap-add=NET_ADMIN kylemanna/openvpn ExecStop=/usr/bin/docker stop vpn [Install] WantedBy=local.target
There’s a lot going on here, so let’s break it down. The stuff in the
[Unit] section is straigtforward enough. We give our service file an arbitrary description. The
After=docker.service mean that this service won’t start until after the docker service has started.
Restart=always means that our service will restart if it fails. The
ExecStart= tells systemd what to run when we start the service. Let’s break this command down further, to help you understand what’s going on here:
docker run– This starts a process in a new container, pulling from the image specified later in the command.
--volumes-from dvpn-data– This gives our new container access to the dvpn-data container, where our certificates live.
--rm– This makes our container be destroyed after stopping.
-p 1194:1194/udp– This maps your physical machine’s port 1194 to the container’s port 1194.
--cap-add=NET_ADMINThis gives your container the ability to access the network stack on your physical machine.
kylemanna/openvpn– This specifies the image from which to start the container.
You can find more info about the
docker run command in the Docker documentation, it has tons of options. Of course, you could just check the
man page for it, with
man docker run.
[Install] section basically allows us to enable the service to be enabled to start at boot. You can read more about systemd service files in this excellent tutorial: Understanding Systemd Units and Unit Files.
Now that our service is created we can start it and enable it to load at boot with:
sudo systemctl start dvpn.service sudo systemctl enable dvpn.service
You can also check its status with
sudo systemctl status dvpn.service
And that’s it! You now have a SystemD managed, Docker controlled, OpenVPN set up. Enjoy!
UPDATE:I tried following my own guide to create a vpn on my home Arch Linux rig and ran into some problems. You might get iptables errors when attempting to start the dvpn service file created above.
┌─[jay@hal]─(~) └─[13:19]$ docker run --name vpn --volumes-from ovpn-data --rm -p 1194:1194/udp --cap-add=NET_ADMIN kylemanna/openvpn Error response from daemon: Cannot start container d8afcbc7069b0530893779c9abf4d10aa73ab53f820c310a8baf2b956f79877c: failed to create endpoint vpn on network bridge: iptables failed: iptables --wait -t nat -A DOCKER -p udp -d 0/0 --dport 1194 -j DNAT --to-destination 172.17.0.2:1194 ! -i docker0: iptables: No chain/target/match by that name. (exit status 1)
It is possibly due to either the
xt_conntrack kernel module not loading or because you simply need to restart the firewalld and docker daemons to reload the iptables rules. Additional info can be found here. Try restarting the daemons first with:
sudo systemctl restart firewalld sudo systemctl restart docker
If that doesn’t work, try loading the kernel module:
sudo modprobe xt_conntrack