Simplify your SSH commands with SSH configuration files.

For years I use to SSH into machines the hard way, specifying every argument, every time:

ssh -A -p 9898 james@core.example.net

That's not too horrendous, but you'll find yourself searching through your shell history to find the right command for each server every time.

With an SSH config file you could achieve the same SSH connection with a much simpler command:

ssh core

Introducing SSH Config

The man page for ssh does a good job of explaining how configuration works:

$ man ssh configTODO
NAME
     ssh_config — OpenSSH SSH client configuration files

DESCRIPTION
     ssh(1) obtains configuration data from the following sources in the following order:

           1.   command-line options
           2.   user's configuration file (~/.ssh/config)
           3.   system-wide configuration file (/etc/ssh/ssh_config)

If we have a user configuration file located at ~/.ssh/config we can configure the SSH client so that we don't need to pass in every argument every single time.

Let's take an SSH command:

ssh -A -p 9898 james@core.example.net

We could rewrite this in our SSH config file so that all we'd need to type ssh core to start a new session, in which case our config file would look as follows:

Host core
    HostName core.example.net
    User james
    Port 9898
    ForwardAgent yes

It's as simple as that!

We can now connect using:

ssh core

The SSH client loads configuration in particular order. Command line arguments take precedent over user and system configuration. This can come in useful if you want to override an argument for a specific use case.

Let's say you wanted to connect on a slightly different port, you can run:

ssh -p 7878 core

Which will use the configuration from our SSH config file, but override the port to use 7878 instead of the 9898 declared in the config file.

The Details

It's worth pointing out that an SSH config file will work with ssh, scp and sftp.

Configuration File Order Precedence

SSH takes the first value it finds for each parameter. Command line arguments take precedent over a user's config file which takes precedence over the system config file and within a configuration file earlier entries take precedent over later entries.

In the below example the user will issue the command ssh core, and the port will be set to 9898 (as defined in Host core) and the later matching entry (Host co*) is ignored:

Host core
    HostName core.example.net
    User james
    Port 9898
    ForwardAgent yes

Host co*
    Port 7878

Given that earlier entries take precedent it's important to put host-specific configuration towards the top of your config file, and leave more generic configuration that uses wildcards towards the bottom.

Jump Hosts

A common security practice when working with SSH access to private networks is to access servers by a bastion host. The bastion host acts as a single entry point to the network. Once you've SSH'd into the bastion you can then SSH into any host within the network.

SSH provides you with some provisions that allow you to SSH easily into one host via another.

In the following examples we'll run ssh web1.cloud to connect to our web server. Behind the scenes SSH will ssh into core.cloud.example.net before SSHing into web1.cloud.example.net

ProxyJump

In OpenSSH 7.3+ the ProxyJump command was introduced. We can use this to tunnel our SSH traffic to web1.cloud.example.net via core.cloud.example.net:

Host web1.cloud
    HostName web1.cloud.example.net

Host *.cloud
    ProxyJump james@core.cloud.example.net:9898
    ForwardAgent yes
    User james

ProxyCommand

If you're running an older version of OpenSSH (7.2 and earlier), you can use ProxyCommand to achieve the same thing:

Host web1.cloud
    HostName web1.cloud.example.net

Host *.cloud
    ProxyCommand ssh -p 9898 core.cloud.example.net nc -q0 %h %p 2> /dev/null
    ForwardAgent yes
    ProxyJump james@core.cloud.example.net:9898
    User james

With this configuration I can still run ssh web1.cloud to login to the web server via core.cloud.example.net which acts as our SSH bastion.

Multiplexing for slow connections

Sometimes it can be useful to re-use an existing SSH connection with multiplexing. This can work will in poor connectivity situations where you want to avoid the overhead of authenticating new SSH connections.

If you're working with an SSH bastion, multiplexing can also be helpful for speeding up the connection time to private servers behind the bastion, as the connection to the bastion is recycled between individual SSH connections from your machine to the private network.

Host core.cloud
    ForwardAgent yes
    ControlPath ~/.ssh/control-path/%r@%h:%p
    ControlMaster auto
    ControlPersist 10m

Multiple Configuration Files

Finally, it's worth noting that in OpenSSH 7.3+ the Include directive was introduced that allows you to split SSH configuration over multiple files.

For example, your main ~/.ssh/config could read as follows:

Include config.d/*

You could then put configuration into separate files, for example:

  • ~/.ssh/config.d/home
  • ~/.ssh/config.d/work
Host desktop
    Port 6565
Host web1.cloud
   HostName web1.cloud.example.net

Host *.cloud
    ProxyJump james@core.cloud.example.net:9898
    ForwardAgent yes
    User james

Using SSH configuration files can be a great way of reducing the complexity of your SSH commands. ProxyJump and ProxyCommand are also indispensable in environments where you are using an SSH bastion or a more complex setup that involves jumping through hosts. A good set of SSH configuration files will make you less dependant on your shell history for finding the right command for the right host.