SSH Reverse Tunnel on Linux with systemd

2 minute read

Phone Home

This aims to do all the same things my SSH Reverse Tunnel on Mac OS X blog, except this is for Linux systems running systemd. Systemd as a process monitor makes an awesome way to implement the phone home ssh service.

I’m going to skip most of the details and justification for doing this and instead defer interested readers to my previous blog entry.

Setup the Server

All the steps are the same, except nowadays I’d recommend generating a ECDSA key. By default my Arch Linux systems makes a ECDSA key with a 256-bit length. This is very similar to the ECDSA algorithm used for things like Bitcoin, but this uses the NIST P-256 curve (Bitcoin uses secp256k1).

client $ ssh-keygen -f ~/.ssh/servername-home-fwd -t ecdsa

As my previous blog instructs, copy the key over to the server and install it in the authorized_keys file.

Astute readers will note the shorter length of the ECDSA public key. Depsite the shorter length, a 256-bit ECDSA key is believed to be stronger then the standard RSA 2048 key ssh-keygen would use by default (see keylength.com for more details).

Setup the Client

Now to setup the client running systemd (Arch Linux in my case), is approximately the same, except that systemd is used instead of launchd on Mac OS X.

To do this, a system service file is necessary, as opposed to a systemd user service running in the user’s session. The system file can be enabled by the system at boot. Typically system files run as root, so it’s necessary to specify the user tag to avoid running the ssh client as root.

Create the systemd unit file @ /etc/systemd/system/phone-home.service:

[Unit]
Description=Phone Home Reverse SSH Service
ConditionPathExists=|/usr/bin
After=network.target

[Service]
User=localuser
ExecStart=/usr/bin/ssh -NTC -o ServerAliveInterval=60 -o ExitOnForwardFailure=yes -o StrictHostKeyChecking=no -i %h/.ssh/servername-home-key -R 12345:localhost:22 remoteuser@servername

# Restart every >2 seconds to avoid StartLimitInterval failure
RestartSec=3
Restart=always

[Install]
WantedBy=multi-user.target

The local user is the user that the ssh client will run as, not root. The ssh client will login to servername as user remoteuser using key servername-home-key and forwards the client’s local port 22 to the remote server’s port 12345.

After that file is modified, start the service:

client $ sudo systemctl restart phone-home.service

Check to see if it started:

client $ sudo systemctl status -l phone-home.service
phone-home.service - Phone Home Reverse SSH Service
   Loaded: loaded (/etc/systemd/system/phone-home.service; enabled)
   Active: active (running) since Thu 2014-02-20 20:40:32 PST; 4min 45s ago
 Main PID: 2559 (ssh)
   CGroup: /system.slice/phone-home.service
           └─2559 /usr/bin/ssh -NTC -o ServerAliveInterval=60 -o ExitOnForwardFailure=yes -o StrictHostKeyChecking=no -i /home/user/.ssh/core-home-fwd -R 12345:localhost:22 remoteuser@servername

Feb 20 20:40:32 recon systemd[1]: Started Phone Home Reverse SSH Service.

Finally, enable it to start at boot:

client $ sudo systemctl enable phone-home.service
ln -s '/etc/systemd/system/phone-home.service' '/etc/systemd/system/multi-user.target.wants/phone-home.service'

Profit.

Next Steps

Unfortunately there isn’t an easy way to hook in to the network state and only start this service when the network is up. Perhaps this will be easier to fix in the future as systemd evolves with Linux distributions. Ideally it would be nice to say “start this if there is a default route that likely leads to the Internet”.

Instead, the current implementation is a service that will attempt to connect to a remote server every 2 minutes and fail, not the end of the world but not perfect – yet.

Comments