5

I have an ssh tunnel listening on a local port, say 8888, opened from a remote machine with ssh -R 8888:localhost:80 myuser@myhost. I need to write a script for "myuser" on "myhost" that will close this tunnel.

The script will be executed by "myuser" on "myhost". It won't be executed by the root nor it will be able to sudo.

One approach wound be to find PID of the process listening on 8888 using lsof or netstat and then kill the process. However, both lsof and netstat refuse to give me the PID without going sudo. With netstat I just get - instead of "PID/Program name" without sudo.

Is there a way to find out PID of the process listening on a given port without the root priviledges? The process listening on this port is running under the same user we are. Or is there any other way to close the ssh tunnel from outside of it?

Note that using the ssh escape sequence to reset the connection is not a solution as we are not in the tunnel. Our script needs to run separately of the tunnel on the same host by the same user.

Also note that just running killall sshd is not a solution as it will kill all other ssh connections not just this one.

This question is not a duplicate of many similar questions, because all of their accepted answers I have found requires root priviledges, or just kills all ssh connections.

The host "myhost" is running Ubuntu 12.04.5.


Edit: Discussion summary as requested by @Jakuje:

  • @Patrick suggested an approach to use setuid or to modify /etc/sudoers to allow the user to run the script with root priviledges without having full sudo. Although, for setuid we'd need to write a binary instead of the script. However, allowing the user to run the script with root priviledges could be a potential security risk. And still this solution does not cover the case the user has no root access at all, so he cannot modify /etc/sudoers. If @Patrick writes this as an answer I'll definitely upvote it, but I won't mark it accepted, yet.

  • @EricRenouf found out that sshd does chown the socket to the user so the user can't use /proc/*/fd to find the process that has the socket. Which is probably why neither lsof nor netstat show it.

More ideas:

As @EricRenouf found there is probably no way to get sshd PID by the opened socket port number nor inode. But I'm curious if there isn't any other trick how to find this sshd PID or how to tell it to close the connection. Maybe somehow directly with sshd.

For instance, if I enabled sshd debug mode sshd -d then it would log which sshd process opened a tunnel to which port in /var/log/auth.log which is readable by the user. So the script could parse the log and find the PID there. But running a debug mode sshd is not a good idea. There is actually a simple patch for sshd to log opened tunnels even without debug mode. But I'm not sure running a patched sshd would be a good idea either.

Is there any other trick?

  • Nice problem, I'm looking forward to the given solutions to this as I suffer the same issue sometimes. – djsmiley2k May 24 '17 at 13:42
  • 1
    @Patrick Isn't SUID ignored on scripts? – martin.macko.47 May 24 '17 at 13:50
  • 2
    You're correct. I wasn't aware of that. Also see this thread: superuser.com/questions/440363/… I think changing the sudoers file might be a better option. – Patrick May 24 '17 at 14:04
  • 1
    It's requested by the user, but it's really being created by sshd which is almost certainly running as root. I think this is backed up by looking in /proc/net/tcp and finding the entry for 22B8 (hex for 8888) and seeing (for me at least) that the uid column has 0 – Eric Renouf May 24 '17 at 14:42
  • 1
    @martin.macko.47 please edit the question with the additional information gathered in the comments. It is very hard to read through the poorly formated comments to understand what the actual problem is. – Jakuje May 24 '17 at 19:53
2

Start by setting up a custom SSH key for connecting to "myhost". Then, edit your .ssh/authorized_keys file on "myhost" so that the shell's parent PID is written to a file:

command="echo $PPID > tunnel.pid; bash" ssh-rsa AAA...

You may want to tweak some other settings for security and/or you could write a custom script to just write the PID and hang. The principle is the same either way.

Once you have the PID of the process that is keeping the SSH session open, it's just a matter of scripting...

kill `cat tunnel.pid`

...to terminate the process, thereby closing the SSH session.

Note that you can use the -i flag to the ssh command to specify a private key to use for the connection.

Edit: Killing the shell process won't close the tunnel if there are active connections, so we kill the parent SSH process.

Edit: Although my first inclination with any automated SSH connection is to use SSH keys, the same process could be emulated with:

ssh -R 8888:localhost:80 myuser@myhost 'echo $PPID > tunnel.pid; for ((;;)); do sleep 1; done'

You can also substitute a script that checks for some other criteria to decide when to terminate itself. That's probably a cleaner way of handling this.

  • Thanks. Almost what I wanted. But it works only if there are no open connections in the tunnel. Because echo $$ gives us only the PID of the bash running inside sshd. And sshd won't end until all open connections close even if the command ends. If there are any connection inside the tunnel sshd will wait for them. Perhaps is there any ssh option to force close all connections when the command ends? – martin.macko.47 May 26 '17 at 11:04
  • Finally. Using echo $PPID instead of echo $$ helped. Here $PPID is PID of bash parent process which is sshd. After you edit your answer to use $PPID I'll accept it. – martin.macko.47 May 26 '17 at 12:12
  • At the end I didn't use tunnel.pid file so I won't accidentally kill some other process with the same PID in case I'd have some ancient tunnel.pid there. But I used a .killswich file and waited for it to be touched: ssh -R 8888:localhost:80 myuser@myhost 'inotifywait .killswich; kill $PPID'. So after touch .killswich the sshd will kill itself. – martin.macko.47 May 26 '17 at 12:16
  • @martin.macko.47, I only tested with no active connections. Thanks for pointing that out. I have edited my answer. – Google Fail May 26 '17 at 14:09

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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