Docker – How to Ensure Docker Traffic in Swarm Passes Through VPN

dockerdocker-swarmiptablesopenvpn

I have two nodes in a Docker Swarm cluster. One of those nodes has an OpenVPN client connection to a VPN provider on interface tun0. My goals are,

  • Any services assigned to this node exclusively use the VPN connection
  • No leaks (i.e., DNS or other traffic)
  • If the VPN disconnects, all traffic gets dropped
  • Allow service discovery and connections to other containers in the Swarm

For DNS, I have added a dns entry to /etc/docker/daemon.json that uses the VPN provider's DNS servers that are only accessible through the VPN.

Here are the iptable rules I have come up with:

iptables -I DOCKER-USER 1 -o tun0 -j ACCEPT
iptables -I DOCKER-USER 2 -i tun0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
iptables -I DOCKER-USER 3 -j DROP

The resulting DOCKER-USER chain looks like this:

Chain DOCKER-USER (1 references)
 pkts bytes target     prot opt in     out     source               destination
    0     0 ACCEPT     all  --  *      tun0    0.0.0.0/0            0.0.0.0/0
    0     0 ACCEPT     all  --  tun0   *       0.0.0.0/0            0.0.0.0/0            ctstate RELATED,ESTABLISHED
    0     0 DROP       all  --  *      *       0.0.0.0/0            0.0.0.0/0

From basic tests like running nslookup and curl with the VPN connection on and off these rules seem to work, but I have very little experience with iptables. Is this the correct way of doing this?

Best Answer

IPtables is linear and it reads the rules from top to bottom until it reaches a ACCEPT, REJECT, or DROP target, and then it stops reading the rules. In your case, you want to have iptables -I DOCKER-USER -j DROP as the very last rule in your chain, otherwise every packet will be dropped. Also, there is no need for the RETURN rule at the end, because IPtables will stop reading rules once it reaches the DROP rule right above it. Those IPtables rules with tun0 look fine, but makes sure you have these rules as well:

iptables -t filter -I INPUT 1 -i tun0 -j DOCKER-USER
iptables -t filter -I OUTPUT 2 -o tun0 -j DOCKER-USER

For good practice, make sure you accept all loopback traffic, which will never reach the internet nor will it leave the machine:

iptables -t filter -I INPUT 3 -i tun0 -j ACCEPT

Let's go through your requirements one-by-one:

  1. Any services assigned to this node exclusively use the VPN connection

You would not use IPtables to do this. Run these commnds on the servers:

ip route add default via ${LOCAL_VPN_IP} 

I think OpenVPN typically uses 10.8.0.0/16, so the default gateway would probably be 10.8.0.1 or something like that. IProute2 (ip command) is built into the kernel, just like IPtables.

  1. No leaks (i.e., DNS or other traffic)

You should first redirect all traffic through the VPN by putting this in your OpenVPN's server config:

push "redirect-gateway autolocal"

This makes the clients put all of their traffic through the VPN, even DNS and such. If the OpenVPN server goes down, the internet will stop working on the clients.

  1. If the VPN disconnects, all traffic gets dropped

See step 2

  1. Allow service discovery and connections to other containers in the Swarm

I believe OpenVPN does this by default. I am able to ping/arp from one client to other clients that are on the OpenVPN server. You should definitely be able to access services that are on other clients.

I hope this answers your questions!