Tunnel traffic of docker containers
Solution: share container network
You want a container to be connected to a Wireguard VPN? You want its traffic to be completely tunneled through the VPN without installing and configuring the VPN client yourself?
I tried in different ways to achieve that, I tried installing the wireguard client into a newly created Dockerfile, using as a base image the wanted base image with the programs you want to run. I tried to play around with iptables rules. None of these solutions were effective, easily reusable and right.
There is an easy solution: container’s network sharing.
Using Docker Compose, you can define the containers you need and then add a wireguard
client container in the same compose file. Once the wg0.conf
client configuration is passed to such container, all is needed is to instruct the other containers to use the “wireguard” client container’s network.
Doing this way, the chosen containers will be sharing the wireguard container’s network, thus tunneling out all the traffic.
The interesting config to set the container’s network is the following:
network_mode: container:wireguard-container-name
Note: the wireguard container needs NET_ADMIN permissions. This adds some level of privileges to the container, which is to take into consideration.
Container exposed ports
One more thing to remember is that if your containers (not the wireguard one) expose any ports locally, you now need to define those ports exposed in the wireguard container.
Docker compose example
Copy your wg0.conf
into ./wg
.
Here is a docker compose example file that creates a qbittorrent container whose traffic will be completely tunneled out through the wireguard container.
The image linuxserver/wireguard has a “client” mode, which makes it act as a wireguard client when a wg0.conf file is supplied.
The container “qbittorrent” is sharing the wireguard container network, this is the magic bit that let the container’s traffic be tunneled out through the wireguard container.
---
version: "3.7"
services:
qbittorrent-wg:
image: lscr.io/linuxserver/qbittorrent:latest
container_name: qbittorrent-wg
environment:
- PUID=1000
- PGID=1000
- TZ=Etc/UTC
- WEBUI_PORT=8080
volumes:
- ./config:/config
- ./data:/downloads
restart: unless-stopped
network_mode: container:wireguard-my
wireguard-my:
image: linuxserver/wireguard
container_name: wireguard-my
restart: unless-stopped
networks:
- net01
volumes:
- './wg:/config'
- '/lib/modules:/lib/modules:ro'
environment:
- PUID=1000
- PGID=1000
cap_add:
- NET_ADMIN
sysctls:
- net.ipv4.conf.all.src_valid_mark=1
networks:
net01:
driver: bridge
This is all.
Health checks
If you want to make 100% sure the containers traffic is properly tunneled, you can create a compose health check script.
Since you know the VPN server IP address, you can make sure the container’s exit IP is the same with a health check.
Create health_check.sh as follows:
#!/bin/bash
# Health check function for a single container
function check_container_health() {
local response
response=$(curl -s ipinfo.io/ip)
if [[ "$response" == "<INSERT VPN IP>" ]]; then
echo "$container_name is healthy."
exit 0
else
echo "$container_name is unhealthy."
exit 1
fi
}
# Health check for container
check_container_health
then share the file with the containers through a volume and set up the health check compose instruction.
Here is the updated compose file:
---
version: "3.7"
services:
qbittorrent-wg:
image: lscr.io/linuxserver/qbittorrent:latest
container_name: qbittorrent-wg
environment:
- PUID=1000
- PGID=1000
- TZ=Etc/UTC
- WEBUI_PORT=8080
volumes:
- ./config:/config
- ./data:/downloads
- ./health_check.sh:/health_check.sh
restart: unless-stopped
network_mode: container:wireguard-my
healthcheck:
test: ["CMD", "/bin/bash", "/health_check.sh"]
interval: 30s
retries: 3
timeout: 10s
wireguard-my:
image: linuxserver/wireguard
container_name: wireguard-my
restart: unless-stopped
networks:
- net01
volumes:
- './wg:/config'
- '/lib/modules:/lib/modules:ro'
- ./health_check.sh:/health_check.sh
environment:
- PUID=1000
- PGID=1000
cap_add:
- NET_ADMIN
sysctls:
- net.ipv4.conf.all.src_valid_mark=1
healthcheck:
test: ["CMD", "/bin/bash", "/health_check.sh"]
interval: 30s
retries: 3
timeout: 10s
networks:
net01:
driver: bridge
I hope you found this post helpful. If you have any questions or feedback, feel free to leave a comment below.
Last modified: 30 July 2023