SSH Canonicalization
A couple of days ago I discovered OpenSSH's ability to manage canonicalization. You might be thinking to your self, "That's nice. Doesn't the system resolver take care of canonicalization? Why would you want OpenSSH to handle the canonicalization?" Those are fair questions, and I hope to provide fair answers. Further, I hope that my answers will intrigue you into using OpenSSH's canonicalization ability yourself.
The biggest reason that I see to have OpenSSH do the canonicalization is that it will canonicalize the hostnames for you and then it will re-parse the config files with the canonicalized host name. Let me say it again, OpenSSH will use the canonicalized name to match Host entries in the config files as if you had typed the full canonical name on the command line. This means that you can empower OpenSSH to do even more for you.
The following OpenSSH client config file will allow you to apply different ssh configuration options based on hostname pattern matches.
CanonicalDomains example.com example.net example.org CanonicalizeFallbackLocal no CanonicalizeHostname yes Host *.example.com IdentityFile ~/.ssh/exampleCOM User gtaylor Host *.example.net IdentityFile ~/.ssh/exampleNET Port 2222 Host *.example.org ForwardX11 yes IdentityFile ~/.ssh/exampleORG
Suppose that I have the following three Raspberry Pis connected to RepRap printers that I helped friends manage:
- reprapcommander
- netreprap
- repraporgmaster
So, here's what happens OpenSSH client does when I issue the ssh netreprap
command in my terminal.
- Process the configuration file(s) looking for Host entries that match "netreprap"
- Attempt to resolve netreprap.example.com. but fail.
- Attempt to resolve netreprap.example.net and succeed.
- Process the configuration file(s) looking for Host entries that match "netreprap.example.net"
#1 may or may not succeed, depending on what other Host entries are in the config file. #2 will fail because there is no host named "netreprap.example.com". #3 will succeed because there is a host named "netreprap.example.net". #4 will then re-process the config file(s) looking for any Host entries that match the canonical host name "netreprap.example.net". Since the canonical host name "netreprap.example.net" matches the "*.example.net" Host entry, ssh applies the IdentityFile and Port settings to the connection.
Having OpenSSH do the canonicalization has the added advantage that it can use the canonical name to apply different settings, something that could not be done if the system resolver library did the canonicalization for us. The system resolver would resolve the host names, but it would be in such a way that ssh couldn't take advantage of the canonical form, thus unable to apply specific settings.
If all of the host names followed a naming pattern you could probably create Host entries based off of those patterns. However, if your host names don't follow a pattern, you have to rely on the canonical host name, where you can apply a Host pattern.
Hopefully you can see how this will scale up quite well without needing to put a bunch of Host entries in your config file. My specific use case is with sub-domains rather than the top level domain, but the same idea holds true. I don't have any host names that appear in multiple sub-domains. Thus the three canonicalization directives and Host entries will cover thousands of machines for me, making for a nice simple client config file.
Update: 2016-01-17
A co-worker brought a problem that SSH canonicalization caused to my attention. Ultimately the problem was that CanonicalizeFallbackLocal was set to no. All of the testing that I had done was with hosts that were included in CanonicalizeDomains or other Host entries. We ended up re-enabling the default of CanonicalizeFallbackLocal = yes. This allowed all host names to resolve correctly.