Forward Services With SSH Tunnels

Problem

We have some services that we would like to be made available at office for convenience reason. The service is available from a certain port in our home computer. For security purposes we have to use ssh tunnels instead of exposing the port from our home network’s port forwarding.

At our disposal is a cloud server we control that we utilized as a bridge between our home computer and office computer. Both our home computer and work computer are connected to the cloud server. However, how do we configure the connection between our home computer and office computer?

Case Study

For the purpose of this article, we assumes that we want to expose our locally available Syncthing GUI configured in homecomputer to listen only to localhost:8384. The service would not be available from outside, as our syncthing configuration would reject any connection not coming from localhost. This is a security feature, that the service is only available locally, as it allows access to sensitive files.

  1. homecomputer is our home computer
  2. workcomputer is our work computer
  3. cloudserver is our cloud server

Solution

With SSH tunnels, from workcomputer we can access homecomputer:8384 as if we are connecting from localhost. We can achieve this by setting up a matching pair of SSH tunnels between homecomputer and workcomputer through a single common local port on cloudserver. From homecomputer we configured a reverse SSH tunnel to cloudserver, and from workcomputer we configured a SSH tunnel to cloudserver.

As a bonus, to have a clean URL when accessing from a browser in workcomputer, we can configure a reverse proxy in a local nginx at workcomputer. A reverse proxy configuration would take a certain URL path and would configure a local port to serve the URL path. By pointing the reverse proxy configuration to the port in workcomputer we configured as a SSH tunnel to cloudserver that is forwarded to homecomputer, we can then access a service available locally in homecomputer as if we are connecting to it locally.

SSH Tunnels

Therefore we just have to configure a SSH reverse tunnel from homecomputer:

# file .ssh/config at homecomputer
# Equivalent to: ssh -R 8385 localhost:8384 username@cloudserver
Host liecorp-tunnel
    User                username
    HostName            cloudserver
    RemoteForward       8385 localhost:8384
    ServerAliveInterval 120

Then from workcomputer we configured a SSH tunnel:

# file ~/.ssh/config at workcomputer
# Equivalent to: ssh -L 8485:localhost:8385 username@cloudserver
Host liecorp-tun
    User                username
    HostName            cloudserver
    LocalForward        8485 localhost:8385
    ServerAliveInterval 120

What we did from homecomputer is basically to open a listening port cloudserver:8385 that would correspond to homecomputer:8384. Then from workcomputer we forward connection from port workcomputer:8485 to cloudserver:8385, which in turn, is forwarded to homecomputer:8384. Therefore to reach homecomputer:8384 from workcomputer, we can point our browser to http://localhost:8485 and our service at homecomputer:8384 would be available from workcomputer.

Nginx reverse proxy

To make everything tidy, we can even set a reverse proxy configuration on nginx at workcomputer:

# File /etc/nginx/sites-available/server.conf 
server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name _;
    root    /usr/share/nginx/html;
    index   index.html

    include "/etc/nginx/default.d/*.conf";

    location / {
    try_files $uri $uri.html $uri/ /fallback/index.html;
    }

    error_page 404 /404.html;
    location = /40x.html {
    }

    error_page 500 502 503 504 /50x.html;
    location = /50x.html {
    }

    location /syncwork0/ {
        proxy_pass          http://127.0.0.1:8384/;
        include             proxy_params;
        proxy_read_timeout  600s;
        proxy_send_timeout  600s;
    }
    location /synchome/ {
        proxy_pass          http://127.0.0.1:8485/;
        include             proxy_params;
        proxy_read_timeout  600s;
        proxy_send_timeout  600s;
    }
}

Therefore we can now access homecomputer:8384 by pointing our browser to http://localhost/synchome/ instead of http://localhost:8485/.

Conclusion

To connect to a service available only to localhost at homecomputer from workcomputer, we:

  1. Configure SSH Tunneling from both homecomputer and workcomputer to cloudserver, where the traffic would go through cloudserver:8385. The route cloudserver:8385 corresponds to a listening port homecomputer:8384, and forwarded traffic from workcomputer:8485. Therefore connecting to localhost:8485 from workcomputer would get us to localhost:8384 at homecomputer. To syncthing, we are accessing from localhost, and therefore is allowed to access the service.
  2. Configure Nginx reverse proxy to have a clean URL without having to type ports. We set up a reverse proxy address for URL /synchome/ that would be served by http://127.0.0.1:8485/. Therefore we can access the service from http://localhost/synchome/.