How To Use SSH Tunnel To Expose A Local Server To The Internet

Updated on September 7, 2021
Read — 3 minutes

As the R&D Lead at Go Wombat I often have to deal with various tasks. Now I want to share our experience of using SHH tunnel to expose a local server.

We have a local server at Go Wombat. And there is no access to that server outside of the local network. We decided to deploy one of our internal tools on the virtual machine inside that local server. And use one of the remotely hosted servers to gain access to that tool through the internet.

It’s a good and free alternative to tools like Ngrok or Serveo.

TL;DR Daemon service for Tunnel

On the local server. Create a file in the /etc/systemd/system/ with the name like tool-tunnel.service

[Unit]
Description=AutoSSH tunnel to remote server
After=network.target

[Service]
Environment="AUTOSSH_GATETIME=0"
ExecStart=/usr/bin/autossh -M 20000 -N -i /home/tool/.ssh/id_rsa user@[IP] -R 8080:localhost:80 -C

[Install]
WantedBy=multi-user.target

On the remote server — add your public SSH key to the  authorized_keys

Remote server

Giving access

To be able to create SSH tunnel to that remote server you need to give your local server SSH access. You need to generate a key (or just use the existing one if you have) using ssh-keygen  command. After it is done add you public key from local server to the ~/.ssh/authorized_keys on the remote server.

You can test if it works by trying to SSH on the remote server from the local server (caution, possible word “server” overdose): ssh username@[IP]

Local server

SSH Tunnel

Finally, the SSH tunnel itself. Use a command like that:

ssh -N -i /home/tool/.ssh/id_rsa user@[IP] -R 8080:localhost:80 -C

So, let’s go through all the arguments:

  • -N : Do not execute a remote command. It’s useful for just forwarding ports.
  • -i /home/tool/.ssh/id_rsa : Which identity (key) to use.
  • user@[IP] : user — username on the remote server, IP — ip of the remote server
  • -R 8080:localhost:80 : This allows anyone on the remote server to connect to TCP port 8080 on the remote server. The connection will then tunneled back to the client host, and the client then makes a TCP connection to port 80 on the localhost
  • -C : Enables data compression

Now you can try to test it by opening your URL in the browser. Or sending some requests. Don’t forget to launch your application 😉

AutoSSH

Also, we need to make a tunnel persistent — make sure the tunnel will always run. For example, once your ssh connection times out (By server-side timeout), your tunnel should be re-established automatically.

There is a tool that will help us — AutoSSH.

Autossh is a program to start a copy of ssh and monitor it, restarting it as necessary should it die or stop passing traffic.

The command I come up with is next:

autossh -M 20000 -N -i /home/tool/.ssh/id_rsa user@[IP] -R 8080:localhost:80 -C

The only things that added are autossh instead of ssh and argument -M 20000 , which means that AutoSSH uses port 20000 to check the connection.

Service

To make sure that the tunnel will be established automatically after server reboot and to have control over the process, we need to set up a systemd service.

Create a file in the  /etc/systemd/system/ with the name like tool-tunnel.service (you can change tool to something more descriptive 😉 )

[Unit]
Description=AutoSSH tunnel to remote server
After=network.target

[Service]
Environment="AUTOSSH_GATETIME=0"
ExecStart=/usr/bin/autossh -M 20000 -N -i /home/tool/.ssh/id_rsa user@[IP] -R 8080:localhost:80 -C

[Install]
WantedBy=multi-user.target

P.S. Don’t forget to change [IP] in the command.

After the file it’s done, run:

sudo systemctl daemon-reload
sudo systemctl start tool-tunnel  # To start your service
sudo systemctl enable tool-tunnel  # To make it start automatically

Now you can check it with sudo systemctl status tool-tunnel .

Done!

If you don’t want to use Nginx

By default, OpenSSH only allows connecting to remote forwarded ports from the server host. However, you can change that behavior.

For that, you need to change the GatewayPorts configuration file sshd_config (Usually this file is /etc/ssh/sshd_config). Simply cahnge it to yes :

GatewayPorts yes

If you want to use Nginx

Remote server

First, we need to prepare the remote server to handle requests and redirect them to port that used in the SSH tunnel. We used Nginx to set it up.

In /etc/nginx/sites-available/ you need to create a file that contains config for your site.

The contents are pretty simple:

server {
    listen 80;
    server_name tool.gowombat.team;
    access_log /var/log/nginx/tool-proxy.log;
    error_log /var/log/nginx/tool-proxy.error.log;
client_max_body_size 32m;
location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

After you done it — you need to add it to /etc/nginx/sites-enabled/ , best way — using symlink:

ln -s /etc/nginx/sites-available/tool /etc/nginx/sites-enabled/

Local server

On the local server, we’re used Nginx as well to serve applications and static files. But if you have something simple or something that doesn’t need static files, you can skip that part.

The config file needs to be put in the same directory as before.

server {
    listen 80;
    server_name tool.gowombat.team;
    access_log /var/log/nginx/tool.log;
    error_log /var/log/nginx/tool.error.log;

client_max_body_size 32m;

location /static/ {
        alias /home/tool/tool-backend/collected_static/;
    }

location / {
        include proxy_params;
        proxy_pass http://unix:/home/tool/tool-backend/tool_backend.sock;
    }

}

This is how we did it at Go Wombat. If you have any questions please let us know!

How can we help you?