A common problem for me when setting up SSH tunnels is that even though the ssh client process is running, data is not going through.
Steps to reproduce:
- Create a tunnel (ssh -D or ssh -L or ssh -R ) on some port
- Data is not going through, so we need to restart it
- The connection cannot be established because bind: Address already in use
- We have to kill the old client, ps aux | grep ssh , to find the right process and then kill -9 it
- Re-create the tunnel
Instead, we could leverage sockets when creating the connection, which allow communication with the SSH process (as opposed to searching for the pid ). This is enabled by the -S option:
ssh -NMfn -S /tmp/server1.local.socket -o ExitOnForwardFailure=yes -D 8080 server1.local
# Additional flags:
# -N Don't open a terminal
# -M Master mode, necessary for socket commands
# -f Go to background
# -S Control socket
Once the socket is set up, we can send commands to it. For example, exit to close the connection (regardless of the state of the connection):
ssh -S /tmp/server1.local.socket -O exit server1.local
Finally, here is a script that will create SOCKS tunnels (proxies) to a list of hostnames. When the script is run again, it will attempt to close any existing tunnels and establish brand new connections:
#!/bin/bash
# These are the servers that we will set up SOCKS tunnels to
HOSTS="server1.local server2.remote"
BASE_PORT=11000
# Some fancy output
bold=$(tput bold)
normal=$(tput sgr0)
# Send exit to all existing sockets
for HOST in $HOSTS; do
if [ -e /tmp/$HOST.socket ]; then
ssh -S /tmp/$HOST.socket -O exit $HOST
fi
done
# Set up variables and nice output
PORT=$BASE_PORT
STATUS_LINE="%-35s%-15s${bold}%s${normal}"
printf "${bold}$STATUS_LINE\n${normal}" "HOST" "SOCKS PORT" "STATUS"
for HOST in $HOSTS; do
printf "$STATUS_LINE" "$HOST" "$PORT" "CONNECTING"
# Attempt to create tunnel
ssh -NMfn -S /tmp/$HOST.socket -o ConnectTimeout=5 -o ExitOnForwardFailure=yes -D $PORT $HOST &> /dev/null
# Report outcome
if (( $? == 0 )); then
printf "\r$STATUS_LINE\n" "$HOST" "$PORT" "DONE "
else
printf "\r$STATUS_LINE\n" "$HOST" "$PORT" "FAILED "
fi
# Increment port number
PORT=$(expr $PORT + 1)
done