Using openvpn-client with Docker
I recently worked out the correct incantation to get a set of containers to connect to the internet via a VPN using docker-compose. I run it on a QNAP NAS, but it should work on any Linux-like system (I couldn’t get it working on MacOS).
The following docker-compose.yml will create two containers - one to run the VPN client and a second that runs curl. I use this to run curl ipv4.canhazip.com to check that the container has connectivity and the VPN IP address is returned.
yaml---version: "3"services:vpn:container_name: vpnimage: dperson/openvpn-client:latestcap_add:- net_adminrestart: unless-stoppedvolumes:- /dev/net/tun:/dev/net/tun- ./vpn-config:/vpn # You'll need to provide thissecurity_opt:- label:disableports:- 8001:8001- 8002:8002- 8003:8003networks:- bridge_vpnentrypoint: ["/sbin/tini", "--", "/usr/bin/openvpn.sh", "-d"]curl:image: alpine/curlcontainer_name: curlrestart: unless-stoppednetwork_mode: service:vpnnetworks:bridge_vpn:
This compose file will expose ports 8001, 8002 and 8003 from any containers using network_mode: service:vpn and make them accessible via a bridge network. This is useful when running a service that connects to the internet using a VPN. (There are no exposed ports in this demo, but I wanted to make a note here as in my actual deployment some of the other services expose ports.)
The biggest change to this configuration compared to a lot of guides out there is the entrypoint line for the vpn service. The openvpn-client image supports a -d flag that adds some DNS related pre/post scripts. I found that these are required to make connectivity work via the VPN.
You may have noticed the vpn-config folder being mounted. This is where you’ll provide your VPN configuration and authentication files. I tested this with Private Internet Access. If you’re a PIA customer, you’ll need to create a file named vpn.auth with your PIA username on the first line, and password on the second line, plus a vpn.conf file with the following contents:
apacheclientdev tunproto udpremote sweden.privacy.network 1198resolv-retry infinitenobindpersist-key# persist-tun # disable to completely reset vpn connection on failurecipher aes-128-cbcauth sha1tls-clientremote-cert-tls serverauth-user-pass /vpn/vpn.auth # to be reachable inside the containercomp-lzoverb 1reneg-sec 0crl-verify /vpn/crl.rsa.2048.pem # to be reachable inside the containerca /vpn/ca.rsa.2048.crt # to be reachable inside the containerdisable-occkeepalive 10 30 # send a ping every 10 sec and reconnect after 30 sec of unsuccessfull pingspull-filter ignore "auth-token" # fix PIA reconnection auth error that may occur every 8 hours
This configuration connects to Sweden, but you can switch to any endpoint that you like. You’ll also need to download crl.rsa.2048.pem and ca.rsa.2048.pem from the PIA site.
Once you have everything configured, it’s time to create the containers by running docker-compose up -d. Once that’s complete, you can test your connection by logging in to the vpn service and making a HTTP call:
bashdocker-compose exec vpn bash -c "curl ipv4.canhazip.com"
If the above command returns an IP address successfully, you can also test it using the curl container which is configured to use the VPN for all network connectivity:
bashdocker-compose run curl ipv4.canhazip.com
At this point, you have a docker-compose setup that connects all of the containers configured via an OpenVPN connection. Replace the curl service with any other service you may want to run behind a VPN and enjoy as your traffic is safe from snooping eyes.