A friend and I ran into this exact problem where we wanted to have docker support multiple network interfaces servicing requests. We were specifically working with the AWS EC2 service where we were also attaching/configuring/bringing up the additional interfaces. In this project, there is more than what you need so I will try to only include what you need here.
First, what we did was create a separate route table for eth1
:
ip route add default via 192.168.1.2 dev eth1 table 1001
Next we configured the mangle table to set some connection marks coming in from eth1
:
iptables -t mangle -A PREROUTING -i eth1 -j MARK --set-xmark 0x1001/0xffffffff
iptables -t mangle -A PREROUTING -i eth1 -j CONNMARK --save-mark --nfmask 0xffffffff --ctmask 0xffffffff
Finally we add this rule for all fwmark
s to use the new table we created.
ip rule add from all fwmark 0x1001 lookup 1001
The below iptables
command will restore the connection mark and then allow the routing rule to use the correct routing table.
iptables -w -t mangle -A PREROUTING -i docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j CONNMARK --restore-mark --nfmask 0xffffffff --ctmask 0xffffffff
I believe this is all that is needed from our more complex example where (like I said) our project was attaching/configuring/bringing up the eth1
interface at boot time.
Now this example would not stop connections from eth0
from servicing requests through to docker0
but I believe you could add a routing rule to prevent that.
I wrote a python program to wrap ipsec that installs iptables entries to allow docker containers to talk to the VPN tunnel:
https://github.com/cbrichford/docker-ipsec
Instead of doing:
ipsec up
You do:
docker-ipsec up
This script might need some work for use with the latest docker networking features, but worked with the old default docker0
bridge.
If you don't want to use my script to edit iptables here is how you construct the iptables command:
- Figure out the virtual IP address of the host in the VPN. Call this virtualIP.
- Figure out the CIDR block for IP address in the VPN. If IP address in the VPN are always of the form 10.10.X.X then your CIDR block would be 10.10.0.0/16. Call this vpnSubnet
- Figure out the default route interface. This will be something like "eth0". This is the command I use to find it:
sudo ip route show | grep -e "^default" | awk -- "{ print \$5 }"
. Call this defaultRouteInterface
- Figure out the CIDR block for the docker network you want to grant access to your VPN. I this command to find it:
sudo ip route show | grep -e "[[:space:]]dev[[:space:]]docker" | awk -- "{ print \$1 }"
. Call this dockerSubnet.
The iptables command you need to run is:
sudo \
iptables \
-j SNAT \
-t nat \
-I POSTROUTING 1 \
-o ${defaultRouteInterface} \
-d "${vpnSubnet}" \
-s "${dockerSubnet}" \
--to-source "${virtualIP}"
Best Answer
Context
I have been using the very good Docker container from Kyle Manna (https://github.com/kylemanna/docker-openvpn). I'm using the so-called "paranoid" documentation to set-up my OpenVPN server, but in my view this should be the standard way and not the paranoid way.
Configuration
In order to allow bi-directional connection between selected Docker containers and the VPN clients, you need to create a Docker network on which you are going to attach container which should be allowed to be accessed by the VPN clients. The VPN server is going to be one of those containers.
The VPN server should have the
client-to-client
,topology subnet
,dev tun0
(or other tun device) andpush "route <docker net IP> <docker net mask>"
configured.The host of the VPN server should be configured to support forwarding of IP packets from one subnet to another. This means setting the sysctl ip_forward to 1 (it should be the case if you have Docker install), allowing packets from the tun device to go through the iptables FORWARD chain and setting proper routing. This can be summarise with these commands:
Anyway, here are the options I've used to set-up the server:
This should generate a server config file similar to:
Concrete example
I will now take a concrete example. In this example, I will run the above mention OpenVPN server inside Docker on host vpn.example.com. This container is attached to the Docker network docker-net-vpn. Here are the commands (in this example I generate the server configuration directly on the server and I skip the CA generation, please follow the paranoid documentation of the above mention project instead):
The first command creates a dedicated new Docker network which define a new subnet. We will attach the OpenVPN server to this network.
The second one creates the OpenVPN configuration using the same subnet as defined in the 1st command.
The third one creates the OpenVPN server. It is attached to the newly created Docker network and uses a fix IP.
The fourth and fifth commands configure IP forwarding.
The last command adds a new route towards the VPN client configuration via the OpenVPN container fixed IP.
Note
I haven't tried it, but it should be possible to restrict the FORWARD rule for iptables. The Docker network creation created a new bridge device. This bridge is named
br-<ID>
with ID being the first 12 characters of the Docker network ID. This ID can be obtained withdocker network inspect -f '{{.Id}}' docker-net-vpn | cut -b-12
. Therefore the following command is maybe more restrictive (so better security-wise) but should still allow our traffic to be routed: