Conditional SSH ProxyCommand: How to Dynamically Switch Between Direct and Proxy Connections


2 views

Many developers working with remote servers face a common challenge: needing different connection methods depending on network context. A typical SSH config might look like this:

Host internal-server
    HostName internal.ip
    ProxyCommand ssh -W %h:%p external-server

This works when you're outside the corporate network, but creates unnecessary latency when you're already inside the VPN where direct access is possible.

The solution lies in creating a conditional ProxyCommand that tests connectivity before deciding the route. Here's how to implement it:

Host internal-server
    HostName internal.ip
    ProxyCommand bash -c "if ping -c 1 -W 1 %h >/dev/null 2>&1; then exec nc %h %p; else exec ssh -W %h:%p external-server; fi"

For more reliability, we can add connection testing through multiple methods:

Host internal-server
    HostName internal.ip
    ProxyCommand bash -c "if (ping -c 1 -W 1 %h >/dev/null 2>&1 || nc -z -w 1 %h %p >/dev/null 2>&1); then exec nc %h %p; else exec ssh -W %h:%p external-server; fi"

For systems where ping might be blocked but SSH connections work:

Host internal-server
    HostName internal.ip
    ProxyCommand bash -c "if ssh -o ConnectTimeout=1 -q %h exit; then exec nc %h %p; else exec ssh -W %h:%p external-server; fi"

Each connectivity test adds some overhead. The ping method is fastest (typically 1 second), while SSH testing takes longer but is more accurate. Choose based on your network environment.

Remember that any conditional logic introduces potential failure points. Ensure your fallback method (the proxy connection) remains secure even if the direct connection test fails unexpectedly.


Many developers working with remote servers face this common scenario: some internal servers are accessible both directly (when on company VPN/internal network) and through a proxy server (when outside). The standard SSH config approach looks like this:

Host internal-server
    ProxyCommand ssh -W internal.ip:22 external-server

This works, but creates unnecessary latency when you're already on the internal network. The connection first goes out to the external proxy server and then back in, adding hops when a direct connection would suffice.

There are several ways to implement conditional proxying in your SSH config:

Method 1: Using nc (netcat) for Connection Testing

This approach tests connectivity before deciding whether to proxy:

Host internal-server
    ProxyCommand bash -c 'if nc -z -w 2 internal.ip 22 2>/dev/null; then nc internal.ip 22; else ssh -W internal.ip:22 external-server; fi'

Method 2: Using Match Exec

For OpenSSH 7.4+ (released 2016-12-19), you can use Match exec:

Host internal-server
    Match exec "nc -z -w 2 internal.ip 22"
        Hostname internal.ip
    Match all
        Hostname internal.ip
        ProxyCommand ssh -W %h:%p external-server

Method 3: Separate Host Entries with Different Conditions

Create two host entries and use different aliases:

# Direct connection when available
Host internal-server-direct
    Hostname internal.ip
    User myuser
    # Add other direct connection parameters

# Proxy connection when direct fails
Host internal-server
    Hostname internal.ip
    User myuser
    ProxyCommand ssh -W %h:%p external-server

For the most robust solution, combine connectivity testing with fallback:

Host internal-server
    Hostname internal.ip
    User myuser
    ProxyCommand bash -c 'if ping -c 1 -W 1 internal.ip >/dev/null 2>&1; then exec nc %h %p; else exec ssh -W %h:%p external-server; fi'

When implementing conditional proxying:

  • The connection test timeout (the -w parameter) should be short (1-2 seconds)
  • Consider using ping instead of nc if firewalls block port probes
  • For frequently accessed servers, the slight delay for the connection test may be worth the direct connection speed

Be aware that:

  • Connection tests might leak information about your network environment
  • Ensure your external proxy server is properly secured
  • Consider using SSH certificates instead of passwords for automated connections