Proper Ways to Gracefully Terminate an SSH Tunnel Connection


1 views

When working with SSH tunnels, proper termination is crucial for maintaining clean system states and preventing orphaned processes. The common Ctrl+C or Ctrl+D methods work but aren't always ideal for scripted environments.

Here are three professional approaches to closing your SSH tunnel:

# Method 1: Using the -f flag for background operation
ssh -f -N -L 5001:localhost:1019 eonil@test.local
# Then terminate with:
pkill -f "ssh -2 -N -L 5001:localhost:1019"

# Method 2: Using a control socket
ssh -f -N -M -S /tmp/ssh_tunnel -L 5001:localhost:1019 eonil@test.local
# Termination command:
ssh -S /tmp/ssh_tunnel -O exit eonil@test.local

# Method 3: PID capture and kill
ssh_pid=$!
# Later termination:
kill $ssh_pid

For complex environments with multiple tunnels, consider these patterns:

# Create named tunnel
ssh -f -N -L 5001:localhost:1019 -E /var/log/ssh_tunnel.log -o TCPKeepAlive=yes eonil@test.local

# Create management script (tunnel_manager.sh):
#!/bin/bash
case "$1" in
  start)
    ssh -f -N -L $2:localhost:$3 $4
    ;;
  stop)
    pkill -f "ssh -f -N -L $2:localhost:$3"
    ;;
esac
  • Orphaned processes: Always verify termination with ps aux | grep ssh
  • Port conflicts: Check port availability with netstat -tuln | grep 5001
  • Authentication timeouts: Add -o ServerAliveInterval=60 to connection string

For mission-critical tunnels:

# Use autossh for automatic reconnection
autossh -M 0 -f -N -L 5001:localhost:1019 eonil@test.local

# Set up monitoring with:
while true; do
  if ! nc -z localhost 5001; then
    pkill -f "autossh.*5001:localhost:1019"
    autossh -M 0 -f -N -L 5001:localhost:1019 eonil@test.local
  fi
  sleep 60
done

ssh -2 -N -L 5001:localhost:1019 eonil@test.local

When you terminate an SSH tunnel with Ctrl+C or Ctrl+D, you're essentially sending a SIGINT (interrupt signal) to the SSH process. While this works, it's not the cleanest method because:

  • It immediately terminates the TCP connections
  • May leave orphaned processes on the remote server
  • Could cause issues with connection tracking

Method 1: Using SSH Escape Sequence

The proper way involves SSH's built-in escape character:

~.

(Enter the tilde followed by period while connected)

This sends a clean disconnect signal through the SSH protocol itself.

Method 2: Killing by Process ID

For more control:


# Find the process
ps aux | grep "ssh -2 -N -L"

# Then kill it properly
kill -TERM [PID]

Method 3: Using ControlMaster (Advanced)

If you setup ControlMaster in your ~/.ssh/config:


Host test.local
  ControlMaster auto
  ControlPath ~/.ssh/sockets/%r@%h-%p
  ControlPersist 5m

You can then use:

ssh -O exit test.local

For scripts, consider this wrapper:


#!/bin/bash
ssh -f -N -L 5001:localhost:1019 eonil@test.local
trap "kill -TERM $!" EXIT
# Your commands here

This ensures the tunnel closes when your script exits.

If connections hang despite proper termination:


# Check for orphaned connections
netstat -tulnp | grep 5001

# Force cleanup if needed
fuser -k 5001/tcp