117

I'm pretty lost on this. From the man page:

 -f      Requests ssh to go to background just before command execution.

After starting SSH with the -f option, I have a working tunnel. But after I finish using it I don't know how to further interaction with it. For example, I cannot close the SSH tunnel when I finish with it.

The usual methods I know don't work. For example, jobs returns nothing. The ~ command is not recognized (and I don't know exactly how to use it, anyway).

However, pgrep tells me that the SSH tunnel is still running (after I have closed the terminal, etc.). How do I interact with it? How do I close it?

7 Answers 7

142

An especially good solution for scripting is to use master mode, with a socket for control commands:

ssh -f -N -M -S <path-to-socket> -L <port>:<host>:<port> <server>

To close it again:

ssh -S <path-to-socket> -O exit <server>

This avoids both grepping for process ids and any timing issues that might be associated with other approaches.

4
  • 22
    Just to expand a little bit for the uninitiated, the <path-to-socket> is a path to a file the master instance of ssh client will create for inter-process communication with other ssh client instances that want to share master instance's connection. The created file will actually represent a unix domain socket. It can be named and located whatever and wherever you want it, e.g. /tmp/session1 (though it is recommended to name it using % patterns -- see ControlPath description in man ssh_config)
    – golem
    May 29, 2015 at 20:37
  • 3
    Also it seems that the <server> part of the ssh -S <path-to-socket> -O exit <server> command can be any string, but it must be present. That's kind of clumsy.
    – golem
    May 29, 2015 at 21:57
  • 7
    This answer and question are somewhat old, but i wanted to endorse this as probably the most elegant and 'correct' way of handling this problem that I've seen. Thank you, sir!
    – zentechinc
    Sep 17, 2017 at 4:42
  • 1
    Note that ssh will close the socket and delete the file if you kill the ssh process relying on it. To determine whether the socket is still open in bash or zsh, you can use the -S boolean e.g. if [[ -S /tmp/session1 ]].
    – Nick K9
    Jul 24, 2022 at 13:44
75

I found the solution here: http://www.g-loaded.eu/2006/11/24/auto-closing-ssh-tunnels/

The best way – Tunnels that auto-close

As it has been mentioned previously, instead of using the -f -N switch combination, we can just use -f alone, but also execute a command on the remote machine. But, which command should be executed, since we only need to initialize a tunnel?

This is when sleep can be the most useful command of all! In this particular situation, sleep has two advantages:

  • it does nothing, so no resources are consumed
  • the user can specify for how long it will be executed

How these help in auto-closing the ssh tunnel is explained below.

We start the ssh session in the background, while executing the sleep command for 10 seconds on the remote machine. The number of seconds is not crucial. At the same time, we execute vncviewer exactly as before:

[me@local]$ ssh -f -L 25901:127.0.0.1:5901 [email protected] sleep 10; \
          vncviewer 127.0.0.1:25901:1

In this case, the ssh client is instructed to fork the ssh session to the background (-f), create the tunnel (-L 25901:127.0.0.1:5901) and execute the sleep command on the remote server for 10 seconds (sleep 10).

The difference between this method and the previous one (-N switch), basically, is that in this case the ssh client’s primary goal is not to create the tunnel, but rather to execute the sleep command for 10 seconds. The creation of the tunnel is some kind of side-effect, a secondary goal. If vncviewer was not used, the ssh client would exit after the 10 sec period, as it would have no more jobs to do, destroying the tunnel at the same time.

During the execution of the sleep command, if another process, vncviewer in this case, starts using that tunnel and keeps it occupied beyond the 10 sec period, then, even if the ssh client finishes its remote job (execution of sleep), it cannot exit because another process occupies the tunnel. In other words, the ssh client cannot destroy the tunnel because it would have to kill vncviewer as well. When vncviewer stops using the tunnel, then the ssh client exits too, as it has already accomplished its goal.

This way, no ssh processes are left running in the background.

3
  • 4
    A short remark/correction: As far as I know you have to specify vncviewer 127.0.0.1::25091 as you are using the port syntax and not the display syntax. Sep 26, 2013 at 10:07
  • This is a great idea, and neatly side-steps an issue I had on Windows. Recent versions of Windows ship with an ssh client, and it's supports the -S option in theory, but just didn't seem to work for me.
    – Keeely
    Jan 3, 2019 at 13:59
  • 3
    For anyone still looking at this answer. This answer is also given in man ssh under the heading TCP FORWARDING Jan 28, 2020 at 19:01
24

To kill the tunnel, use ps -C ssh or ps | grep ssh or any other variant to determine which ssh process is running your tunnel. Then kill it.

