Setting Up autossh to Maintain a Reverse Tunnel (SSH Server Having a Dynamic IP Address)
I have bumped into several obstacles in the process of setting up an autossh reverse tunnel, so I’m writing this post to cover the process itself, as well as some other things to pay attention to during the process.
A not-too-old version of Ubuntu is assumed both for the client (the machine with autossh) and the server (the machine that the client will connect to).
Installing autossh
This is just a one-liner:
sudo apt install -y autossh
Setting Up SSH Environment
For security reasons, we are setting up a dedicated user for the SSH tunnel on the server.
sudo useradd -s /bin/true -m sshtunuser
Specifying /bin/true
as a user’s shell prohibits them from running anything yet still allows them to create a tunnel. (Source)
The -m
option creates the new user’s home directory so we can put the public key we’ll have later into ~sshtunuser/.ssh/authorized_keys
.
The next step is to generate a public/private key pair, and put the public key into ~sshtunuser/.ssh/authorized_keys
. Make sure the right permissions are applied:
sudo chown -R sshtunuser:sshtunuser ~sshtunuser/.ssh
sudo chmod 700 ~sshtunuser/.ssh
sudo chmod 600 ~sshtunuser/.ssh/authorized_keys
On the server side in /etc/ssh/sshd_config
, we’ll also need a few options for the created user:
Match User sshtunuser
GatewayPorts yes
ForceCommand /bin/false
ClientAliveInterval 30
ClientAliveCountMax 3
The first option allows SSH clients to listen to a port with IP addresses other than localhost, and the second tells the server to ensure the client is still alive every 30 seconds. The third one is optional; it tells the server to disconnect a client if it hasn’t replied to keep-alive messages 3 times (this is the default).
Do a sudo systemctl reload ssh
to make sure new options are taken without error.
Creating a systemd Service on the Client
Here I am assuming we will tell the server to
mkdir -p ~/.config/systemd/user
cat << EOF > ~/.config/systemd/user/autossh.service
[Unit]
Description=Autossh
Wants=network-online.target
After=network-online.target
StartLimitIntervalSec=0
[Service]
ExecStart=/usr/bin/autossh -M 0 -N \
-o "ServerAliveInterval 15" -o "ServerAliveCountMax 3" -o "ConnectTimeout 10" -o "ExitOnForwardFailure yes" \
-i /home/you/.ssh/yourkey -p 1122 sshtunuser@server.com \
-R 10022:localhost:22
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF
A brief explanation on the service file and autossh arguments:
StartLimitIntervalSec=0
: The option that tells systemd not to stop after some restart attempts. May also beStartLimitInterval=0
on some older versions of systemd.Restart=always
: Always restart the service no matter what exit code is returned.RestartSec=1
: Wait 1 seconds before restarting.
-M 0
: Disables the “base monitoring port” for autossh itself.-R 10022:localhost:22 -p 1122
: Some common arguments that are passed to the ssh program.-o "ServerAliveInterval 15" -o "ServerAliveCountMax 3" -o "ConnectTimeout 10"
: Arguments passed to the ssh program that allow faster discovery of broken connection and faster timeout.-N
: SSH argument to disable command execution (just forward ports).-o "ExitOnForwardFailure yes"
: Without this option, if the ssh client is able to establish the connection but unable to setup a listening port, it will remain running instead of returning an error exit code.
Note: If you haven’t logged into the newly created user on the server before, you must do it once manually to answer the host key verification prompt.
Now enable the service:
systemctl --user enable --now autossh
And we’re done.