M0AGX / LB9MG

Amateur radio and embedded systems

Yet another autossh tutorial

SSH remote port forwarding is very useful to access machines behind firewalls and NATs. The ssh client connects to the server (the server must be publicly reachable). The server then starts listening on ports requested by the client and passes the traffic to the client. To the outside world this looks like the public machine has extra services running.

I find this scheme especially handy to access devices connected using 5G. Mobile networks almost never provide public IP addresses and use CGNAT unless you are a big business customer.

This tutorial is valid as of 2023/2024. Using this setup my connections have been running reliably for months without any issues.

The first part is setting up and testing bare ssh and then adding autossh to keep the connection alive.

The server is the machine that has a publicly reachable address. The client is the machine that wants to expose its servers but does not have a publicly reachable address.

The server

The client needs a pair of keys (made with ssh-keygen). Client's public key has to be added to ~/.ssh/authorized_keys on the server to allow connecting without a password.

Hint: if you only want to allow the client (using this exact key) to forward ports and not allow shell access you can add no-pty in front of the client key in authorized_keys so the row looks like:

no-pty ssh-rsa AAAA...rest...of...the...key... user@somemachine

The client

To keep things simple I run everything as root. On the client side ssh can be running under any regular user account. systemd service file will need an extra User= directive in that case.

ssh config

The configuration that has to be added to ~/.ssh/config looks like this:

1
2
3
4
5
6
7
Host my-tunnel-name
   HostName      remote.server.com
   User          remoteusername
   RemoteForward  5001 localhost:22
   RemoteForward  5002 localhost:80
   ServerAliveInterval 90
   ServerAliveCountMax 3

It should be pretty self-explanatory. The tunnel name can be anything. It will be used later in ssh and autossh command line. User is obviously the one that is on the server. Many ports can be forwarded at once. The first column is the port number on the server. The server will most likely disallow forwarding ports below 1024 so higher numbers have to be used.

This config can be tested using ssh my-tunnel-name. Lack of password prompts or error messages is a good sign. It should be possible to connect to remote.server.com:5002 and see the content from client's web server.

autossh

autossh is available in any major distro. I use Debian so I installed it using apt install autossh.

Testing autossh is as simple as running autossh -M 0 my-tunnel-name -N . Again, there should be no error messages and the exposed ports should be publicly reachable. Of course pressing Ctrl+C will terminate autossh.

systemd

The last piece of the puzzle is starting autossh automatically (and keeping it running). Most distros use systemd so the process is very easy.

Let's create a new service file, for example /etc/systemd/system/autossh-my-tunnel-name.service, containing:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
[Unit]
Description=My awesome AutoSSH tunnel to local ssh and friends
After=network.target

[Service]
Environment="AUTOSSH_GATETIME=0"
ExecStart=/usr/bin/autossh -M 0 my-tunnel-name -N
Restart=always
RestartSec=15s

[Install]
WantedBy=multi-user.target

Next, run systemctl daemon-reload to tell systemd that there is a new service and systemctl restart autossh-my-tunnel-name to start it. Again, the ports should become publicly reachable.

Finally, the service can be enabled at startup by systemctl enable autossh-my-tunnel-name.