Here's a quick tour through some of my favourite
~/.ssh/config options starting with the more common to the less common.
Often when I spin up a new server with a cloud provider I don't want to bother with giving it a domain name. However it becomes annoying to type the IP address over and over in
ssh commands. Thankfully
~/.ssh/config has a solution:
For example if I have a test droplet that was given the IP address
10.11.12.13 I can add an entry to my
Host testDroplet dropletA Hostname 10.11.12.13
Now I can run
ssh testDroplet or
ssh dropletA and don't have to remember the IP address. I usually indent the lines after
Host to show they're settings for that host. Let's add some more customized settings to this server.
To avoid SSH brute force attacks I often run
sshd on a port other than
9999). That can be annoying if you have to remember to add
-p9999 to the
ssh command every time.
I can add a
Port option to the same
~/.ssh/config host to avoid this:
Host testDroplet dropletA <snipped> Port 9999
Passwords are a relic of the 70s and one of the worst ways to authenticate a user. Because of that I always set up SSH key based authentication for my servers. I know I never want to use password authentication for certain hosts so why not disable it outright?
Host testDroplet dropletA <snipped> PasswordAuthentication no
You might be familiar with SSH showing you "Host Key Fingerprints". Usually the output is something like:
$> ssh dropletA Host key fingerprint is SHA256:IqtYCzrIVi385SzZvoLuQlfSMQRqneh66RFAB/CZYTA
I don't know about you, but I don't find encoded SHA256 hashes very memorable. Thankfully there's an option for the more visually minded called
Host testDroplet dropletA <snipped> VisualHostKey yes
Now the fingerprint output is visual!
$> ssh dropletA Host key fingerprint is SHA256:IqtYCzrIVi385SzZvoLuQlfSMQRqneh66RFAB/CZYTA +---[ECDSA 256]---+ |E++oo. | |.+=+.o | |.++o. o | |o. . o | | .o = . S | |.. B + o | |* B = * | |+X = = + | |+.Bo +o. | +----[SHA256]-----+
What if the droplet is running a test service local to the server on port
1234 that doesn't have a firewall rule to allow inbound traffic? Usually I would access this by running
ssh testDroplet -L8888:localhost:1234 to make a local port forward for
1234 on the droplet to
8888 on my local machine.
Why not make that automatic too by adding a
LocalForward to the
Host testDroplet dropletA <snipped> LocalFoward 8888 localhost:1234
(Be careful here, the syntax is slightly different than on the command line)
Sometimes you don't know what host/port you want to forward through the server. This is called dynamic port forwarding. On the command line using
ssh -D 5555 dropletA would make a SOCKS5 proxy on local port 5555 that would forward through
dropletA. Of course there's a
~/.ssh/config equivalent to
Host testDroplet dropletA DynamicForward 5555
If you've worked in high security environments you might be familiar with the "bastion" (or jumpbox) design pattern. Imagine if we had three more servers,
dropletD and only wanted to allow SSH access through the jumpbox,
dropletA. That would mean I would have to run two
ssh commands to access
ssh dropletA and then
ssh dropletC. Annoying!
If you're using a modern
ssh (7.3 or newer) you can do this all in one command (
ssh dropletC) using the handy
ProxyJump feature in your
~/.ssh/config for each of the servers behind the jump host:
Host dropletA <snipped> Host dropletB ProxyJump dropletA Host dropletC ProxyJump dropletA Host dropletD ProxyJump dropletD
If you're using an old
ssh (I believe macOS is too old by default) you can fake the
ProxyJump config with
Host dropletB ProxyCommand ssh -W %h:%p dropletA
ProxyJump is so much easier I'll let you read
man ssh config to figure out the
ProxyCommand arguments if you're curious.
I live in a remote area and sometimes have to use extremely slow internet. Sometimes I'll start working on a droplet (
ssh dropletA) and later will want to run another command in a different shell (running
ssh dropletA again). That's two separate SSH connections by default and if your internet is as slow as mine you'll definitely notice the time it takes to connect once more.
Thankfully SSH has a great feature that lets you use the first connection as a persistent "control master" and have all other SSH connections multiplex on top of it.
I use the following in my
Host dropletA <snipped> ControlMaster auto ControlPersist 4800 ControlPath ~/.ssh/control/%r.%h.%p.sock
The complicated looking
ControlPath indicates where information about the connection master is stored on disk. Using patterns like
%p helps make sure that
sshing as different users to different ports on the same server won't overwrite the control master since those should be different connections. With the above
ControlPath when I run
ssh daniel@dropletA a file in the path
~/.ssh/control/daniel.<dropletA server ip>.<dropletA ssh port>.sock appears. Running
ssh daniel@dropletA a second time is lightning fast because it uses the existing connection.
The last thing to note is that I often want to set some of these settings for all servers, not just
dropletA. You can do that by adding the config under a special
* wildcard host:
Host * User daniel VisualHostKey yes Compression yes PasswordAuthentication no ControlMaster auto ControlPersist 4800 ControlPath ~/.ssh/control/%r.%h.%p.sock
Now for any host I ssh to I'll default to using username
daniel, having visual host keys, compression (slow internet remember?), no password auth, and a control master.
I hope this brief tour of the SSH config was helpful to you! You can read further about each one of these options (and more) in the
ssh config man page:
man ssh config