Using SSH Tunnels over OpenVPN

Using the schematic below as an example, the requirement is to run a bespoke application on the laptop that sends TCP data on port 6000 to the remote Linux servers C & E.

There is no network route between Server A and Server C or E.

The solution was achieved using multiple SSH Tunnels.

This design is based on the earlier 19.07 software, I did try upgrading to a newer version but the performance was affected. There are differences in the firewall configuration that you will require to check if using current software.

The initial task was to get OpenVPN running on the router running OpenWRT, this was carried out using this straightforward to follow process:

Once OpenVPN was configured and a port forward from the broadband router was added (UDP port 1194) it was possible to connect using PuTTY to Server A over SSH.
It is possible to log into Server B & D over SSH from Server A, however from A you can't reach C or E. From B you can SSH to C and from D you can SSH to E. This is where an SSH tunnel can provide a working path end to end, two tunnels are required, the first from Server A to B or D, the second tunnel runs from B to C or D to E.

Server A has two network interfaces, eth0 is on the main 10.0.0.0 subnet, eth1 is connected to the 192.168.1.0 subnet provided by the broadband router.

The OpenWRT router needs to know what to do with packets sent from the laptop to Server C or E. This requires static routes to be added to the network configuration that have the eth1 ip address (192.168.1.10) as their gateway. The route can be added either in the web interface or by editing the /etc/config/network file.



Running the laptop application with the static routes the port 6000 data is now present on the eth1 interface and can be shown using tcpdump.
In order to get the packets from eth1 to eth0 on the main Linux server it is necessary to add the following entries into the firewall table:

iptables -t nat -A PREROUTING -i eth1 -p tcp --dport 6000 -j REDIRECT --to-port 6001 -d 10.3.0.1
iptables -t nat -A PREROUTING -i eth1 -p tcp --dport 6000 -j REDIRECT --to-port 6001 -d 10.5.0.1

It was also necessary to enable the forwarding between the eth0 and eth1 interfaces for port 6000 using the following:

iptables -A FORWARD -i eth1 -o eth0 -p tcp -m tcp --dport 6000 -j ACCEPT

Anyone paying attention will notice that the final destination port in the PREROUTING table is 6001, this is due to port 6000 already being in use on Servers B & D, it is therefore necessary to move the data to a different port.

An SSH tunnel from Server A to B is started using the following command:

ssh -v -4 root@10.2.0.1 -L 0.0.0.0:6001:10.2.0.1:6001

Once running any packets from the laptop to UDP port 6000 will be present on Server B and can be shown using tcpdump. A similar tunnel can be setup for the Server D connection using:

ssh -v -4 root@10.4.0.1 -L 0.0.0.0:6001:10.4.0.1:6001

Now that packets have reached Server B & D it is necessary to create a second tunnel to reach the final destination servers C & E. The changed 6001 port also requires to be moved back to the original port 6000. The SSH tunnels in server B & D are started using:

ssh -tt -4 root@10.3.0.1 -L 0.0.0.0:6001:10.3.0.1:6000

Or

ssh -tt -4 root@10.5.0.1 -L 0.0.0.0:6001:10.5.0.1:6000

With both tunnels setup, the client application running on the laptop can access the server application running on Server C & E. Any server that can be connected to over SSH should be able to be routed as part of the tunnel. To drop the second tunnel it is necessary to use "Ctrl-C"

I don't think there was any other way to meet this requirement but would appreciate input from others on the setup.

In the actual system this has been implemented in there is at least 50 remote servers that need connected to from the laptop. To make the ssh tunnel setup easier I have created a bash script that takes the Server name as an argument ($1)

Here is the script:

#!/bin/bash


echo "FALSE" > count.txt
awk '/'$1'/{print}' /root/sitelist.txt | while read server_name ip_address ip_host

do

echo "TRUE" > count.txt
ssh  -tt -4 root@$ip_host -L 0.0.0.0:6001:$ip_host:6000
done

if grep -q "FALSE" count.txt; then
echo "Unable to match supplied parameter with name '$1' in sitelist.txt file"
fi

@StuartP, Kudos to you for providing a diagram and getting it working.

I'll include some comments / questions, but don't need a response. I may not be able to continue the discussion.

The diagram is a little confusing as it seems to mix layer3 routeing and layer2 switching together. It appears than everything below ServerA is on a flat network w/ 10.0.0.0/8. But maybe there is some routing in there represented by the WAN clouds. I'm sure there is are requirements and constraints that I don't know or understand.

First first thought would be to simplify the network so you don't have to do all the ssh tunneling.
In other words, cheat. Wherever you put in SSH tunnels instead putting in routing and control access with firewalls. Probably not feasibly.

Another option may be to Pick a specific host like Server A or OpenWRT router and have every host make a VPN connection to it, maybe with Wireguard but I guess OpenVPN would work. Then, they can all communicate over some specific VPN network like 172.16.0.0/16. Not sure if it would be a violation or benefit if ServerC can talk to ServerE.

1 Like

Thanks for the reply, the 10,0.0.0 network is a lot more complicated and is something I have no control over. The network is largely a flat layer 2 network so theoretically I should be able to reach any device but the configuration of the switches prevent this. In addition within the WAN cloud element there is layer 3 routing. I did struggle with trying to represent the example network in the diagram but in my head that makes sense.

1 Like