SSH: Conditional host address based on network or location

I have been searching extensively to find a solution to ease management and administration of my servers from multiple location. Let me present my environment and then my use cases

Environment

  • Site One (10.0.1.0\24)
    • Server A
    • Server B
    • Server C
  • Site Two (10.0.2.0\24)
    • Server D
    • Server E
  • Site Three (10.0.3.0\24)
    • Server F

Use cases

I manage these servers from any of the sites and even other external networks and the two main issues I have had are

  1. Reaching any server from any other server, workstation or laptop in the field with:
    ssh server-x
  2. Be able to bridge multiple server jumps. Normally, only “A”, “D” and “F” have an externally port forwarded to it.
    For example, sending a file from “B” to “E” is troublesome with

    rsync local_file server-e:.

My solution

Perhaps my Google skills are lacking, but finding a solution that is efficient across all locations and dynamic was hard. There were quite a few samples of having two settings in the ssh config file for each host, i.e. server-x-local and server-x-remote. Sure, doable but a pain to maintain across all sites and not transparent enough.

The solution to this problem was the Match keyword for the ssh config. This is the configuration for Server A:

Match Originalhost server-a Exec "ifconfig | grep 10.0.1"
     Hostname 10.0.1.2
Host server-a
     Hostname external.siteone.com

Let’s explain this construct. The match clause executes the ifconfig and pipes the output to grep in order to see if we are on this subnet. This will evaluate to true if I’m located on any host on subnet One. If the match is true it will set the hostname to the local IP of the server. If the clause evaluates to false, the default (external DNS) hostname of the server will be used. According to the ssh_config man page, the first match of a setting will be used. Therefor, the Match clause needs to be before the Host.

This works great from my initial testing and makes it possible to use the very same ssh config file regardless of client.

The second issue: Jump/Proxy/Bastion setup.

In order to reach other servers behind the externally connected servers easily I had previously been using SSH tunneling to solve some tricky cases. Now that I had some Google flow I attempted to take a stab at that challenge as well.

As it turns out, this was an easier fix thanks to the ProxyCommand feature in later SSH versions. From my understanding, this executes a custom command as a precondition to the final SSH connection.

Expanding on the previous solution, this is what I came up with. First, the complete Server A ssh configuration, same as before.

Match Originalhost server-a Exec "ifconfig | grep 10.0.1"
     Hostname 10.0.1.2
Host server-a 
     Hostname external.siteone.com

Then, the Server B configuration:

Match Originalhost server-b Exec "ifconfig | grep 10.0.1"
     ProxyCommand none
Host server-b
     Hostname 10.0.1.3
     ProxyCommand ssh -W %h:%p server-a

The idea here is the same. The default case is connecting from an external site and the ProxyCommand initiates an ssh connection to server-a first and uses it as a proxy to then connect to server-b. If, on the other hand, we are located on the local subnet already, the ProxyCommand is disabled and no proxy connection to server-a is made.

All that remains now is to configure all different hosts at one client and the roll out the very same config file to any host that is going to connect to any of the servers.

Conclusion

I’m really happy with this solution and it has greatly improved my efficiency when connecting to the different servers. Now the connections are really transparent and flexible!

Let me know if you have any success with this solution!

Credits

http://sshmenu.sourceforge.net/articles/transparent-mulithop.html

https://unix.stackexchange.com/questions/150002/only-apply-match-keyword-to-single-host-in-ssh-config/