4 November 2021

Managing SSH config

OpenSSH is an awesome tool. However, using it may seem tedious. So many parameters to remember and type out. Three jump hosts in one line? I’m not sure it’s even possible.

Not to worry! We are going easy our interaction with the tool and tidy up its config.

No config at all

Let’s say we have a wonderful server with an address 111.222.333.444 and a user remote-user.

To access we have to type a bulky command:

ssh [email protected]

We can save some time by skipping remote-user@ if our local user has exactly the same name. We still have to remember an IP address which is somewhat doable with IPv4 but IPv6 is much more questionable. It can be solved by a domain name but they are not always there.

There are other popular options like a private key, a non-standard port.

ssh [email protected] -i ~/.ssh/wonderful_server -p 54321

Well, that’s definitely a lot to remember.

Non-native ssh config

A power user of a shell may have a nice trick up their sleeve if it’s the only server we have or one of a few. Shell alias.

For zsh we can add the following alias to ~/.zshrc:

alias ws='ssh [email protected] -i ~/.ssh/wonderful_server -p 54321'

where ws stands for Wonderful Server. It’s probably the easiest way to offload it from the memory. The result is the shortest one.

Unfortunately, it doesn’t scale really well.

Native ssh config MVP

By default ssh looks for its config in ~/.ssh/config. Let’s map a familiar command to a config file.

ssh [email protected] -i ~/.ssh/wonderful_server -p 54321

should become

Host ws wonderful_server
  User remote-user
  HostName 111.222.333.444
  IdentityFile ~/.ssh/wonderful_server
  Port 54321

Now we can access it either by typing:

ssh ws

or

ssh wonderful_server

Both ws and wonderful_server are just local names (aliases) for us to call this server. You can use as many aliases as you like (as I’m aware).

It’s super useful with services like GitLab and GitHub. I recommend having three aliases: gh, github, github.com

The first one is for you to be able to make a quick git clone:

git clone gh:ejiek/detox

The second one is handy when you have multiple upstreams.

The third one is for tools that supports ssh git interaction but have no way to override a host.

ProxyJumps

This is the first option that takes us beyond plain ssh commands in a console. For quite some time OpenSSH natively support jumping through the hosts with forwarding traffic bach to the original client. It’s awesome because you can limit your keys and ssh agent just to a local machine.

The ability to jump through multiple hosts is limited to a config file. It’s not exactly true. There is a flag -J. Unfortunately, I had a lot of struggle specifying multiple jumphosts that are not in a config file when they have a lot of parameters.

In this example, we have 3 servers: ws0, ws1, ws2. For some reason, maybe related to security, we can’t access ws1 and ws2 directly. ws0 knows how to get to ws1. ws1 knows how to get to ws2.

Let’s jump:

Host ws0 wonderful_server
  User remote-user
  HostName 111.222.333.444
  IdentityFile ~/.ssh/wonderful_server
  Port 54321

Host ws1 wonderful_server1
  User anothe-remote-user
  HostName 222.333.444.555
  IdentityFile ~/.ssh/wonderful_server
  Port 54321
  ProxyJump ws0

Host ws2 wonderful_server2
  User anothe-remote-user
  HostName 333.444.555.666
  IdentityFile ~/.ssh/wonderful_server
  Port 54321
  ProxyJump ws1

Connection to ws2 would actually establish a connection to ws0, from there to ws1 forwarding traffic back to you. Only then it’s going to connect to ws2 from ws1.

If you have a passphrase for your key and it’s not in an ssh agent, you’ll have to enter the passphrase three times. One time for each server in a chain.

Includes

There could be a lot of wonderful servers. At some point, they are going to eat up a lot of space in the main file. Fortunately, OpenSSH supports includes.

In my config I have the following before any actual host definitions:

Include config.d/*

Since the provided path is relative, it’s going to look for config.d in ~/.ssh. Let’s move all wonderful servers into a separate file ~/.ssh/conig.d/wonderful_servers

Be careful with placing the include directive. It supports using inside Match and Host.

vim configuration non default files

I had my config highlighting broken for all included files. To fix it in Vim, it was enough to add one line:

" ssh config.d/* syntax highlighting
autocmd BufRead,BufNewFile ~/.ssh/config.d/* set syntax=sshconfig

Wildcard

All the wonderful servers look pretty much the same. It’s as bad with just a few of them. With dozens - it’s a disaster.

Let’s shave things off wonderful servers configs:

Host ws0 wonderful_server
  User remote-user
  HostName 111.222.333.444

Host ws1 wonderful_server1
  HostName 222.333.444.555
  ProxyJump ws0

Host ws2 wonderful_server2
  HostName 333.444.555.666
  ProxyJump ws1

Host ws* wonderful_server*
  User anothe-remote-user
  IdentityFile ~/.ssh/wonderful_server
  Port 54321

A wildcard must go after hosts that use it. OpenSSH keeps the first value for a given parameters. Having a wildcard in front would block us from overriding values in a Host. In this example we are overriding user for ws0.

Sometimes it’s necessary to override ProxyJump not to jump at all. It’s possible with ProxyJump none.

My global wildcards

There are two of them right in the beginning of my ~/.ssh/config:

Host *
  ServerAliveInterval 240
  IdentitiesOnly yes

The first one keeps alive connections with NAT somewhere in the way. When you idle for too long, a TCP session for your ssh connect isn’t closed. However, some firewalls can decide that enough is enough and just forget about it. This option sends some packets once it 4 minutes just to keep NATs happy. You may need to tune the time.

IdentityOnly stop your OpenSSH client from going through all the keys you have, when the provided one isn’t working. Aside from not wasting time and effort, it helps not to reach the server’s attempts limit (if there is one) and be sure that you’re using a correct key. It can be fun to remove a key you think you don’t use. Not that I know of =]

To the infinity and …

OpenSSH knows many more cool tricks. Port forwarding in both directions, socks5 proxy, and much more. To find out more use man 5 ssh_config.