4

I am currently porting all my crontab configs into systemd-services, i.e. creating units for tasks that needs to autostart on my computers.

One of these units is giving me a headache. I called it "uplink.service", it's purpose is to call a script that builds a reverse ssh tunnel to my server and establishes that. This is the unit I created:

[Unit]
Description = SSH-Uplink
After = network.target

[Service]
ExecStart = /bin/bash /root/script/uplink.sh

[Install]
WantedBy = multi-user.target

This is the script the unit points to:

ssh -fNC -R XXXX:localhost:22 user@ip -pXXXX -i ~/script/id_rsa
touch /tmp/uplinkonline

As you can see, for debugging this script tries to make a new file called uplinkonline in /tmp. This works; the file is created after starting the service, so the script itself is successfully called.

And this is the output of systemctl after trying to start it:

uplink.service - SSH-Uplink
   Loaded: loaded (/etc/systemd/system/uplink.service; disabled; 
vendor preset: disabled)
   Active: inactive (dead)

Nov 07 10:44:01 loki systemd[1]: Started SSH-Uplink.

The script itself works while starting it manually or calling it by startup via crontab. But somehow systemd seems to expect a different behaviour and exits, without starting the SSH connection. Where did I go wrong?

  • More to this problem: It seems that systemd starts the script, but exits it right away - so, for a short moment the connection ist functional. How can I tell systemd that this process needs to be kept alive? – t3ngu Nov 7 '18 at 11:44
6

Your script starts ssh in background and doesn't stick around, while systemd expects the programs it executes to stay running while the service is up.

All you need to do is tell systemd this is a one-shot service, by configuring it with Type=oneshot in the service unit. You might also want to include RemainAfterExit=yes so systemd keeps listing the service as "up".

In short, update your service file to the following:

[Unit]
Description=SSH-Uplink
After=network.target

[Service]
Type=oneshot
ExecStart=/bin/bash /root/script/uplink.sh
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target

(Note the usual style for systemd unit files has no spaces around the "="s, so I fixed that too.)

You might want to consider adding an ExecStop= command to tear down the SSH tunnel, in order to make systemctl stop uplink work as expected.

  • 1
    Using Type=forking is perhaps also a possibility, since ssh -f will leave a background process around and it's possible systemd will correctly identify it as the main process for the service once the shell script terminates... You might want to give that a try as well if you think that's interesting. – filbranden Nov 7 '18 at 16:45
  • Hey Filipe, thanks very much, works like a charm! Can you please explain more what you mean by Type=forking? Will this fork the session everytime the process gets killed? – t3ngu Nov 8 '18 at 7:21
  • 1
    Using Type=forking might work since then systemd will look for a process left around after the shell script finishes, and ssh -f will fork and leave an ssh process around like that. See this example from systemd documentation for a more exact description of how Type=forking looks for a background process to select as the main process for the service. (Using Type=oneshot is also OK in this case, so if you're happy with that, it's definitely fine.) – filbranden Nov 8 '18 at 7:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.