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. 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: 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: 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. Related content feature 14 great preprocessors for developers who love to code Sometimes it seems like the rules of programming are designed to make coding a chore. Here are 14 ways preprocessors can help make software development fun again. By Peter Wayner Nov 18, 2024 10 mins Development Tools Software Development feature Designing the APIs that accidentally power businesses Well-designed APIs, even those often-neglected internal APIs, make developers more productive and businesses more agile. By Jean Yang Nov 18, 2024 6 mins APIs Software Development news Spin 3.0 supports polyglot development using Wasm components Fermyon’s open source framework for building server-side WebAssembly apps allows developers to compose apps from components created with different languages. By Paul Krill Nov 18, 2024 2 mins Microservices Serverless Computing Development Libraries and Frameworks news Go language evolving for future hardware, AI workloads The Go team is working to adapt Go to large multicore systems, the latest hardware instructions, and the needs of developers of large-scale AI systems. By Paul Krill Nov 15, 2024 3 mins Google Go Generative AI Programming Languages Resources Videos