Routing Docker traffic through a VPN container
I'm using a VPN for years now but I recently decided to route the traffic of some of my container through a VPN connection to by-pass some country-specific restrictions and to enhance my privacy.
I'm using a VPN for years now but I recently decided to route the traffic of some of my container through a VPN connection to by-pass some country-specific restrictions and to enhance my privacy.
I found out a docker container which suits perfectly to what I wanted to do and I will show you how to quickly built this setup.
Prerequisites
Folder structure
As a first step, you have to create a folder structure as the following one.
docker-vpn-project/
|_ docker-compose.yaml
|_ vpn
|_ config
|_ my-container # If needed
|_ config
OpenVPN files
To be able to work, this container will need some OpenVPN configurations from my current VPN provider, Windscribe.
On their website, you can easily find an OpenVPN configuration generator which provides you the key elements to continue:
- An *.ovpn configuration file
- A certificate and a private key
- An username/password for the authentication
You have now downloaded and extracted all the files and it looks like this:
Now it's time to copy these files into your VPN configuration folder.
Authentication file
You will need to create a file "vpn.auth" for the credentials. This file must contains only 2 lines of text, the first one is your username and the second line is the password, both provided by your VPN provider.
Now you need to open the *.ovpn file to target the authentication file we have create. You just need to add the path of the file inside the container /vpn/vpn.auth
following the existing auth-user-pass section (line 7).
How does it look on my Synology NAS:
docker-vpn-project/
|_ docker-compose.yaml
|_ vpn
|_ config
|_ ca.crt
|_ ta.key
|_ vpn.auth
|_ Windscribe-Zurich-Alphorn-GCM.ovpn
|_ my-container # If needed
|_ config
Docker-compose.yaml
Now let's deep-dive into the docker-compose.yaml file. For this example, I route through VPN a librespeed container but you can easily use this also for Sonarr, Radarr, ruTorrent, qbitTorrent, Jackett, Prowlarr, etc.
version: "2.3"
services:
vpn:
container_name: my-vpn
image: dperson/openvpn-client:latest
restart: unless-stopped
networks:
default:
ipv4_address: 172.51.0.2
dns:
- 9.9.9.9
ports:
- 1234:80
cap_add:
- NET_ADMIN
sysctls:
- net.ipv6.conf.all.disable_ipv6=0
security_opt:
- label:disable
environment:
- PUID=1026
- PGID=100
- TZ=Europe/Zurich
devices:
- /dev/net/tun:/dev/net/tun
volumes:
- ./vpn/config:/vpn
command: '-f "" -r 192.168.0.0/24'
healthcheck:
test: ["CMD-SHELL", "if [ $$(curl --silent https://ifconfig.co/country-iso) = 'DE' ]; then exit 0; else exit 1; fi"]
start_period: 5s
interval: 5s
timeout: 10s
retries: 3
my-container:
image: linuxserver/librespeed:version-5.2.4
container_name: my-container
restart: unless-stopped
network_mode: "service:vpn"
environment:
- PUID=1026
- PGID=100
- TZ=Europe/Zurich
networks:
default:
driver: bridge
ipam:
config:
- subnet: 172.51.0.0/16
gateway: 172.51.0.255
The key section are the following:
- vpn.networks - Used to define a fixed IP address to your VPN container into the current network range
- vpn.dns - The DNS address your VPN container is going to use to resolve domains. I'm using Quad9
- vpn.dns - The ports you need to map. As all the traffic is redirected through the VPN, you need to map all the ports into the VPN container and not your services container
- my-container.network_mode - No custom port declaration, we are delegating the complete network management to the VPN service, running inside the same docker-compose.yaml file, named vpn.
Note: If you want to run the VPN container in a separate docker-compose.yaml or docker run command, you will need to setcontainer:vpn
instead ofservice:vpn
- networks - We are defining the default network of this stack
- healthcheck - This healthcheck command is going to define my VPN container as unhealthy if its connection is not located in Germany
And now you are ready to test!
Container creation and connectivity
You can now create and start your Docker stack with the command:sudo docker-compose up -d
You can check your VPN container logs to check if everything is fine. It should look like this:
As soon as both of the containers are created and started, it's time to check your connectivity and especially your IP address.
Let's start with the VPN container, you can run the following command:
sudo docker exec -it my-vpn curl ifconfig.co/json
You can see below that your IP address is now located in Zurich, the VPN is correctly connected!
Now, we are going to check the librespeed container, you can run the following command:sudo docker exec -it my-container curl ifconfig.co/json
The result is exactly the same! Congratulations, you are now routing all your Librespeed container traffic through a VPN container! 🎉
Debug
If you encounter any issue related to the /dev/net/tun
device interface, as I did on my Synology NAS, you might need to create it before.
I made a small bash file you can easily run to fix this issue!
- Create the file
sudo nano tun.sh
- Paste this content inside
#!/bin/bash
# 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
- Make it executable
sudo chmod +x tun.sh
- Execute it
sudo ./tun.sh