Alternatively, you can look for the process by determining which one has this port open:

netstat -lnpt | awk '$4 ~ /:1234$/ {sub(/\/.*/, "", $7); print $7}'

If you want to kill all ssh clients running on your machine (as your user), pkill ssh will do it.

2
  • thanks -- just wanna highlight that this is not optimal if I am working on a remote host (A), tunneling to another host (B) (it closes the connection also with A, which is not ideal)
    – jjrr
    Feb 2, 2022 at 14:26
  • If 'netstat' is not a typo you can use command-not-found to lookup ... So are the alternative commands? Feb 25 at 19:37
21

As answered by others here, pkill ssh kills it.

To create a tunnel that could be brought back, I start it in screen without the -f option, and then detach the screen with Ctrl-A D. To bring back the tunnel, call screen -r.

5
  • 1
    Brute force, but effective
    – mafrosis
    Feb 14, 2018 at 23:19
  • 2
    Beware if you are connecting remotely. pkill ssh will even kill your current connection.
    – kennyut
    Nov 13, 2018 at 16:56
  • 1
    @kennyut Yet another reason to use screen. May 11, 2019 at 12:20
  • You don't need to pkill ssh.. you can simply do ps -ef | grep $PORT to find the proper screen process... or even do screen -ls to see all open screens.. find the right one... open it, and then close it with CTRL+A and type :quit
    – SgtPooki
    Nov 23, 2019 at 2:26
  • BTW. This is the simplest and most organized way to keep multiple ssh tunnels working. socket control sounds great and proper and efficient, and it may be "the right way" but its overly complex for what 80% of people actually need
    – SgtPooki
    Nov 23, 2019 at 2:28
8

When I start a tunnel with:

ssh -fN -D 8080 SOME_IP_HERE -l LOGIN_NAME_HERE
  • -f Requests ssh to go to background just before command execution.
  • -N Do not execute a remote command. This is useful for just forwarding ports.
  • -D Specifies a local "dynamic" application-level port forwarding.
  • -l Specifies the user to log in as on the remote machine.

I can close it with:

ps -lef | grep ssh | grep "8080" | awk "{print \$2}" | xargs kill
2
  • 3
    For me on Ubuntu 20.04 it's awk "{print \$4}" instead of awk "{print \$2}" as ps -lef prints the PID at the fourth and not the second position.
    – grssnbchr
    Mar 19, 2021 at 8:41
  • Even on Ubuntu bionic (18.04) it's at the fourth column. Jul 20, 2022 at 21:32
7

I have found a robust solution at SO, see: https://stackoverflow.com/a/26470428 . All credit goes to @ghoti .

I hope I'll be forgiven for not repeating the whole story. It is based on the "advanced" SSH ControlMaster configuration option, which you best put into your ~/.ssh/config file. Here is how I did it for VNC-over-SSH (my employer's VPN has all ports closed except SSH and HTTPS).

~/.ssh/config part (note the ControlMaster and ControlPath entries:

Host vnc
    HostName    my.desktop.internal.within.company
    User    laryx
    IdentityFile ~/.ssh/id_ecdsa
    LocalForward 5999 127.0.0.1:5900
    RequestTTY no
    ExitOnForwardFailure yes
    ControlMaster auto
    ControlPath ~/.ssh/control_sockets%r@%h:%p

I connect/disconnect with a little Bash script:

#!/bin/bash
if [ $# -lt 1 ]; then
    echo "This script creates an SSH tunnel to my desktop"
    echo "so that I can share its screen via VNC-over-SSH"
    echo "Usage: $0 start|stop"
    echo "Remember to start VPN first!"
    exit 1
fi

if [ "$1" = "start" ]; then
    # Assuming that the VPN connection has been established
    # 1) Start the tunnel with
    ssh -f vnc -N
    # 2) check it
    ssh -O check vnc
    echo "Now share the screen by connecting to 'vnc://localhost:5999'"
    exit 0
fi

if [ "$1" = "stop" ]; then
    ssh -O exit vnc
    echo "Now you may exit the VPN"
    exit 0
fi

The tunnel is controlled via the ssh -O commands.

2
  • @Michaelvsk Thank you for your edits! Upvote the answer if you like it ;-) Sep 1 at 12:57
  • I like this solution!
    – crow16384
    Nov 9 at 16:32
5

The best solution I found to kill all tunnels in one command line is

ps -lef|grep ssh|grep "\-L"|awk '{print $4}'|xargs kill

for the ssh sessions, just remove the tunnel option

ps -lef|grep ssh|awk '{print $4}'|xargs kill

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .