Conditional ProxyCommand in ~/.ssh/config?

I have my ~/.ssh/config configured with various hosts that are accessible either while on our company VPN, or via a SSH proxy server.

At the moment I just have

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

However if I'm in the internal network I can directly access the internal ip, so proxying through the external server just adds a delay to connecting.

Is there a way I can provisionally proxy if the internal ip isn't reachable, and connect directly otherwise?


Solution 1:

I usually setup something like this. It assumes the intermediate host will be able to resolve the name.

Host *%homeproxy
    ProxyCommand ssh user@proxyhost /bin/netcat -w 1 $(echo %h | cut -d%% -f1) 22

So I would connect to like ssh blah%homeproxy.

Solution 2:

I use a different method using Match. In my case, I want to use a local proxy if it's running, or not, if it isn't. The following directive accomplishes this nicely:

Match Exec "nc -z 127.0.0.1 1086"
    ProxyCommand nc -X 5 -x 127.0.0.1:1086 %h %p

It matches every ssh attempt, and executes a quick check using nc to see if my proxy is up and running on 1086 (in which case, nc exits with no error). If that happens, it sets the ProxyCommand.

Solution 3:

Thanks for @till's Answer, it inspired me a lot.

I found that you can forcely redirect your connection with ProxyCommand nc dst dst-port.

For example, You will in fact connect to B.com if you use

ssh A.com -o ProxyCommand="nc B.com 22"

But UserKnownHostsFile will still record as A.com

So you could add a domain "auto" to your ssh_config

Host auto.internal-server
  Hostname {internal-server ip or domain}
  ProxyCommand bash -c '(timeout 0.1 nc -z %h %p) && nc %h %p || ssh -W %h:%p external-server'

I replaced nc -w 1 %h %p with (timeout 0.1 nc -z %h %p) && nc %h %p , it will be more quick if you could reach internal-server less then 100ms.

Or you can replaced by ping, but it might indicate bad information if you use a TCP based proxy like proxychains, or server doesn't allow a ICMP echo.

Host auto.internal-server
  Hostname {internal-server ip or domain}
  ProxyCommand bash -c '(ping %h &>/dev/null) && nc %h %p || ssh -W %h:%p external-server'

You can replace (timeout 0.1 nc -z %h %p) with anything which detects whether you are in internal-server.

If you has multiple candidate IPs, even you can use this:

Host auto.internal-server
  Hostname {internal-server ip or domain}
  ProxyCommand bash -c 'f(){(timeout 0.1 ping -c 1 $1 &>/dev/null) && nc $1 %p;}; f 1.1.1.1 || f 2.2.2.2 || f 3.3.3.3'

It will try to connect 1.1.1.1, if fail try to connect 2.2.2.2, and then 3.3.3.3.