Skip to content

Instantly share code, notes, and snippets.

@Duckle29
Last active October 8, 2023 18:53
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Duckle29/e3d2caea714dd2a5514187a6288fa743 to your computer and use it in GitHub Desktop.
Save Duckle29/e3d2caea714dd2a5514187a6288fa743 to your computer and use it in GitHub Desktop.
A systemd service file to ensure a persistent reverse SSH tunnel is active

Persistent reverse-ssh-tunnel systemd unit

This unit file, if enabled on boot, will attempt to connect to a remote server and establish a reverse tunnel. It uses StrictHostKeyChecking=accept-new for the ssh connection, so if you want to make sure the host you're connecting to is the right one, carry out the first connection manually and check the key.

REMOTE is considered a remote server that's available over ssh
LOCAL is considered the device initiating the remote tunnel. This will likely be a device dropped behind a NAT with no option of portforwarding

To use it, copy this multiline command somewhere to edit the configs and run it as root on the device:

curl -sL https://git.io/JUErA | \
sed 's/RHOST/REMOTE HOST TO CONNECT TO/g
s/RUSER/REMOTE USER TO CONNECT AS/g
s/RPORT/REMOTE PORT TO REVERSE TUNNEL/g
s/LPORT/LOCAL PORT TO TUNNEL/g
s/LUSER/LOCAL USER TO TUNNEL FROM/g
s/KEY/PATH TO THE SSH PRIVATE KEY USED FOR AUTH/g' \
> /etc/systemd/system/reverse-tunnel-NAME.service \

This assumes you have a user on the local and remote, that has an SSH key pair with no password, allowing ssh connections. Because of this, it is recommended to set up a user on both LOCAL and REMOTE that has no login permissions, and configure the SSH keys for that.
If using the following example, LUSER and RUSER will both be sshtunnel

on LOCAL:

useradd -ms /usr/sbin/nologin sshtunnel &&\
su -s /bin/bash sshtunnel -c "\
ssh-keygen -t rsa -b 4096 -q -f \"/home/sshtunnel/.ssh/tunnel_rsa\" -N \"\" &&\
cat \"/home/sshtunnel/.ssh/tunnel_rsa.pub\"" \

Copy the public key and hold on to it

on REMOTE:

useradd -ms /usr/sbin/nologin sshtunnel && \
su -s /bin/bash sshtunnel -c "\
mkdir /home/sshtunnel/.ssh && \
echo \"<PUBLICKEY FROM LOCAL HERE>\" >> /home/sshtunnel/.ssh/authorized_keys" \

on LOCAL:

systemctl daemon-reload 
systemctl enable --now reverse-tunnel-NAME.service
systemctl status reverse-tunnel-NAME.service

And you should be good to go.

If you use this for ssh like I do, you'd then connect to the REMOTE first, then run:

ssh -l actualUser localhost -p RPORT

where actual user is the user you wanted to get to in the first place (sshtunnel/LUSER is only used for the tunnel)

This is based on a few different blog posts using autossh, which then lead me to: https://blog.stigok.com/2018/04/22/self-healing-reverse-ssh-systemd-service.html

[Unit]
Description=Setup a secure reverse ssh tunnel to RHOST
After=network-online.target
[Service]
User=LUSER
ExecStart=/usr/bin/ssh -NTg -o ServerAliveInterval=30 -o ExitOnForwardFailure=yes -o StrictHostKeyChecking=accept-new -R RPORT:localhost:LPORT -i KEY RUSER@RHOST
# Restart every >2 seconds to avoid StartLimitInterval failure
RestartSec=5
Restart=always
[Install]
WantedBy=multi-user.target
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment