Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Setup a secure (SSH) tunnel as a systemd service. #systemd #ssh #ssh-tunnel #ssh-forward

README

Create a template service file at /etc/systemd/system/secure-tunnel@.service. The template parameter will correspond to the name of target host:

[Unit]
Description=Setup a secure tunnel to %I
After=network.target

[Service]
Environment="LOCAL_ADDR=localhost"
EnvironmentFile=/etc/default/secure-tunnel@%i
ExecStart=/usr/bin/ssh -NT -o ServerAliveInterval=60 -o ExitOnForwardFailure=yes -L ${LOCAL_ADDR}:${LOCAL_PORT}:localhost:${REMOTE_PORT} ${TARGET}

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

[Install]
WantedBy=multi-user.target

We need a configuration file (inside /etc/default) for each target host we will be creating tunnels for. For example, let's assume we want to tunnel to a host named jupiter (probably aliased in /etc/hosts). Create the file at /etc/default/secure-tunnel@jupiter:

TARGET=jupiter
LOCAL_ADDR=0.0.0.0
LOCAL_PORT=20022
REMOTE_PORT=22

Note that for the above to work we need to have allready setup a password-less SSH login to target (e.g. by giving access to a non-protected private key).

Now we can start the service instance:

systemctl start secure-tunnel@jupiter.service
systemctl status secure-tunnel@jupiter.service

Or enable it, so it get's started at boot time:

systemctl enable secure-tunnel@jupiter.service
@erikbgithub

This comment has been minimized.

Copy link

@erikbgithub erikbgithub commented May 8, 2017

Thanks man, you just saved me like 4 hours of reading systemd docs!

Edit: If this blogpost is your source, maybe a reference is also polite. (not my blog, just reading what others say about the same topic)

Also, maybe the network-online.target is more interesting?

@drmalex07

This comment has been minimized.

Copy link
Owner Author

@drmalex07 drmalex07 commented Sep 5, 2017

Thanks @erikbgithub. No, the article you mention was not my source (just the official docs of systemd).

@comperem

This comment has been minimized.

Copy link

@comperem comperem commented Dec 3, 2017

Super helpful.

I think this is what worked for my restart failures:
RestartSec=5

@derkosak

This comment has been minimized.

Copy link

@derkosak derkosak commented Mar 1, 2018

+1, really useful.
As the blogpost mentioned by @erikbgithub, for flexibility you might want to allow configuration of the SSH key file and for the remote user. I added -l ${REMOTE_USER} -i ${KEY_FILE} to the command in ExecStart and the defaults file.

@willjian

This comment has been minimized.

Copy link

@willjian willjian commented Mar 13, 2018

Very helpful
Thanks @drmalex07 so much

@renxida

This comment has been minimized.

Copy link

@renxida renxida commented Mar 22, 2018

I've been using this to connect to lab computers for a while. Awesome config file. Thank you so much.

I just tried using it on a public computer but it didn't work, so I made a root-free version:

https://github.com/renxida/labtunnel

It also includes install/uninstall scripts.

@sanludhi

This comment has been minimized.

Copy link

@sanludhi sanludhi commented Mar 24, 2018

I was struggling... thanks for saving so much of time ... it worked perfectly fine

@sanludhi

This comment has been minimized.

Copy link

@sanludhi sanludhi commented Aug 10, 2018

How to forward multiple ports

@htfy96

This comment has been minimized.

Copy link

@htfy96 htfy96 commented Sep 21, 2018

Maybe it's better to set it as a user service

@dhruv

This comment has been minimized.

Copy link

@dhruv dhruv commented Nov 7, 2018

Thank you for this!

@sunnyszy

This comment has been minimized.

Copy link

@sunnyszy sunnyszy commented Nov 12, 2018

I also think it's better to exec in user mode. But my machine systemctl has bug when running --user, so I add user permission in service file

@linuxmalaysia

This comment has been minimized.

@maykel535

This comment has been minimized.

Copy link

@maykel535 maykel535 commented Jan 31, 2019

I used the -R instead -L because did not works...

@siliconhippy

This comment has been minimized.

Copy link

@siliconhippy siliconhippy commented Jan 31, 2019

Can this be modified to set up reverse SSH for multiple hosts behind firewalls, with as much automation as possible?

E.g., the port #s on the middleman machine represent each individual host, with the port #s left as variable in the host script. This variable port# determined at client- middleman login time and displayed, from within a pool of port #s.

The keygen should be done on middleman machine, and the public keys copied to target host machines apriori.

@LuoLee

This comment has been minimized.

Copy link

@LuoLee LuoLee commented May 16, 2019

Helpful a lot, thanks.

@stiv-yakovenko

This comment has been minimized.

Copy link

@stiv-yakovenko stiv-yakovenko commented Dec 23, 2019

Thank you friend, I was missing: -NT -o ServerAliveInterval=60 -o ExitOnForwardFailure=yes

@Blaimi

This comment has been minimized.

Copy link

@Blaimi Blaimi commented Jan 20, 2020

I needed to have a ssh-connection to some jump-host to connect to a mysql-database so I've changed it a little bit to have more options and use the native syntax of the config file to configure the service.

You can configure whatever ssh can, even multiple port forwardings, LocalForwardings, RemoteForwardings, etc.

[Unit]
Description=Setup a secure tunnel to %I
After=network.target

[Service]
ExecStart=/usr/bin/ssh -F /etc/default/secure-tunnel.config -NT %i

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

[Install]
WantedBy=multi-user.target

contents of /etc/default/secure-tunnel.config

Host example
   HostName example.com
   User some-user
   IdentityFile /etc/some_folder/id_rsa.some-user.examle.com
   LocalForward 127.0.0.1:4306 dbserver.example.com:3306
   ServerAliveInterval 60
   ExitOnForwardFailure yes

to enable the service, you can use the name given in the Host-line (systemctl enable --now secure-tunnel@example)

@lsaavedr

This comment has been minimized.

Copy link

@lsaavedr lsaavedr commented Feb 3, 2020

@Blaimi thanks!

@dixon1e

This comment has been minimized.

Copy link

@dixon1e dixon1e commented Mar 17, 2020

Thank you, this is extremely helpful and a great time saver.

@antortjim

This comment has been minimized.

Copy link

@antortjim antortjim commented Mar 20, 2020

Yet another thank you from a thankful beginner sysadmin!
I arrived here after spending a while trying to get a script running port forwarding with ssh via a systemd service like this:

ssh -fNL dest_port:127.0.01:source_port SERVER

The script can be executed interactively (./script.sh) but it won't work when run in systemd. I confirm the solution is your answer!

@fcjbispo

This comment has been minimized.

Copy link

@fcjbispo fcjbispo commented Apr 1, 2020

Hi, i am using the version that uses the /etc/default/secure-tunnel.config file and able to get connection when i run the command from shell. But when i try to start it from systemctl (on a Debian 9 system), i receive a failed response as follow:

secure-tunnel@ackt0.service - Setup a secure tunnel to ackt0
Loaded: loaded (/etc/systemd/system/secure-tunnel@.service; disabled; vendor preset: enabled)
Active: activating (auto-restart) (Result: exit-code) since Wed 2020-04-01 14:51:07 UTC; 1s ago
Process: 10744 ExecStart=/usr/bin/ssh -F /etc/default/secure-tunnel.config -NT ackt0 (code=exited, status=255)
Main PID: 10744 (code=exited, status=255)
Apr 01 14:51:07 pfmw-traveller1 systemd[1]: secure-tunnel@ackt0.service: Unit entered failed state.
Apr 01 14:51:07 pfmw-traveller1 systemd[1]: secure-tunnel@ackt0.service: Failed with result 'exit-code'.

Does someone can help me to figure out what this status 255 means? Thanks.

@antortjim

This comment has been minimized.

Copy link

@antortjim antortjim commented Apr 1, 2020

journalctl -ru secure-tunnel@ackt0.service
you will see more details into what went wrong, as it shows logs including Exceptions like the one you are getting. It goes from most recent to oldest logs as you scroll down

@fcjbispo

This comment has been minimized.

Copy link

@fcjbispo fcjbispo commented Apr 1, 2020

thanks!

@fcjbispo

This comment has been minimized.

Copy link

@fcjbispo fcjbispo commented Apr 1, 2020

Hi guys,

To avoid any error due to host key checking on startup of the service, add the -o StrictHostKeyChecking=no on ssh command line or equivalent on secure-tunnel.config. I did put it on the ssh call like this:

[Service]
ExecStart=/usr/bin/ssh -o StrictHostKeyChecking=no -F /etc/default/secure-tunnel.config -NT %i

@SkellaMental

This comment has been minimized.

Copy link

@SkellaMental SkellaMental commented May 8, 2020

@drmalex07
SUPERB
Your detail has saved me a lot of digging - thank you!

@RestOp

This comment has been minimized.

Copy link

@RestOp RestOp commented Aug 12, 2020

Hello,

i have at least the same example


[Unit]
Description=Tunnel for HA
After=network.target

[Service]


ExecStart=/usr/bin/ssh -i "/home/pi/.ssh/id_rsa" -p 22 -o ExitOnForwardFailure=yes -o ServerAliveInterval=60 -N -R 31281:localhost:8123 login@server.com

RestartSec=5s
Restart=always

[Install]
WantedBy=multi-user.target

But it do not start. i get
image

It also do not shows any logs with command
image

PLease advice me what have i've done wrong?

Thanks a lot!

@MartianRover

This comment has been minimized.

Copy link

@MartianRover MartianRover commented Aug 15, 2020

So, I followed your example, got everything working as far as I can tell.
Some issues I had as others mentoned above but it was just because I didn't have my ssh keys in the root users .ssh directory.
So I have my RPi4 (which is on my home network) connected to my DigitalOcean droplet and I have my laptop connected to the same droplet but from my wireless hotspot.
The service is running on both machines.... but now what.

This is probably a stupid question but how do I actually interact with my RPi4 from my laptop while on a different network?
Can I access my RPi4's web server from my laptop that way? If so, how?
How do I access it via the CLI? Can I access the desktop with VNC using this tunnel?

Thanks for any help or pointers. I'm not sure what to ask google to get the answers I'm looking for.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.