Sonntag, 15. März 2015

Tunneling OpenVPN through multiple hops

For my bachelor thesis I have to set up an experimental network configuration with multiple OpenVPN-clients and servers.

The goal is to establish a tunnel to a host by tunneling OpenVPN through multiple other hosts.

I use vagrant to set up some virtual machines on my real machine in order to experiment with the setup and the configurations.
Vagrant lets you take an image of a virtual machine and configure it through a configuration file (called Vagrantfile). In that file you can also tell vagrant to start multiple instances of that image with different configurations. You can also let vagrant do various installations and configurations for each instance separately, but I'll just use it to get some virtual machines running with certain network configurations.

The best way to get started with vagrant is to follow the "Getting Started" instructions on their page.

For me, the default configuration for the box I chose already set some network parameters that I didn't want, so I removed them in "~/.vagrant.d/boxes/$path_to_vagrantfile".

The Vagrantfile I use for my machines is pretty simple and consists mainly of a few entries like this:
config vm.define "host2" do |host1| = "trusty64" "private_network", ip: "" "private_network", ip: "" "forwarded_port", guest: 22, host: 23456, id: "ssh"

This sets the machine up with two additional interfaces (eth1 and eth2, eth0 is used for communication with the "outer" world, i.e. the host machine). The last line is necessary in order make ssh with multiple virtual machines work. Just give it another not-well-known-port for each instance.

My setup then looks something like this: 

I started with the ".2" addresses as the host pc automatically gets assigned the ".1" IP address.

Now that this is set up the OpenVPN-configuration can begin.

I want to try two different ways of establishing an OpenVPN-tunnel from Host4 to the network:

The first way is to establish a tunnel between Host4 and Host3 and then establish another tunnel between Host4 and Host2 through the first tunnel. Something like this:

The second way is to establish a tunnel between Host4 and Host3 and another tunnel between Host3 and Host2. Could look something like this (disregard the last tunnel in this picture):

For both I'll have to run OpenVPN-servers on Host2 and Host3. I'll skip the tunnel between hop2 (=Host2) and the receiver for now and will be happy if I can just ping it through the tunnels.

In the first version, I only have to run OpenVPN-clients at the sender. In the second, there also needs to be a client at hop1 (=Host3) that establishes the OpenVPN-tunnel between hop1 and hop2.

When configuring the OpenVPN-stuff I mostly followed the instructions here (unfortunately in german) and here.

One of my config files looks like this:

port 1194

proto udp

dev tun

ca ./easy-rsa2/keys/ca.crt
cert ./easy-rsa2/keys/server.crt
key ./easy-rsa2/keys/server.key

dh ./easy-rsa2/keys/dh2048.pem


ifconfig-pool-persist ipp.txt

push "route"
push "route"

keepalive 10 120

cipher AES-128-CBC


status openvpn-status.log

verb 3

The most complicated thing here probably is generating the keys and certificates. If you use easy-rsa even that is not that hard, though.

First Version:

There are two OpenVPN-servers running, one at Host3 and one at Host2.

Host4 first connects to Host3 and than through that connection to Host2. For the first connection, the following configuration is used:


dev tun

proto udp

remote 1194

resolv-retry infinite



ca ./keys/ca.crt
cert ./keys/host1.crt
key ./keys/host1.key

ns-cert-type server

cipher AES-128-CBC

verb 3

The second configuration file looks pretty much the same, only the IP of the remote server is changed.

With this I can connect Host4 to the OpenVPN-server running on Host3, but I can't ping Host2 (the echo-request arrives, but no reply is delivered). The reason for this is that I have to enable NAT at at Host3, otherwise the sender IP of the ping that arrives at Host2 is the 10.8.1.x-OpenVPN-IP that belongs to Host4. For my testing purposes, this is enough:

sudo iptables --table nat --append POSTROUTING --out-interface eth1 -j MASQUERADE

Now all packets that Host3 forwards through it's eth1 interface get NATed and I can ping Host2 and establish an OpenVPN connection to it. In order to be able to ping Host1, I still have to enable NAT at Host2 and then I'm done!

Relevant part of the routing table at Host4: via dev tun1 via dev tun0 dev eth1 proto kernel scope link src

Second Version:

There are still two servers and two clients, but now one of the clients runs on Host3. Basically it is the same setup twice. Only the NATing is a bit more complicated here. Also I had to enter the route to Host4 into the routing table of Host1 myself (this could also be done by adding the "push" command for the in the OpenVPN-server config of Host3).

Two NAT rules have to be applied, one at Host2 and one at Host3.

At Host2, the NATing should happen at tun1, as every packet that is forwarded through this interface should get the IP of Host2. The rule I added is:

sudo iptables --table nat --append POSTROUTING --out-interface tun1 -j MASQUERADE

At Host3, the NATing should happen at eth1, so that the packets that Host3 forwards to Host4 don't have the address as source. The rule is:

sudo iptables --table nat --append POSTROUTING --out-interface eth1 -j MASQUERADE

Now I can ping from Host1 to Host4 through the OpenVPN-tunnels.

The routing table at Host4 now looks like this (only relevant parts): via dev tun0 via dev tun0 dev eth1 proto kernel scope link src


  • After halting and starting the machines again, all openvpn clients and servers are started automatically.
  • The default network configuration of the machine can mess up the configurations for the individual instances.
  • A lot more I can't remember right now.

Keine Kommentare:

Kommentar veröffentlichen