SSH Reverse Tunnels--or: How I Learned to Stop Messing With Static IPs and Port Forwarding

Hosting servers from home! Is a lot more painful than it feels like it should be.

A typical situation

  • You have your gateway. This is a machine owned by your ISP. It has your home network on one side and the internet on the other side, and connects the two.
  • You have your router. This is often also owned by your ISP, and is often part of the same physical machine box as your gateway. It routes traffic between several devices in your home network via ethernet cables and/or WiFi, so they can all talk to each and and all use the internet simultaneously.
  • You would like to run a server (website, minecraft, whatever) on some computer at home and have people be able to connect to it.

How setting up a home server theoretically works

  • You ask your ISP to assign your gateway a static (doesn't change) and publically reachable IP address, so that other computers on the internet can initiate connections with it. Perhaps they charge slightly more for this.
  • You configure your router to route incoming traffic on a certain port (80 and 443 for websites, 25565 for minecraft, etc) to the computer you're running your server on.
  • Optionally, you buy a domain name and point it towards that IP address, so you can tell people things like "go to phoenixkahlo.com" rather than "go to 123.47.231.83".

And then anyone on the internet should be able to connect to your server.

home network
home network
gateway
gateway
computer
computer
client
client
server
server
DNS record
DNS rec...
router
router
Text is not SVG - cannot display

What actually happens

  • Your ISP doesn't let you buy a static IP, so it'll change uncontrollably.
    • So maybe you say, ok, I'll just use DDNS to make my DNS record update automatically whenever my IP address changes.
    • Well first off, DNS records have minimum caching times, so it'll still take like 10 minutes at least for it to update.
    • But also...
  • Your ISP doesn't even give you an unshared IP address at all, but rather uses NAT to make you and your neighbors share a single IP address
    • So now you're not even at a problem of stability, you just straight up can't accept incoming connections.
  • And also you go to try and configure your router with port forwarding and that's like a huge pain because it's different for every router and sometimes you mess it up and break the internet for not only you but your roommates too.
  • Plus now people kind of know where you live based on your IP address. But that one's a bit harder to blame on the ISP.

How I avoid all this: SSH reverse tunnels

home network
home network
gateway
gateway
computer
computer
client
client
server
server
DNS record
DNS rec...
router
router
cloud host
cloud host
cheap VM
cheap VM
SSH reverse tunnel
SSH reverse...
Text is not SVG - cannot display

So basically what I do is:

  • I choose a cloud host, like Google Cloud or AWS, and rent their cheapest VM. Google Cloud offers a 0.25 CPU, 1 GB of RAM VM for like $7 a month, which is easily less than the difference between many ISP plans. I'm sure you could find other hosts that would offer cheaper.
  • I also rent a static IP from that cloud host, which is like another $2 a month, and assign the IP to that VM and configure it to allow incoming traffic.
  • (Optional) I register a domain name and point it to that IP address.
  • I set up an SSH key which is authorized to SSH into the VM, and put it on the computer in my home.
  • I run the server program in the computer in my home as normal.

And then on that computer in my home, I also run a command like this (and let it keep running):

ssh -R 0.0.0.0:25565:127.0.0.1:25565 phoenix@phoenixkahlo.com
\____/ \_____/ \___/ \_______/ \___/ \_____/ \______________/
  |       |      |         |     |      |       |
  |       |      \-----\   |     |      |    through this domain
  |       \---------\  |   |     |      |    name or IP address
  \-- establish an  |  |   |     |      |    (the cheap VM)
      SSH reverse   |  |   |     |      |    
      tunnel        |  |   |     |  logging in with this username
          /---------/  |   |     |  (and the SSH keys in ~/.ssh)
          |      /-----/   |     \------------\
          |      |         \-------\          |
  accept from    |                 |          |
  all          accept from     relay to     relay to
  addresses    port 25565      localhost    port 25565
  \______________________/     \_____________________/
     on the cheap VM           on the computer at home
      

And so:

  • The computer at home SSHs into the cheap VM, and has the cheap VM start accepting incoming connections.
  • When an incoming connection is accepted, it's relayed down that SSH connection to the computer at home, and then relayed to the actual server.
  • A client simply connects to the IP address of the cheap VM, and doesn't need to know that it's being relayed.

Benefits

  • If your computer at home can connect to the internet, it can do this. No messing with your ISP required. No worries with port forwarding or NAT or whatever. And you can switch which computer is running the server instantly. If you wanted you could use this technique to host a minecraft server from your phone in an airplane lavatory using the in-flight WiFi. Please do that. Please do that. Please do that.
  • It also enhances the privacy, on account that you don't need to tell others your real IP address, and also it's kinda like a firewall too.

Drawback, Costs, Limitations

  • You gotta pay up to like $9 a month.
  • Increased latency.
  • I'm not sure if this could work with UDP.

Furthermore (A Worthwhile Security Note)

I run my Minecraft server in a docker container, and the SSH reverse tunnel in a different docker container, and connect them via a docker virtual network. This means that even if Minecraft has another log4j-level vulnerability which lets an attacker take control of the server host, the worst they could do would be mess up my minecraft server, as that container does not have the SSH keys for accessing phoenixkahlo.com, or any other sort of system access.

Furthermore 2: Another Thing

The web server which hosts this here blog is pretty light-weight, so I just run it directly on the cheap VM. This increases the availability, so it's still there even if my home network fails.

Also, I have nginx as an HTTP reverse-proxy in front of it, which in addition to making it really easy to add TLS and some other stuff, would allow me to reverse-proxy specific sub-paths and sub-domains to reverse tunnels if I wanted. That way I could have some HTTP endpoints hosted directly on the VM and other HTTP endpoints hosted on a server in my home.