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 sshdis 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
setuidor to modify
/etc/sudoersto allow the user to run the script with root priviledges without having full sudo. Although, for
setuidwe’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/*/fdto find the process that has the socket. Which is probably why neither lsof nor netstat show it.
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 -dthen it would log which sshd process opened a tunnel to which port in
/var/log/auth.logwhich 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?
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.