SSH tunneling using an intermediary computer

tl;dr - I had to SSH tunnel with a proxy computer in the middle due to some weird ISP restrictions/regular OpenVPN not working properly for me. Basically the setup is to SSH tunnel from one machine to another, and run another tunnel on the proxy computer. I used this surprisingly low latency setup to run a SOCKS5 proxy that did the job. “Remote Port Forwarding” is what I was doing, and a super awesome blog post helped show me the way.`

UPDATES: After sharing this blog post on reddit’s r/linux, a few helpful users pointed out some improvements to the methodology/terminology in the article, check it out:

When it comes to linux system administration, truly understanding SSH along with Port forwarding via SSH is one of my weakest areas. I generally just google for what I need, use one or two one-off commands, and never think about it again. Unfortunately that doesn’t build much understanding into the wide breadth of things that ssh can do (and all the other things it enables), and all the other little utilities that one can use to diagnose problems. I find that I rarely use simple tools like netcat despite how useful they can be in diagnosing and solving problems, and just general application testing.

This time actually isn’t much different, but faced with a rather unique problem I was able to find my own solution (with lots of help from the internet), and learn something valuable – maybe this article will come to someone else’s aid in the future.

Background

When you purchase internet from your ISP (Internet Service Provider) they almost always give you a dynamic IP address. This means that the IP address that you’re given could change at any time (though it often doesn’t so often). This would be unimaginable in the physical world – imagine the houses on your street changing addresses (street, number, state, etc) every few days or few weeks. This works on the internet because the vast majority of traffic is going from a home computer (you) outwards to a relatively-small number of destinations on the internet. Websites on the internet like google.com operate behind fixed IP addresses (for example XXX.XXX.XXX.XXX) – they don’t change (unless something goes very wrong somewhere), so that all the computers and routers around the world can get to used to google.com being (“resolving” in DNS terms) XXX.XXX.XXX.XXX.

In this reality, if you want to reliably connect one user’s computer (behind an ISP) to another user’s computer (behind a possibly different ISP), you’re going to need to do some more work. One of the ways to set this up for yourself at the very least (given that you own multiple computers) is to set up Dynamic DNS. As you might be able to imagine, as your computer’s IP changes, there’s absolutely no way some external network could know what it changed to unless your computer checks in and updates the service. This is like sending a notification to a match making service whenever you change addresses – your computer needs to let some other service know that it’s IP has changed. While there’s many ways you could do this, the feature is very often built into routers and is often very easy to set up. Various sites offer free dynamic DNS for people to utilize.

Normally the process is pretty simple, and looks something like:

  • Ensure the port on your computer that you want to forward available (For example port 22 for ssh)
  • Ensure your router let’s through traffic, to the computer you want it to go to, on the right port (this often requires going through the router administration UI, which can be a mixed bag)
  • Test it all out, using your dynamic IP (from something like your phone, or a computer on a different network), which you can get from sites like whatismyip.org
  • Make it more permanent/easier with some sort of DDNS (Dynamic DNS), so you no longer have to worry about getting your changing IP every time

My problem

Earlier this year I was on a short vacation, traveling internationally, but I wanted to maintain a connection to my home computer (as well as the ability to pass traffic through it as necessary). I wasn’t too worried about having a VNC connection to the computer, just a terminal would do, but I wanted the ability to use my main tower (and relatively good home internet connection) from far away from my less powerful laptop.

My problem here is that it wasn’t possible for me to set up the Dynamic DNS services on my router due to the kind of connection that my ISP allowed my router to make to their upstream network. Well it’s either that or I’m just completely inept when it comes to setting up my local router and navigating network problems. Either way with the inability to set up Dynamic DNS for my router, I had to start looking at other options to make my home computer (which a IP address that is likely to change while I was on vacation). Luckily for me I happen to rent some servers with static IPs, that I often access from my laptop as well – looks like I’ve got an anchor point I could use!

The next thing was to figure out how exactly to set this up – it should be possible to bridge traffic between two computers with a computer in the middle, proxies do it all the time. I want it to be secure, so SSH tunneling seems like the best bet with the keys distributed before hand manually by me (just moving files from computer to computer).

Figuring out a solution

So this is a pretty hard topic to google for – I tried things like “connect to one server through another”, and was able to stumble upon a blog post that was a life saver. I was vaguely familiar with SSH tunneling and this blog post gave me a very important refresher and introduction with some concepts that I was not super solid with. It also introduced the name for what I was trying to do: a Remote Port Forward. What I was trying to do was expose my local computers port 22 as one of the server’s port (let’s say 9999). This meant I could now access my local computer from elsewhere, through the server’s IP (at port 9999).

Note that this solution is basically equivalent to janky DDNS (your home computer must connect to the intermediary server to set up the remote port-foward), and only works if you have a server in the middle (that you control) with a static address of some sort to do the remote port forward on. It’s far from ideal, but it did work for me, and with relatively low latency so I was happy that it worked.

A few things to consider: 1. What port you want to forward (maybe not ssh? maybe an awesome tool like Cockpit 2. Ensuring that the port forward is always running and gets restarted when your home computer (the one you’re trying to reach) restarts/goes down

I chose to go with a regular SOCKS proxy over a full VPN (also read some good analysis in this article). As various services start to crack down on VPNs, it should be basically impossible (outside of locking an account to one region on the service side) to crackdown on a SSH tunnel + SOCKS proxy to a home computer, because the traffic in earnest comes from your home computer. Short of some other service or some cookies, or something basically giving you away, it should be extremely hard for any remote service to control for/filter.

The code

Here’s the code that I ended up using – it’s in the form of a SystemD service since that was my solution to #2:

[Unit]
Description=Vacation remote ssh
Wants=network-online.target
After=network-online.target

[Service]
User=<your local username>
ExecStart=/usr/bin/ssh -o "ExitOnForwardFailure yes" -nNT -R 2345:localhost:22 <user>@<server-to-ssh-to>

Restart=always
RestartSec=30

[Install]
WantedBy=multi-user.target

I ended up forwarding SSH in addition to some other ports, but that’s the configuration for SSH at least.

As always test the setup before you leave (if you happen to be in the exact same situation as me), from something like a phone, or another computer from outside your home network. I found it very easy to just tether through my phone (btw if your carrier has disabled tethering on your phone or charges you some special fee for it in 2017, you should change phone carriers), and use my laptop to access my desktop.

I ran into a few issues when testing (which basically just is due to my lack of skill with SystemD unit files): - The ssh command didn’t work properly at first (user was the issue, it needed to be run as my user, NOT root) - Network needed to be available (the Wants/After clauses were added to address this) - The service needed to always be running no matter what (the Restart/RestartSec clauses were added to address this)

The results

During my vacation I enjoyed some pretty great quality access to my home computer and was very pleased. I was even able stream video which was fantastic, in relatively high quality… The only problem I encountered were the meager graphics capabilities of my current laptop, an ASUS Zenbook (mine is like that one, an earlier model, which I love) with a fanless CPU.

Hope you enjoyed this walk through, and never have to do sometihng this janky with your own setup!