Conditional ProxyCommand in ~/.ssh/config?

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.


Solution 4:

A decade ago I wrote a proxy command for this kind of scenario. In your usage case my proxy command could be used like this:

Host internal-server
    ProxyCommand ssh-multipath-proxy %h:%p -- ssh -W %h:%p external-server

A couple of caveats: I am a bit embarrassed to admit, that it doesn't handle IPv6, and that it will only try a single IP address per hostname. Also, it won't transfer the client banner to the server before the server banner has been sent. But that is unlikely to cause problems.

Tags:

Ssh