In a previous post I described how to route traffic for a single Linux user through a VPN. For containers, the process is much simpler.
To begin, create a docker-compose
file (or a Portainer stack). This should include the containers you wish to protect, as well as a container connecting to your VPN. I've chosen to use a WireGuard container provided by the Linuxserver group.
version: "3"
services:
vpn:
image: lscr.io/linuxserver/wireguard
container_name: wireguard
cpus: 4
cap_add:
- NET_ADMIN
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/Berlin
volumes:
- /opt/wireguard:/config
- /lib/modules:/lib/modules
ports:
# Add ports here of other containers in the stack which you want to expose
- 8080:80 # port forward of the whoami testcontainer
sysctls:
- net.ipv6.conf.all.disable_ipv6=1 # Recomended if using ipv4 only
- net.ipv4.conf.all.src_valid_mark=1
restart: unless-stopped
testcontainer:
image: nginx
network_mode: service:vpn
depends_on:
- vpn
container_name: testcontainer
restart: always
networks:
default:
name: wireguard
# Use this if you created the network elsewhere
# external: true
Next, download the WireGuard configuration from your VPN provider. For my setup, I used Mullvad. From the available configurations (usually provided for different locations), select one and save it inside /opt/wireguard/wg_confs
as wg0.conf
.
Afterwards, modify the wg0.conf
file and add the PostUp
and PreDown
rules provided below. Essentially, these rules ensure that communication with internal networks outside the VPN remains intact. Without them, you wouldn't be able to access the exposed ports of your containers, such as dashboards.
I have taken this configuration from Linuxservers' blog.
Note that when using these specific routes in the WireGuard configuration, it's imperative that ports you want to forward for all your containers in the stack are explicitly specified in the VPN container. An example of this is how I specified port 8080
in the configuration above.
To verify your configuration, you can initiate a shell inside the container using:
docker exec -it testcontainer /bin/sh
For those using Portainer, there's a dedicated button for this purpose. Once inside, execute the following commands to test:
curl https://am.i.mullvad.net/connected # If you use mullvad
curl ifconfig.me # to get the outgoing IP from the container
Furthermore, from outside your container, you should be able to run curl 127.0.0.1:8080
to access the container's port.
Conclusion
By following the steps above, you can ensure that your containerized applications communicate securely via a trusted VPN.