The OpenSSH client and server applications are ubiquitous. Like many a software dev, I’m `ssh’ing all over the place. And you know what? I’ve put off learning the ins and outs of its configuration for far too long. I learned that a little bit of know-how can simplify my day-to-day use of SSH. That’s exactly why I’ve written this post to exemplify the configuration options I’m now using.

SSH Client Configuration

The file ~/.ssh/config contains settings for a user’s SSH client. System-wide SSH client settings are placed in /etc/ssh/ssh_config. Settings here can be defined per-host. General configuration options include aliases, SSH agent behavior, and jump hosts to name a few. Different sections of a sample ~/.ssh/config are discussed in detail below. Further documentation is available from the ssh_config manpage. This article assumes the reader is familiar with the basics of SSH and the syntax of SSH configuration files.

localhost

The first option does not reside in a Host or Match block because it only refers to one host, localhost on the current machine.

~/.ssh/config
NoHostAuthenticationForLocalhost yes

Normally, SSH consults the ~/.ssh/known_hosts when connecting to a host. The known hosts file stores an identifying key for each host it has connected to previously. As connections are made to new hosts, keys are registered in the user’s known hosts file. Each time SSH establishes a connection to a known host, the key is checked, and the connection is aborted if it does not match.

When running multiple SSH servers from a single host, the key will fail to match whenever switching servers. If you connect to various VMs through localhost, SSH will throw an error whenever switching between machines. Setting NoHostAuthenticationForLocalhost to yes is a convenient way to workaround by skipping host checking is skipped for localhost altogether.

This does not skip host checking for the localhost address available on jump hosts. See the section below on HostKeyAlias for such a use case.

A Humble Host Alias

The following section contains only one option which applies to vm_host. As an example, vm_host represents a computer dedicated for running multiple virtual machines.

~/.ssh/config
Host vm_host
  Hostname vm_host.local

The option, Hostname, indicates the true hostname of this host which means vm_host is really just an alias. The alias vm_host can be used on the command-line instead of having to type out vm_host.local.

Jump Hosts and Multiple Servers on a Single Host

Next, several configuration options are given for the hosts vm1 and vm2.

~/.ssh/config
Host vm1
  HostKeyAlias vm1.localhost

Host vm2
  HostKeyAlias vm2.localhost

Host vm1 vm2
  Hostname localhost
  Port 9001
  ProxyJump vm_host

Starting with the shared configuration options, the Hostname and Port options indicate that both hosts are available on the port 9001 at the reserved localhost address. Since both vm1 and vm2 use the same hostname and port, they can’t both be available simultaneously. In this scenario, either virtual machine vm1 or vm2 would be available at any one time.

The ProxyJump key is used here with one intermediate host, vm_host, defined previously. This means that the SSH connection will connect to the localhost address on vm_host. So, instead of executing ssh vm_host followed by ssh -p 9001 localhost to access either of the VMs, use just one command either ssh vm1 or ssh vm2.

The individual host blocks provide a distinct HostKeyAlias for each host. This is critical because these virtual machines will have different host keys but share the same hostname localhost. When checking the known hosts file, instead of looking for an entry for localhost, SSH will look for an entry for the value provided to HostKeyAlias. The configuration line NoHostAuthenticationForLocalhost yes from the beginning of the file only works for the localhost address on the current machine. In other words, this option has no effect when connecting to localhost on any other machine, including a jump host. According to the ssh_config manpage, HostKeyAlias should be used for handling multiple servers running on a single host.

Multicast DNS

The following configuration accommodates the variability of the IP address associated with a given hostname on networks using multicast DNS.

~/.ssh/config
Host *.local
  CheckHostIP no

Multicast DNS uses the ".local" top-level domain exclusively for hosts on a network. Setting CheckHostIP to no here allows IP addresses for any ".local" hosts to change.[1]

It might be worth setting this option for all users in the system-wide configuration file /etc/ssh/ssh_config.

The SSH Agent

The last section contains a few defaults for all hosts, which boil down to a few quality-of-life improvements.

~/.ssh/config
Host *
  AddKeysToAgent yes
  IdentitiesOnly yes
  IdentityFile ~/.ssh/id_rsa_catalina-build.pub
  PreferredAuthentications publickey,keyboard-interactive,password,gssapi-with-mic,hostbased
  ServerAliveInterval 90
  ServerAliveCountMax 4

  # Specific to macOS
  UseKeychain yes

The meanings of these options are described one-by-one below.

AddKeysToAgent

Automatically registers SSH keys with the SSH Agent as they are loaded.

IdentitiesOnly

Limits the identities that will be used to authenticate. Because I use KeePassXC to handle my SSH keys, the SSH Agent may have lots of other keys loaded which can lead to authentication problems.

IdentityFile

Provides the path to an authentication identity. The private key file is often provided for this option, but providing the public key works when storing the private key itself directly within KeePassXC.[2].

PreferredAuthentications

Sets the order of preference for the various authentication methods. Here, public key cryptography is preferred first because I use this most often.

ServerAliveInterval

The amount of time in seconds between sending successive SSH keepalives to the SSH server. Setting this option enables SSH server keepalives, which are helpful for keeping existing SSH connections open. A value of 90 here results in a keepalive being sent every 90 seconds.

ServerAliveCountMax

The number of successive keepalives the SSH server must fail to respond to before terminating the connection. With a max count of four and a ServerAliveInterval of 90 seconds, the session will be terminated after six minutes where no response from the SSH server is received.

UseKeychain

On macOS, this allows storing and accessing keys from the user’s Keychain so they are automatically available after logging on.[3]

SSH Agent Forwarding

It is possible to forward the SSH Agent to avoid typing passwords so much. You can forward the SSH Agent by setting ForwardAgent to yes, shown in the following example.

~/.ssh/ssh_config
Host server
  ForwardAgent yes

Be wary when using agent forwarding. Those with administrative access on the forwarded machine will be able to use the keys from your agent to authenticate as you.[4]

SSH Sever Configuration

The SSH server is configured in /etc/ssh/sshd_config. I explain my typical server options here.

/etc/ssh/sshd_config
ClientAliveInterval 90
ClientAliveCountMax 4
ClientAliveInterval

The amount of time in seconds between sending successive SSH keepalives to the SSH client. Setting this option enables SSH client keepalives, which are helpful for keeping existing SSH connections open. A value of 90 here results in a keepalive being sent every 90 seconds.

ClientAliveCountMax

The number of successive keepalives an SSH client must fail to respond to before terminating the connection. With a max count of four and a ClientAliveInterval of 90 seconds, the session will be terminated after six minutes where no response from the SSH client is received.

Conclusion

That was a bit of configuration. Assuming I didn’t just cover topics you already knew, you should have a better understanding of SSH and its configuration options. Hopefully you’ll be able to capitalize on this knowledge by simplifying your SSH work flow. If you’re interested in smoothing out even more wrinkles in your SSH use, you might checkout the Mosh project.