Tunneling an SSH connection only when necessary using Match
2017-07-13If you have to connect via a gateway relay to a server when you’re outside your
home network, you probably have a ~/.ssh/config
that looks something like
this:
Host server-remote
Hostname server.example.com
ProxyJump gateway.example.com
Host server
Hostname server.example.com
With this setup, you run ssh server
to connect directly at home, and ssh server-remote
to connect via the gateway elsewhere.1
The direct connection is more quickly established, stable and faster. But if
you forget you’re not at home and type ssh server
, then you’ll get an error
(possibly after a long delay, depending on the reason server.example.com
is
not accessible).
Conditional configuration of ssh with Match
Wouldn’t it be nice to be able to type the same command, ssh server
, wherever
you are, and have ssh connect via the gateway only if necessary and with
minimal delay?
This is where the Match
directive comes in. This allows you to conditionally
configure ssh by specifying a command whose output determines whether the rest
of the configuration block is used. Here’s what it looks like.
Match host server !exec "command_that_tests_connection"
ProxyJump gateway.example.com
Host server
Hostname server.example.com
The ProxyJump
directive is applied if and only if you’re connecting to host server
and command_that_tests_connection
exits with non-zero status. In that
case you’ll go via the gateway.
My configuration
It’s up to you to write command_that_tests_connection
. What you need will
depend on the circumstances in which your server is inaccessible. You want a
command that fails (ideally quickly) when the server is not directly
accessible.
This command works for me. It exits with non-zero status if the host doesn’t respond to a ping command within one second.
#!/bin/sh
ping -t 1 -o "${1}"
I save this as local-accessible
in my path. Then I can do
# at home
$ local-accessible server.example.com &>/dev/null && echo "OK" || echo "FAIL"
OK
and
# elsewhere
$ local-accessible server.example.com &>/dev/null && echo "OK" || echo "FAIL"
FAIL
~/.ssh/config
then looks like this:
Match host server !exec "local-accessible server.example.com &>/dev/null"
ProxyJump gateway.example.com
Host server
Hostname server.example.com
With this setup, when I run ssh server
, I connect directly and almost
instantaneously at home, and via the gateway with a one second overhead
elsewhere.
Debugging
If you run into problems, first check that your version of local-accessible
behaves as expected by running it manually and testing the exit code, e.g.
$ local-accessible server.example.com &>/dev/null && echo "OK" || echo "FAIL"
If all looks well, you can examine the logic of the Match
conditions by
connecting with ssh -vvv server
. Pay particular attention to the debug3
lines near the top of the output.
-
The
ProxyJump
option was introduced in OpenSSH 7.3. It simplifies connecting via a gateway by replacingProxyCommand ssh -W %h:%p gateway.example.com
or something involvingnc
. ↩︎