by Virag Mody

Make life easy with ssh_config

feature
Apr 29, 20215 mins
DevopsSoftware Development

You can avoid command line tedium and simplify access to a fleet of servers by creating a flexible configuration file for your SSH client. Here’s how.

easy simple pixel hand computer pointer
Credit: Getty Images

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:

Host myserver
      Hostname myserver.aws-west.example.com
      User virag
      Port 1024
      IdentityFile /users/virag/keys/us-west/ed25519

How does ssh_config work?

The SSH client reads configuration information from three places in the following order:

  1. System wide in /etc/ssh/ssh_config
  2. User-specific in ~/.ssh/ssh_config
  3. 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:

Host [alias]
      Option1 [Value]
      Option2 [Value]
      Option3 [Value]

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:

Host myserver2
      Hostname myserver2.aws-west.example.com
Host myserver*
      Hostname myserver1.aws-west.example.com
      User virag
      Port 1024

Using the myserver1 alias, we get what we expect from the second stanza.

      Hostname myserver1.aws-west.example.com
      User virag
      Port 1024

But myserver2 also has a similar list of options.

      Hostname myserver2.aws-west.example.com
      User virag
      Port 1024

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.

ssh_config example

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 east-prod
      HostName east-prod.prod.example.com
Host *-prod
      HostName west-prod.prod.example.com
      User virag
      PasswordAuthentication no
      PubKeyAuthentication yes
      IdentityFile /users/virag/keys/production/ed25519
Host east-test
      HostName east-test.test.example.com
Host *-test
      HostName west-test.test.example.com
      User root
Host east-dev
      HostName east-dev.east.example.com
Host *-dev
      HostName west-dev.west.example.com
      User virag
Host * !prod
      PreferredAuthentications publickey
Host *
      HostName bastion.example.com
      User Default
      ServerAliveInternal 120
      ServerAliveCountMax 5

If we were to run ssh east-test, our full list of options would read:

HostName east-test.test.example.com
User root
PreferredAuthentications publickey
ServerAliveInternal 30
ServerAliveCountMax 5

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

SSH simplicity

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 newtechforum@infoworld.com.