Setting up SSH in 2023

I’ve been using SSH for over 25 years now. Haven’t really re-evaluated how I do that in a long time. I’ve been using the same 2048-bit RSA key since 2007. And mostly without an ssh agent of any sort, typing my key passphrase (or worse, Unix password) like a barbarian. So I’m setting up something new.

I’m struck by how little has changed. I might have gotten something wrong, feel free to comment. The Arch docs were a primary reference for me.

For advanced users, the things I learned this time are: ed25519 keys, that the ancient keychain is still the tool of choice, and that it doesn’t work well with agent forwarding. Really, keychain? Has nothing changed in 20 years?

Generating a key

Go to a secure Linux box that’s been up for at least a few minutes (so it has some entropy in the random numbers) and run

ssh-keygen -t ed25519 -C "name@example.com 20230212" -f id_2023

Two files will be generated: id_2023 and id_2023.pub. The first is your private key and while it’s passphrase protected, best to keep it secret and safe. You can spread your public key far and wide. It will look like this

I’m following Github’s advice here on the type of key. 3072 bit RSA is the default and is probably fine. ed25519 is a fancy elliptic curve that a lot of folks like. I like it because the keys are shorter, not that it matters much. The comment includes a date it was generated; ssh doesn’t store that in the key file. Be sure to set a strong passphrase when prompted.

$ cat *pub
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEKye4EydeuVQ8h5wG8GyluQ5+RK7BeaW76d0aYchiUj nelson@monkey.org 20230212

$ ssh-keygen -lvf *pub
256 SHA256:nJE3PwWMgAhll0EDbmBCNRwEUG2AOHwtNJa6hnIOrco nelson@monkey.org 20230212 (ED25519)
+--[ED25519 256]--+
|B=%&B+=+.. o.    |
|++.O*oo. .. ..   |
| .o.+   o o   .  |
| . .   . + o .   |
|...     S   o    |
|+oo          .   |
|o=               |
|o .              |
|oE               |
+----[SHA256]-----+

Authorizing a key

Now that you have a key you can use the public key anywhere to authorize it. The whole public key is just one line of text so you can easily copy and paste it. Append it to ~/.ssh/authorized_keys and now most Linux machines will let you log in remotely with it. There’s also the fancy ssh-copy-id tool which will append the key for you.

Some websites like GitHub will let you add your key, generally you just paste in the one line from the .pub file.

Using a key manually

The simplest way to use a key is just name it on the command line

ssh -i ~/.ssh/id_2023 example.com

If all goes well you’ll be prompted for the passphrase for the key instead of your Unix password. I find this is a little confusing and unpredictable but ssh -v helps me sort out any problems. The “-i” flag to explicitly name the key isn’t necessary if you use one of the default filenames like id_rsa or id_ecdsa. You can also put stuff in ~/.ssh/config to name a key to use for a particular host.

Using ssh-agent and keychain in Unix tty consoles

Typing your passphrase all the time sucks. ssh-agent will maintain a persistent copy in memory of your unlocked key and use it automatically. eval `ssh-agent` is the easy way to start it, but you have to do that in every shell (and remember to kill it when the shell ends!).

The better solution for Unix console / ttys is keychain. keychain is a wrapper for ssh-agent that maintains a persistent agent process running all the time, even when you’re not logged in. (Sounds dangerous! But handy.) In a tty environment there’s an easy way to start it, just drop this in your .bash_profile

eval `keychain --eval --agents ssh id_2023`

Keychain will look for an ssh-agent and attach to it if it can. Otherwise it’ll start one and prompt you to type your passphrase to unlock it. It’s a chatty program, you may want the --quiet flag. Also it is a little obnoxious if your id_2023 file doesn’t exist on the machine, so my login script checks for that (see below)

You can run keychain --stop all to remove all agents. There’s also a --timeout option.

Keychain vs agent forwarding

SSH agent forwarding lets you forward your ssh credentials so they can be used on the remove server after you log in. The private key stays on your original machine but the intermediate machine can query your ssh agent to log in to further machines. It’s very handy if you regularly bounce through one host to another. You have to manually enable ssh agent forwarding for each host you log in to in your config file (see the link).

keychain interacts badly with agent forwarding; it’ll ignore that you have a working agent and override it with one running locally. So it’s best to only run keychain if there’s no agent already defined in SSH_AUTH_SOCK. This is what I have in my .bash_profile

SSH_KEY=$HOME/.ssh/id_2023
if [ -z "$SSH_AUTH_SOCK" -a -e "$SSH_KEY" ]; then
        eval `keychain --eval --agents ssh "$SSH_KEY"`
fi

GUIs for Keychain

All this setup is for 1970s Unix tty user interface. A modern system from the 1990s has a GUI. I used to have ssh-askpass wired up in my X session so I’d type my passphrase once per X GUI login, it was great. I’m sure MacOS folks have something similar, maybe using Apple’s own keychain. Windows also has some GUI ssh agents but none of them seem designed for a WSL-centric work mode. I’ll probably just stick to the tty thing unless it gets really annoying with VS.Code, the only Windows GUI program I use that uses ssh.

Passwordless / restricted access

All these instructions assume you have a passphrase you have to type to unlock your key. It’s possible to generate ssh keys without them, very useful for automation. But it’s also dangerous; now all your security is in the privacy of the file only. There’s ways to restrict an ssh key to only be able to run certain commands to make that safer. I’ve done this a couple of times but it makes me nervous.

Yubikey / FIDO

Ultimately ssh-agent is storing an unencrypted version of your login key in memory. Best practices now are to use a secure hardware device of some sort to store that key, particularly a Yubikey plugged in to USB. There’s support in OpenSSH for keeping your key on a Yubikey. Also for generic FIDO authentication, which I think will ultimately let you use the OS own secure enclave for key management. I bet MacOS already has a good system for that and Microsoft is working on it. I’m not using any of that, I’m content with 1970s-era security apparently.