How to effectively use SSH as a port-forwarding tunnel

The problem: You have an expensive piece of bespoke hardware box, $A$, on a private subnet $S_1$. Your computer of interest, $C$, is sat on a different subnet, $S_2$. There is a linux router between the two, $B$, running sshd. You wish $C$ (or a virtual machine thereon) to talk to $A$, but don’t want the rest of $S_1$ to be accessible to all of $S_2$ or vice-versa. In short, you want something on $C$ to use a specific (virtual) network interface to go $C\rightarrow B\rightarrow A$. I encountered this problem in the context of developing MRI hardware and attaching a remote debugger, and although there are many ways to do it, the following is crude, encrypted, and therefore effective.

A solution: This requires root access on $B$ in order to create a new network device. You can edit sshd.conf, enable root, login, and then disable root login a few seconds later – connections are persistent in modern versions of ssh. I reccomend using exchanged keys over passwords for login. You also need to be root on the host computer (i.e. $C$)

  • First of all, create an ssh tunnel between $C$ and $B$: ssh -o Tunnel=ethernet -w 0:0 root@$B$. This creates a new network interface (which is down initially) on $B$ (-o Tunnel=ethernet), and forwards all traffic to it (-w 0:0) before waiting in the background (-f)
  • On $B$, further characterise the tunnel we’re interested in, and set up port forwarding:

    su
    echo 1 > /proc/sys/net/ipv4/ip_forward #Enable NAT
    iptables -t nat -A POSTROUTING -o eth1 -j MASQUERADE #Set up DNAT 
    iptables -A FORWARD -i eth1 -o tap0 -m state --state RELATED,ESTABLISHED,NEW -j ACCEPT #Enable connections from the private subnet S1 out
    iptables -A FORWARD -i tap0 -o eth1 -j ACCEPT #Enable connections from the tunnel to the the private subnet, i.e. from S2 to S1
    #Bring up the tunnel: 
    ifconfig tap0 up 
        
    #Configure the tunnel as you like: 
    ifconfig tap0 192.168.0.1 netmask 255.255.255.0 #Adjust to taste; the scanner uses 172.16.0.x internally so I chose this on a whim 
    
  • Back on $C$, it’s now time to bring the virtual tunnel up, i.e. sudo ifconfig tap0 up, and configure your program to speak to tap0. I just bridged a VM’s network adapter to tap0, and configured 192.168.0.1 as the default gateway: it worked like a charm (i.e. the VM could access 172.16.0.x happily).