While managing servers directly via SSH is mostly an anti-pattern these days (there’s always that
red-headed stepchild of a host that runs those cron
entries you’re just not sure if you can
delete), I still use it heavily. Here are some tips and usage patterns I’ve integrated into my
workflow – and I’m sure I have barely scratched the surface!
Keep config in sync across workstations
I really like to keep my configuration in sync across devices (and backed up!). I use syncthing to accomplish this. I have all my machines (NAS, desktop, laptop{0…?}, phone, work machine) all setup to sync. This is crazy helpful to avoid losing keys and keep consistent configuration.
I recommend using the .stignore
file to avoid synchronizing certain files (eg, work keys/config,
known_hosts, authorized_keys). This is configured per-device!
// avoid sync conflicts with known hosts
known_hosts
// I prefer to directly manage which machines I can ssh into via my key
authorized_keys*
// skip synchronizing work keys/conf
**work**
Let your config files do the work
Entropy is great isn’t it? What was once a pretty simple id_rsa
you setup when first cloning a git
repo or configuring a raspberry pi has proliferated into a folder full of keys as you migrated
between computers and setup a homelab and forgot old passphrases. How to keep track of which key to
use on which servers? And forget keeping track of usernames…
Luckily, your ssh config can remember all of that!
✅ Tip ✅
On Linux, the global ssh config is located at
/etc/ssh/ssh_config
. It’s a great place to start familiaring yourself with the available options and to learn howssh
prioritizes them. You can alsoman ssh_config
.
I keep my ssh config split up into appropriate files. This enables selective syncing and makes it much easier to manage over time.
Base config – ~/.ssh/config
I use this mostly to load in my application or environment specific configs, as well as to set some
global Host
settings:
# Apply to all hosts unless overridden by more explicit match
Host *
# Only use identities (eg, keys) explicitly defined via CLI or ssh config
# This avoids extra work trying invalid keys
IdentitiesOnly yes
# Check if remote server is alive, terminate session after 90s if not
ServerAliveInterval 30
ServerAliveCountMax 3
# Automatically add keys to ssh-agent
AddKeysToAgent yes
# Include my env or account specific options
Include config.d/*.conf
Home config – ~/.ssh/config.d/$HOMENETWORK.conf
This is for things like my NAS, other personal computers, etc.
Host $DESKTOP # match exactly my desktop name
# Specify my home username & personal ssh key
User jesseops
IdentityFile ~/.ssh/jesseops
# Override IP to get around VPN issues capturing DNS lookups
Hostname 192.168.x.x
Host *.localnet # Match any host on my localnet
User $AUTOMATIONUSER
ForwardAgent yes # Let me jump from one host to another
IdentityFile ~/.ssh/localnet_infra
Git config – ~/.ssh/confid./git.conf
# git specific keys!
Host github.com
IdentityFile ~/.ssh/github
Host bitbucket.org
IdentityFile ~/.ssh/bitbucket
Work config – ~/.ssh/config.d/work{,_git}.conf
Here I keep track of customizations for $current_job and can exclude it from synchronizing to my personal machines.
# NOTE: I now use the _proper_ way, ie the core.sshCommand gitconfig entry instead
# of hacky URL rules. Much easier.
# Neat little trick in combination with my git config that allows me to override
# the keys I use to access github/bitbucket (useful for public hosted organizations)
# Host github.com-work
# HostName github.com
# IdentityFile ~/.ssh/work
# Specify multiple hostname patterns
Host *.workdomain *.workdevdomain
IdentityFile ~/.ssh/work
User $USERID
# Add a bastion for accessing eg cloud hosted systems
Host work-bastion
HostName $bastionip
User ec2-user
IdentityFile ~/.ssh/bastion-key
StrictHostKeyChecking no # not secure _but_ for ephemeral instances the host key changes often
UserKnownHostsFile=/dev/null # Lets just skip tracking host keys anyway
# Access host via bastion
Host someinternal.cloud
HostName $internalip
User ec2-user
# May not be available in your ssh version, check out
# https://superuser.com/questions/1253960/replace-proxyjump-in-ssh-config
ProxyJump work-bastion # exactly what it sounds like