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 What is Rust? Safe, fast, and easy software development Unlike most programming languages, Rust doesn't make you choose between speed, safety, and ease of use. Find out how Rust delivers better code with fewer compromises, and a few downsides to consider before learning Rust. By Serdar Yegulalp Nov 20, 2024 11 mins Rust Programming Languages Software Development how-to Kotlin for Java developers: Classes and coroutines Kotlin was designed to bring more flexibility and flow to programming in the JVM. Here's an in-depth look at how Kotlin makes working with classes and objects easier and introduces coroutines to modernize concurrency. By Matthew Tyson Nov 20, 2024 9 mins Java Kotlin Programming Languages analysis Azure AI Foundry tools for changes in AI applications Microsoft’s launch of Azure AI Foundry at Ignite 2024 signals a welcome shift from chatbots to agents and to using AI for business process automation. By Simon Bisson Nov 20, 2024 7 mins Microsoft Azure Generative AI Development Tools news Microsoft unveils imaging APIs for Windows Copilot Runtime Generative AI-backed APIs will allow developers to build image super resolution, image segmentation, object erase, and OCR capabilities into Windows applications. By Paul Krill Nov 19, 2024 2 mins Generative AI APIs Development Libraries and Frameworks Resources Videos