Use a YubiKey for SSH connections

This article is the third of a serie dealing with privacy on the Internet. I strongly recommend that you do the second part of this serie of articles (if you haven’t already done so) otherwise you will have a hard time following it. Technically it is not impossible but throughout these articles, the keys used will be the same (among others) and if you don’t have the same structure, it will be a real headache.

The daemon to manage private keys independently from any protocol (gpg-agent) supports the OpenSSH ssh-agent protocol, as well as Putty’s Pageant on Windows. This means it can be used instead of the traditional ssh-agent. There are some differences from ssh-agent, notably that gpg-agent does not cache keys but converts it, encrypts and stores them persistently as GPG keys and then makes them available to SSH clients. Any existing SSH private keys that you’d like to keep in gpg-agent should be deleted after they’ve been imported to the GPG agent.

When importing the key to gpg-agent, you’ll be prompted for a passphrase to protect that key within GPG’s key store. You may want to use the same passphrase as the original’s SSH version. GPG can cache passphrases for a determined period as well as SSH. Note that when removing the old private key after importing to gpg-agent, keep the .pub key file around for use in specifying SSH identities.

Information and requirements

These elements are to be taken into consideration to follow this article:

Create the configuration

Copy the following snippet into ~/.gnupg/gpg-agent.conf.

enable-ssh-support
default-cache-ttl 60
max-cache-ttl 120
pinentry-program /usr/bin/pinentry-curses

The cache-ttl options do not apply when using a YubiKey as a smartcard as the PIN is cached by the smartcard itself. Therefore, in order to clear the PIN from cache (smartcard equivalent to default-cache-ttl and max-cache-ttl), you need to unplug the YubiKey.

Replace agents

Copy the following snippet into the shell rc file. Mine is located at ~/.config/fish/config.fish but for BASH it’s ~/.bashrc.

set -x GPG_TTY (tty)
set -x SSH_AUTH_SOCK (gpgconf --list-dirs agent-ssh-socket)
gpgconf --launch gpg-agent
gpg-connect-agent updatestartuptty /bye > /dev/null

Copy public key

It is not necessary to import the corresponding GPG public key in order to use SSH. Copy and paste the output from ssh-add -L to the server’s authorized_keys file.

ssh-add -L
ssh-ed25519 AAAA[...]

Save public key for identity file configuration (optional)

By default, SSH attempts to use all the identities available via the agent. It’s often a good idea to manage exactly which keys SSH will use to connect to a server, for example to separate different roles or to avoid being fingerprinted by untrusted SSH servers. I’ve already talked about that in this article.

The argument provided to IdentityFile is traditionally the path to the private key file. For the YubiKey, it should point to the public key file, SSH will select the appropriate private key from those available via the SSH agent.

In the case of YubiKey usage, extract the public key from the SSH agent.

ssh-add -L > ~/.ssh/id_ed25519_yubico.pub

Add these lines at the end of your ~/.ssh/config (after other Host directives).

Host	*
	PubkeyAuthentication no
	IdentitiesOnly yes

Then you can explicitly associate this YubiKey-stored key to use it with a host. Copy the following snippet into the SSH configuration file (usually ~/.ssh/config).

Host	example.com
	PubkeyAuthentication yes
	Hostname <IP address
	User <user>
	Port <port>
	IdentityFile ~/.ssh/id_ed25519_yubico.pub

Connect with public key authentication

If you have enabled the authentication touch, you will need to touch the YubiKey to complete the authentication process.

ssh user@example.com

The third part is done. Now, we will see how to use a YubiKey to sign commits and tags for GitHub, Gitea or GitLab (it’s the exact same things).