Home

TIL: Forwarding ports using SSH and proxying with Apache

I have two servers: a VPS, and a small server that I primarily use on my home network. The server at home is quite a bit more powerful than my VPS, and significantly cheaper to run, so I'd like to start moving some of my hosted services to it. The problem is that I don't have a static IP, so I'd need to get dynamic DNS setup and open the right ports on my router, which is a bit tedious (and not officially supported by my ISP).

My interim solution is to use port-forwarding via SSH, wherein I can specify a target host and port, and map it to a local port, so that requests on the target machine that go to http://localhost:<port> will instead be directed to the machine running SSH (relevant guide on Ubuntu documentation).

The basic command looks like this:

ssh -R 8080:localhost:3000 user@my-remote-server

Which is useful, but also just opens a regular SSH connection, which has to then be kept alive - I could use screen or tmux but that's still not ideal, if the pipe gets broken I'll have to reconnect it manually. A tool called autossh can be used to run the SSH connection in the background, and will automatically spawn a new process if the old one fails:

autossh -M <echo port> -f -N -T 8080:localhost:3000 user@my-remote-server

<echo port> is just an open port that autossh will attach to, and use that to check if the connection is running. -f runs the command in the background, so I don't need to keep a terminal alive, and -N and -T mean no command executed, and don't open a tty connection, respectively.

So now the port has been forwarded, but it's probably not accessible to the outside world, partly because unless the relevant ssh config setting is set on the remote machine (GatewayPorts yes), and the port is open in the server firewall, this will only be available on localhost:8080. So to use this, we also need to proxy queries. I'm using Apache on my server, because that's what the WordPress auto-installer setup for me and I'm lazy, but you could also use Nginx:

<VirtualHost my.subdomain:80>
  ServerAdmin you@yourtld
  ServerName my.subdomain

  <Proxy *>
    Order allow,deny
    Allow from all
  </Proxy>

  # http://httpd.apache.org/docs/2.0/mod/core.html#limitrequestbody
  LimitRequestBody 0

  # Pass requests on
  ProxyPass / http://localhost:8080/ keepalive=On
  ProxyPassReverse / http://localhost:8080/
</VirtualHost>

I'm not including SSL in the above config, which you absolutely should enable for this, because I use certbot, which generates the configuration for me. Add this to /etc/apache2/sites-available/<site-name>.conf and activate it with a2siteenable <site-name>. Restart apache, and assuming the domain is configured correctly, it should be accessible at the domain configured as ServerName.