This blog post covers how to configure the behavior of an SSH client using the ssh_config file. We will take a look at the use of wildcards and how they can be used to radically simplify your life without bloating your client.
Whether you are looking to add some additional security constraints, minimize failures, or prevent carpal tunnel, ssh_config is an often underutilized, yet powerful tool. Our goal is to make life easier to manage a fleet of servers and users. We’ll do that here by creating a flexible configuration file for our SSH client.
Please note, this post is not about server-side configuration via sshd_config.
What is ssh_config?
You may be surprised by how much of SSH client behavior can be configurable. Without a config file, specifying command line arguments to SSH becomes cumbersome quickly:
ssh -i /users/virag/keys/us-west/ed25519 -p 1024 -l virag \ myserver.aws-west.example.com
That’s too long to type once, let alone multiple times a day. If you’re managing multiple servers and VMs, creating a customized ~/.ssh/ssh_config is a great way to prune commonly used ssh commands. (Read more: “Comparing SSH Keys.”)
For example, we can shorten the above to
ssh myserver by editing the ssh_config to read:
How does ssh_config work?
The SSH client reads configuration information from three places in the following order:
- System wide in /etc/ssh/ssh_config
- User-specific in ~/.ssh/ssh_config
- Command line flags supplied to SSH directly
This means that command line flags (#3) can override user-specific config (#2), which can override global config (#1).
Going back to the example above, you may notice that ssh_config is organized into stanzas starting with a host header:
While not technically necessary, this format is legible by humans. The SSH client, however, does not care about this formatting. Instead, it will take configuration parameters by matching the SSH argument entered in the command line with any and all host headers. Wildcards can be used as part of the host header as well. Consider:
Using the myserver1 alias, we get what we expect from the second stanza.
But myserver2 also has a similar list of options.
The SSH client obtains this information by pattern matching and locking in values as it reads sequentially down the file. Because myserver2 matches both myserver2 and myserver*, it will first take the Hostname value from myserver2. Then, as it comes to the second pattern match, the User and Port values are used, but the Hostname field is already filled. Let me repeat this: SSH accepts the first value for each option.
Expanding on what we have learned, let’s see how we can organize ssh_config when we have a modest fleet. Take the following scenario:
- Virag works with six environments: Dev, Test, and Prod on both east and west coast AWS regions.
- Virag has regular user access to both Dev and Prod environments, but is root on Test.
- Prod environments have stricter security controls.
Instead of remembering several SSH command combinations, I’ve edited my local config file.
Host * !prod
If we were to run
ssh east-test, our full list of options would read:
The SSH client picked up the intended option values by matching with east-test, *-test, * !prod, and *. You may notice the Host * stanza will apply to any SSH argument. In other words, Host * defines the global setting for all users. This is particularly useful for applying security controls available to the client. Above, we used just two, but there are several keywords that will tighten up security, such as CheckHostIP, HashKnownHosts, StrictHostKeyChecking, and many more hidden gems. (Read more: “How to SSH Properly.”)
A word of caution: Because the SSH client interprets options sequentially, generic configurations should be placed towards the bottom of the file. If placed at the top, option values will be fixed before the client can read host-specific options further below. In the case above, putting Host * at the beginning of the file would result in the user being Default.
If one-off cases arise, always remember that options entered into the command line will override those in ssh_config:
ssh -o "User=root" dev
The broader takeaway from this article is to make life simple. This can be accomplished with even the simplest of configuration options used in clever ways (or using Teleport). These allow us to stay committed to strong security and minimize human error. (Read more: “Developer Friendly Infrastructure Security.”)
Virag Mody joined Teleport in January of 2020, after co-founding a software code auditing company for Ethereum applications. He continues to learn about trending technologies and produces high quality written and video content. In his free time, Virag enjoys rock climbing, video games, and walking his dog.
New Tech Forum provides a venue to explore and discuss emerging enterprise technology in unprecedented depth and breadth. The selection is subjective, based on our pick of the technologies we believe to be important and of greatest interest to InfoWorld readers. InfoWorld does not accept marketing collateral for publication and reserves the right to edit all contributed content. Send all inquiries to firstname.lastname@example.org.