Docker – Allow Docker containers to connect to OpenVPN clients on the host tunnel interface

dockeropenvpn

I have the following setup :

  • A CentOS host running the docker service
  • A user defined docker bridge network
  • 2 Docker containers connected to that user defined bridge network
  • An OpenVPN installation (currently running on the host. Can also run in a docker container)
  • Some clients connected to the OpenVPN

How can I allow for docker containers on the docker bridge network to communicate with the openvpn clients on the tun0 network ?

I would like to be able to have tcp based communication between docker1 (10.10.0.3) and clients connected to the vpn (172.19.0.x range) in a transparent way.

What do I need to setup on docker (networking / iptables / …) side and on the host (iptables ?)

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) and push "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:

$ sudo sysctl -w net.ipv4.ip_forward=1
$ sudo iptables -A FORWARD -i tun+ -j ACCEPT
$ sudo ip route add 192.168.255.0/24 via <IP address of OpenVPN server container>

Anyway, here are the options I've used to set-up the server:

$ docker run --rm --net=none -it -v $PWD/files/openvpn:/etc/openvpn kylemanna/openvpn:2.4 ovpn_genconfig -u udp://<FQDN> -N -d -c -p "route <docker net IP> <docker net range>" -e "topology subnet"

This should generate a server config file similar to:

server 192.168.255.0 255.255.255.0
verb 3
key /etc/openvpn/pki/private/vpn.example.com.key
ca /etc/openvpn/pki/ca.crt
cert /etc/openvpn/pki/issued/vpn.example.com.crt
dh /etc/openvpn/pki/dh.pem
tls-auth /etc/openvpn/pki/ta.key
key-direction 0
keepalive 10 60
persist-key
persist-tun

proto udp
# Rely on Docker to do port mapping, internally always 1194
port 1194
dev tun0
status /tmp/openvpn-status.log

user nobody
group nogroup
client-to-client

### Push Configurations Below
push "dhcp-option DNS 8.8.8.8"
push "route 172.20.20.0 255.255.255.0"

### Extra Configurations Below
topology subnet

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):

$ docker network create --attachable=true --driver=bridge --subnet=172.20.20.0/24 --gateway=172.20.20.1 docker-net-vpn
$ docker run --rm --net=none -it -v $PWD/files/openvpn:/etc/openvpn kylemanna/openvpn:2.4 ovpn_genconfig -u udp://vpn.example.com -N -d -c -p "route 172.20.20.0 255.255.255.0" -e "topology subnet"
$ docker run --detach --name openvpn -v $PWD/files/openvpn:/etc/openvpn --net=docker-net-vpn --ip=172.20.20.2 -p 1194:1194/udp --cap-add=NET_ADMIN kylemanna/openvpn:2.4
$ sudo sysctl -w net.ipv4.ip_forward=1
$ sudo iptables -A FORWARD -i tun+ -j ACCEPT
$ sudo ip route add 192.168.255.0/24 via 172.20.20.2

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 with docker 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:

$ NET_VPN_BRIDGE="br-$(docker network inspect -f '{{.Id}}' docker-net-vpn | cut -b-12)"
$ sudo iptables -A FORWARD -i tun+ -o ${NET_VPN_BRIDGE} -j ACCEPT
Related Topic