Deep Dive: How SSH ProxyCommand Works with -W Flag for Bastion Host Jumping


2 views

The ProxyCommand directive in SSH is a powerful feature that enables connection chaining through intermediate hosts. When examining the configuration:

Host final
Hostname final.com
Port 22
AgentForwarding yes
User guestuser
ProxyCommand "ssh user@bastion.com -W %h:%p"

Here's what actually happens during execution:

  1. The local SSH client parses the final host configuration
  2. It executes the ProxyCommand before attempting direct connection
  3. The -W %h:%p flag tells the intermediate SSH session to:
%h → final.com (target host from Hostname)
%p → 22 (target port from Port directive)

While functionally similar to nc %h %p, the -W flag offers several advantages:

  • No dependency on netcat being installed on bastion
  • Uses SSH's native socket forwarding
  • Better handling of TCP connection states

For more complex environments with multiple bastions:

Host final
Hostname final.internal
User appuser
ProxyCommand ssh -q -W %h:%p bastion2

Host bastion2
Hostname jump2.example.com
User gateway
ProxyCommand ssh -q -W %h:%p bastion1

Host bastion1
Hostname gateway.example.com
User admin

The tunnel remains open until:

  • Either SSH session terminates
  • TCP connection times out
  • Explicit disconnect command sent

For latency-sensitive operations:

ProxyCommand ssh -o ControlMaster=auto \
-o ControlPath=~/.ssh/cm-%r@%h:%p \
-o ControlPersist=10m \
bastion -W %h:%p

When working with multi-tier network architectures, SSH's ProxyCommand becomes an essential tool for traversing bastion hosts. The feature essentially creates a pipe between your local machine and the target host through intermediate jump servers.

Let's analyze the exact sequence when executing ssh final with your configuration:

1. Local SSH client initiates connection to 'final' host
2. SSH detects ProxyCommand and executes: ssh user@bastion.com -W final.com:22
3. The -W flag establishes a direct TCP forwarding channel:
   - Creates SSH connection to bastion.com
   - On bastion.com, opens TCP connection to final.com:22
   - Bridges STDIN/STDOUT between connections
4. Your local SSH client then communicates through this tunnel

The -W host:port option is indeed similar to netcat but more robust because:

  • It uses SSH's native socket forwarding
  • Maintains encryption throughout the entire path
  • Automatically handles connection termination

Here's an advanced multi-hop scenario:

Host bastion1
  Hostname jump1.example.com
  User admin

Host bastion2
  Hostname internal-jump.example.com
  User deploy
  ProxyCommand ssh -W %h:%p bastion1

Host production-db
  Hostname db01.internal
  User postgres
  ProxyCommand ssh -W %h:%p bastion2

While convenient, chained ProxyCommands can introduce latency. For better performance:

# Use ControlMaster for persistent connections
Host *
  ControlMaster auto
  ControlPath ~/.ssh/control-%r@%h:%p
  ControlPersist 1h

The technique is sometimes called "SSH chaining" or "SSH hopping". Other implementations include:

# Using nc (less secure)
ProxyCommand ssh bastion nc %h %p

# Using SSH -J (newer versions)
ssh -J bastion1,bastion2 final