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
.