OpenVPN docker: IP address of client on local network services

We are running on the same host 2 docker containers:

  1. docker-openvpn
  2. php5.6-apache

Clients are assigned a Static IP address (as decribed here https://github.com/kylemanna/docker-openvpn/blob/master/docs/static-ips.md) and it works great: when a client connects to the VPN, if the common name matches the file name in the /etc/openvpn/ccd directory, it gets the configured IP address as it should.

utun10: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 1500
	inet 192.168.254.1 --> 192.168.254.2 netmask 0xffffffff

Is it possible for services on the same docker host to view that IP address as the source? We tried this with php5.6-apache and it’s showing the OpenVPN’s container IP address instead of the IP address assigned to the specific client.

[/] # docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' ovpn
10.0.3.2
[/] # docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' my-apache-php-app
10.0.3.3

When doing curl http://192.168.1.10:80 (being 192.168.1.10 the IP address of the Host), this is the REMOTE_ADDR as returned by PHP:

    [SERVER_NAME] => 10.0.3.3
    [SERVER_ADDR] => 10.0.3.3
    [SERVER_PORT] => 80
    [REMOTE_ADDR] => 10.0.3.2

We want to have 192.168.254.1 instead of 10.0.3.2 in the REMOTE_ADDR .

After digging into this, it is only possible using OpenVPN in tap mode (Layer 2) instead of tun mode (Layer 3). The configuration is a bit tricky but the main changes are

server-bridge [OpenVPN server IP] 255.255.255.0 [IP-start] [IP-end]
dev tap0
client-to-client
topology subnet

You have to use the same network range (as the one where your services like Apache, MySQL etc are running on), as we are using bridging instead of routing. If you are using a DHCP server on that network, remember to reserve IP addresses in the [IP-start]-[IP-end] range.

You also have to use the bridge-start script as provided by the OpenVPN community. I also had to make a few changes to it (appending those lines at the bottom of the script):

$eth_gateway=[IP address of your gateway/router]
route add default gw $eth_gateway
iptables -A INPUT -i tap0 -j ACCEPT
iptables -A INPUT -i br0 -j ACCEPT
iptables -A FORWARD -i br0 -j ACCEPT

#https://blog.michael.kuron-germany.de/2015/07/arp-and-multicast-packets-lost-with-openvpn-in-tap-mode/
brctl setageing br0 0

And of course update the ovpn_run script to run it before starting the OpenVPN server daemon:

echo "Running bridge-start"
/bin/sh /usr/local/bin/bridge-start
echo "Running 'openvpn ${ARGS[@]} ${USER_ARGS[@]}'"
exec openvpn ${ARGS[@]} ${USER_ARGS[@]}

You’ll also have to update your client configuration to use tap instead of tun, of course.

Using this configuration I’m able to use ACL on my services based on IP addresses of the clients connected to the VPN. Win!