Alberto Ventafridda
Written on

privacy-focused ssh keys management

This is the opinionated way I manage my ssh keys.
If you use SSH or Git but aren’t familiar with SSH keys, learning about them will simplify your workflow. This quick recap is all you need to get started:

  • Ssh keys are a safer alternative to passwords for authenticating to ssh.

  • an ssh key is actually a key pair: The private key stays on your computer. The public key can be shared, and it’s comparable to a padlock that can be opened with the private key.

  • git supports ssh to connect to remote repositories. This makes ssh keys the recommended (and the easiest) way to authenticate to github or other git servers.

Generating a key

This command will generate a brand new key pair:
ssh-keygen -t ed25519 -f ~/.ssh/test_key -C "key description"

  • -t ed25519 Sets the algorithm. In 2024 ed25519 is the preferred choice; It’s widely supported, and it produces keys that are much shorter than rsa 4096, while remaining as safe.

  • -C "key description" Sets the key description. It will be part of the public key, and is therefore something that you share. A good description should help you - and whoever you share the key with - to understand where the private key is stored, and who is the owner. Something like Alberto Ventafridda work_thinkpad

  • -f ~/.ssh/test_key Sets the path where the key pair will be saved. The sensible default is to save all keys in the .ssh folder in your home directory.

I like to generate a dedicated ssh key for every service that requires it. With this practice your ssh folder will grow pretty quickly, which is why it’s important to give a descriptive name to the key file, for example github_work.
You might be wondering what’s the point of having dedicated ssh keys, when it’s absolutely safe to use the same key for multiple services. Allow me to explain:

The case for using dedicated ssh keys

When ssh tries to authenticate via public key, it sends the server all your public keys, one by one, until the server accepts one. This is a minor information disclosure issue, since public keys can then be used by a potential attacker to link someone’s presence to private infrastructure, or github accounts.
You can see this in action by running running ssh with the -v flag, or by connecting to
ssh whoami.filippo.io

To avoid this disclosure I like to generate a dedicated ssh key for every service that requires it, and explicitly associated the key to the host in the .ssh/config file. You can see an example here:

#~/.ssh/config

#some ssh server
Host homelab.halb.com
  User admin
  IdentitiesOnly yes
  PubkeyAuthentication yes
  IdentityFile ~/.ssh/id_homelab

# By default, don't send any ssh public key to avoid
# information disclosure to potential attackers.
# This block must be at the END of the config file
Host *
  PubkeyAuthentication no
  IdentitiesOnly yes
  IdentityFile ~/nonexistent
  User ubuntu

This practice will also solve another potential issue: When your client tries to authenticate with all possible keys in your .ssh folder, and there are too many of them, you will exceed the remote server MaxAuthTries limit and get the error Too many Authentication Failures. Since we are not sending any key at all, except for the ones we explicitly bind to a host, our client will never encounter this error.

The case for avoiding ssh keys on the cloud

I want to mention very briefly this topic because I’ve seen a lot of ssh keys misuse in this area: If you are manually setting ssh keys to access cloud infrastructure, chances are there are better ways to manage authentication that better integrate with the existing IAM layer.

For example, Google cloud offers Os login which binds a Google identity to linux user accounts on a VM. Compared to basic access, this system will allow you to:

  • provide fine grained authorization using Google IAM
  • integrate two-step verification
  • integrate audit logging
  • you won’t need to keep inventory of existing ssh keys

AWS offers similar features with EC2 Instance connect, and I’m sure other clouds have similar systems.
All these cloud providers offer an authentication / access control layer on top of all their services, which becomes completely useless the moment you bypass it with your own hardcoded SSH keys.

Of course this discussion applies only to cloud infrastructure; ssh keys remain a convenient tool in all other situations.

Using ssh alias

You can create host aliases for the hostnames that are too long to type. For example:

#~/.ssh/config

Host vps
  HostName a.very.long.hostname.com
  IdentitiesOnly yes
  PubkeyAuthentication yes
  IdentityFile ~/.ssh/id_vps

In this example, to connect to the server you can simply type ssh vps

managing multiple github accounts

The practice of manually associating keys to hosts makes it very easy to authenticate to multiple github accounts. You just have to create a host alias.

For example, I created a host alias for my work github account called github.com.work.
Every time I need to clone a github repository from my work account, i simply run git clone git@github.com.work:organization/repository.git and my ssh client will automatically use the correct key. This is the ssh config:

#~/.ssh/config

#personal github.com
Host github.com
  IdentitiesOnly yes
  PubkeyAuthentication yes
  IdentityFile ~/.ssh/id_personal_github

#work github.com (example usage: git clone git@github.com.work:your-repo.git)
Host github.com.work
  HostName github.com
  IdentitiesOnly yes
  PubkeyAuthentication yes
  IdentityFile ~/.ssh/id_work_github

putting everything together

This is an example of ssh config that integrates everything I’ve mentioned so far:

#~/.ssh/config

#some ssh server
Host homelab.local
  User admin
  IdentitiesOnly yes
  PubkeyAuthentication yes
  IdentityFile ~/.ssh/id_homelab

#personal github.com
Host github.com
  IdentitiesOnly yes
  PubkeyAuthentication yes
  IdentityFile ~/.ssh/id_personal_github

#work github.com (example usage: git clone git@github.com.work:your-repo.git)
Host github.com.work
  HostName github.com
  IdentitiesOnly yes
  PubkeyAuthentication yes
  IdentityFile ~/.ssh/id_work_github

# By default, connect to a ssh server with user ubuntu - a very common username,
# and don't send any ssh public key to avoid information disclosure to potential attackers.
# This block must be at the END of the config file
Host *
  PubkeyAuthentication no
  IdentitiesOnly yes
  IdentityFile ~/nonexistent
  User ubuntu