Managing SSH identites.md
How do people live with more than one SSH identity? Here’s how I do.
My way
⚠ May be out of date, since I no longer need this mechanism and don’t use it.
Identify the keys
First I need a way to identify keys for myself. Something I can use to understand what am using at any given moment. Filenames could be an obvious choice, but I don’t store private keys in my file system directly. I use an SSH Agent and KeePass combo.
What I did is I exported public key files from my KeePass and stored them in ~/.ssh/identities
. Public keys are not secret at all, in fact, you can get public SSH keys of any GitHub user by using a URL like https://github.com/{username}.keys
. Here are mine. Thus no, doing so is not in any way a security risk.
Why are public keys useful, you might ask, if ssh
needs a private key to authenticate? Read on.
Tell ssh
the correct key
What I found is that ssh
can actually accept public keys as identities, as long as it has access to respective private keys! And in my case it can access keys through the SSH agent. This works both with the -i
option of ssh
to specify a key for a single command and with a default key location like ~/.ssh/id_rsa.pub
. I specify the key I need by symlinking it to the default path:
rm ~/.ssh/id_rsa.pub
ln -s "$pubkey" ~/.ssh/id_rsa.pub && \
echo "Using $(basename "$pubkey" .pub) as SSH identity"
Yes, I didn’t define $pubkey
yet. There’s more than one way to do that.
Key selection menu
I have the files with meaningful names in ~/.ssh/identites
and now need a way to pick one of them. The snippet above uses $pubkey
, so that is where I’ll place the result.
These solutions have one thing in common: the find ~/.ssh/identities -type f
command that outputs all the identity files.
Option 1: bash, select
This command outputs a prompt where every option has a number. You need to type in the number of the option you want and hit Enter.
select pubkey in $(find ~/.ssh/identities -type f)
do
rm ~/.ssh/id_rsa.pub
ln -s "$pubkey" ~/.ssh/id_rsa.pub
break
done
Option 2: fzf
fzf
is a nifty command-line utility that accepts multi-line input displays a menu out of them, plus an input field that accepts a search query. The search algorithm is “fuzzy”, hence the name (FuZzy Finder); similar algorithms drive functions like the “command palette” and “go to anything” in many modern text editors (probably popularized by Sublime Text):
pubkey=$(find ~/.ssh/identities -type f | fzf)
rm ~/.ssh/id_rsa.pub
ln -s "$pubkey" ~/.ssh/id_rsa.pub
Display the selected key
I use zsh with a Powerlevel10k theme that displays prompt segments relevant to the command I’m typing in.
This is the section of my ~/.p10k.zsh
that displays the current key when I need it:
#####################################[ ssh_key ]#####################################
# Shows the name of the public key behind the symlink at `~/.ssh/id_rsa.pub`
function prompt_ssh_key() {
p10k segment -f 208 -i '🔑' -t "$(basename $(readlink ~/.ssh/id_rsa.pub) .pub)"
}
# Hide the segment by default, show it when it's needed:
typeset -g POWERLEVEL9K_SSH_KEY_SHOW_ON_COMMAND='ssh|git|hub'
Other (bad?) ways
A typical way to do this is to use the -i
argument and specify the key explicitly in every ssh
command. That’s a hassle as it often requires editing the command someone else may have given. This can be somewhat alleviated by storing keys in the same places, but that’s too much to ask: for instance, I prefer not to store SSH keys in the filesystem at all.
Another way is to use an alias for when you need to use a particular key. That works when you’re invoking ssh
from the command line yourself, but doesn’t work when you’re using a script that includes its own call to ssh
. So I wanted a way that works implicitly.
Implicitly. Okay. How about SSH config then? It allows specifying keys that are specific to certain hosts, be it a hostname, IP address or a subnet. What could go wrong there? Rather a lot.
- IP addresses from private ranges may be pointing at different hosts depending on the network you’re in, and yes, a machine may roam between networks both physically (especially if it’s a laptop) and virtually (using VPN). Use hostnames then? Hostnames aren’t always set up properly, unfortunately, one of the systems I worked on had a script that obtained an IP address that matches the given constraints.
- One and the same set of hosts could be providing you access to different resources depending on the key you used. And there’s a popular example: GitHub. A popular myth with beginners is that SSH key is a direct alternative to a password, except with a different way of usage. That’s not true: an SSH key both tells a service who you are and proves it, and in that sense it serves as both username and password. The username you use to access GitHub is actually
git
, for everyone. Executingssh git@github.com
allows you to see what account you’re authenticated with on GitHub.
Table of contents
Notes mentioning this note
There are no notes linking to this note.