Start an OpenVPN server so that other users (i.e. workers) can connect to your internal network.
The network architecture looks like this:
graph LR subgraph Internal Network subgraph Docker Host<br/>192.168.13.20 GW[OpenVpn gateway <br/> 18.104.22.168 <br/> 192.168.255.1] end A[Host A <br/> 192.168.13.1] B[Host B <br/> 192.168.13.2] end Client[Client <br/> 192.168.255.2] --> GW GW --- A GW --- B
We’ve got three separate subnets:
192.168.13.0/24- the target internal network (i.e. company network)
172.17.0.0/16- docker internal bridge network
192.168.255.0/24- openvpn network, from which addresses are assigned to clients
We want to be able to connect through OpenVPN and access any host in the internal network. This will be achieved through routing and NAT (as opposed to bridging, where VPN clients would get IP addreses from internal network).
Docker to the rescue
Let’s start with OpenVPN docker image. The heavy lifting has already been done and there is an image at https://github.com/kylemanna/docker-openvpn/tree/master that not only starts OpenVPN server, but also facilitates registering new clients and generating profiles for them. There’s also a guide on how to use it with docker-compose. Sweet.
docker-compose.yml looks like this:
version: '2' services: openvpn: cap_add: - NET_ADMIN image: kylemanna/openvpn container_name: openvpn ports: - "1194:1194/udp" restart: always volumes: - ./openvpn-data/conf:/etc/openvpn
Before running openvpn server itself, we need to generate it’s config and certificates, using scripts provided in the image.
> docker-compose run --rm openvpn ovpn_genconfig -u udp://VPN.SERVERNAME.COM > docker-compose run --rm openvpn ovpn_initpki
You will be asked for private key passphrace and Common Name for server certificate, then DH keypairs will be generated. When it says it’s going to take a long time - it really is. Go make yourself a tea.
ovpn_genconfig script that we used in previous step generates two config files:
ovpn_env.sh. It takes a bunch of additional arguments to help you customize the config. You can either play along with it or edit the generated config manually.
ovpn_genconfig also does some basic
iptables configuration, which otherwise you would have to edit by hand, so I’ll stick with the script.
Let’s see what
ovpn_genconfig can do for us:
> docker-compose up -d openvpn > docker-compose exec openvpn bash bash-4.3# ovpn_genconfig -? Invalid option: -? usage: /usr/local/bin/ovpn_genconfig [-d] -u SERVER_PUBLIC_URL [-e EXTRA_SERVER_CONFIG ] [-E EXTRA_CLIENT_CONFIG ] [-f FRAGMENT ] [-n DNS_SERVER ...] [-p PUSH ...] [-r ROUTE ...] [-s SERVER_SUBNET] optional arguments: -2 Enable two factor authentication using Google Authenticator. -a Authenticate packets with HMAC using the given message digest algorithm (auth). -b Disable 'push block-outside-dns' -c Enable client-to-client option -C A list of allowable TLS ciphers delimited by a colon (cipher). -d Disable default route -D Do not push dns servers -k Set keepalive. Default: '10 60' -m Set client MTU -N Configure NAT to access external server network -t Use TAP device (instead of TUN device) -T Encrypt packets with the given cipher algorithm instead of the default one (tls-cipher). -z Enable comp-lzo compression.
In this scenario, we want to:
- enable NAT (
- push (
-p) routes to the client, so it knows to it should use vpn network to connect to company network
- use company’s internal dns for name resolution (
-n) and set default domain to
Here’s the command to do that:
# remove old ovpn_env.sh > docker-compose run --rm openvpn rm /etc/openvpn/ovpn_env.sh # generate new config files > docker-compose run --rm openvpn ovpn_genconfig -N -d -n 192.168.13.6 -u udp://vpn.mycompany.net -p "dhcp-option DOMAIN mycompany.net" -p "route 192.168.13.0 255.255.255.0" -p "route 172.17.0.0 255.255.0.0"
The generated config will be in
./openvpn-data/conf and it should look like this (you may also want to take a look at
server 192.168.255.0 255.255.255.0 verb 3 key /etc/openvpn/pki/private/dev.legimi.com.key ca /etc/openvpn/pki/ca.crt cert /etc/openvpn/pki/issued/dev.legimi.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 ### Push Configurations Below push "dhcp-option DNS 192.168.13.6" push "dhcp-option DOMAIN legimi.com" push "route 192.168.13.0 255.255.255.0" push "route 172.17.0.0 255.255.0.0"
Now that openvpn server is configured, make sure it is up and running:
> docker-compose up
To connect, we’ll need a client profile.
# Generate a client certificate (you will be asked for a passphrase) > export CLIENTNAME="your_client_name" > docker-compose run --rm openvpn easyrsa build-client-full $CLIENTNAME # Generate client profile for openvpn > docker-compose run --rm openvpn ovpn_getclient $CLIENTNAME > $CLIENTNAME.ovpn
Finally, connect to openvpn:
> openvpn -c your_client_name.ovpn
If everything goes fine, you should be able to ping the internal network (i.e. 192.168.13.1 host).
If you cannot connect to openvpn:
- make sure
openvpncontainer starts without errors and port 1194 is exposed at docker host
- make sure port 1194 is open at vpn url you specified when configuring (vpn.mycompany.net)
If you can connect to openvpn, but cannot ping internal network:
- check if the client machine has an IP address from openvpn network assigned (
192.168.255.1(that’s the default gateway for openvpn connections). If you can’t, there’s probably something wrong with openvpn config.
openvpncontainer’s internal IP (
172.17.0.x) and ping it.
- check routing tables on client machine. There should be routes for networks
192.168.13.0going through Gateway
- check if you can ping internal network from