My answer is inspired by @Robert Kihlberg, but goes a bit further.
I have a home network with a machine called vorlon
on it, and a gateway called luthor
. Vorlon is behind a NAT, and is not visible on the internet, while luthor
has a dyndns address and is accessible from anywhere in the world.
The following is present in ~/.ssh/config
on all my machines. (The options LocalCommand
and PermitLocalCommand
are only there to simplify reconfiguration, or to help debugging if something goes wrong – note the redirection to STDERR in them though, >&2
, without those rsync
breaks!):
# For gateway machine.
# Coming from localhost.
Match originalhost luthor exec "[ %h = %L ]"
LocalCommand echo "SSH %n: To localhost" >&2
# Coming from outside home network.
Match originalhost luthor !exec "[ %h = %L ]" !exec "{ ip neigh; ip link; }|grep -Fw 60:0e:56:36:c4:ca"
LocalCommand echo "SSH %n: From outside network, to %h" >&2
Hostname luthor.dyndns.org
# Coming from inside home network.
Host luthor
PermitLocalCommand yes
LocalCommand echo "SSH %n: From home network, to %h" >&2
First, in order to detect which network I'm on, I use the following command (as argument for the Match … exec "…"
option):
{ ip neigh; ip link; }|grep -Fw 60:0e:56:36:c4:ca
This command returns true if the MAC address of my gateway machine can be found on the network. This will only happen if the machiche I'm using is currently connected to my home network. (The ip neigh
command lists all the hosts on the network [including their MAC addresses] that is known to the machine I'm SSH:ing from. However this it's output does not include the machine I'm SSH:ing from itself, which is instead done by the ip link
command. – The output formats of the two commands is slightly different, but it doesn't matter, as they both include the MAC addresses in the same format.)
There are three distict cases:
1. SSH:ing to localhost.
If we're SSH:ing to 'luthor' (originalhost luthor
), and the host I'm coming from is the same as the destination ([ %h = %L ]
) then we're SSH:ing to localhost, in which case no further logic is needed.
It may not look as such, but [ %h = %L ]
is a shell command, which is percent-expanded by SSH, and which returns true if the local hostname (%L
) is the same as the remote host (%h
).
2. SSH:ing to the gateway machine from outside the home network.
If we're SSH:ing to 'luthor' (originalhost luthor
), and the host that I'm coming from is not the same as the destination (!exec "[ %h = %L ]"
), and the MAC address of my gateway server 'luthor' can't be found on the local network (!exec "{ ip neigh; ip link; }|grep -Fw 60:0e:56:36:c4:ca"
). Then I'm outside my home network.
In this case Hostname
is set to the address visible from the internet (in my example luthor.dyndns.org
, but it could equally well have been an IP number).
3. SSH:ing from inside the home network.
If none of the above matches, then we just use the default profile which assume that we are on the home network. Any additional options added here like XForward
, etc. will be applied in all three cases.
Entries for machines on my home network look kinda similar:
Match originalhost vorlon exec "[ %h = %L ]"
LocalCommand echo "SSH %n: To localhost" >&2
Match originalhost vorlon !exec "[ %h = %L ]" !exec "{ ip neigh; ip link; }|grep -Fw 60:0e:56:36:c4:ca"
LocalCommand echo "SSH %n: From outside network, via proxy luthor" >&2
ProxyJump luthor
Host vorlon
PermitLocalCommand yes
LocalCommand echo "SSH %n: From home network" >&2
The only difference here is that instead of setting Hostname
when we're SSH:ing in from the outside, we instead set ProxyJump
(from what I gather the SSH option ProxyCommand
can also be used for this, but I haven't tried it) so that we may tunnel through the gateway and into the home network.
Note: I initially tried to use the iwgetid -r|grep SSID
command to detect when the computer I'm on is connected to my home wifi, but I wanted something that would work even if I hook up my computer with an Ethernet cable.