Securing Git with SSH and FIDO security keys

Git is currently the most popular version control system. It supports both HTTPS and SSH for accessing remote repositories. SSH is often preferred as it is more convenient and more secure.

As OpenSSH supports FIDO security keys and Git supports SSH, their combination can be used effectively to increase the security of your Git repositories.

This document describes

  • how to protect SSH access to your repositories using a security key

  • how to sign Git commits and tags with SSH keys that are protected with a security key

Protecting Git operations over SSH with a security key

When using git with SSH as its transport protocol, using a FIDO security key to authenticate Git operations works exactly the same as when setting up SSH for any other SSH connections.

If you are hosting a remote Git repository yourself, you will need to install and configure an OpenSSH server that supports FIDO. A default install will work fine, just make sure you enable SSH public key authentication.

If you are using a cloud service like Github or Gitlab, see below.

Git tag and commit signing

Because anyone can spoof a Git committer or author name with a simple git config command, many developers like to sign their tags and commits. Traditionally, this is done using PGP, and you can use a YubiKey for that as well.

However, it may be simpler to use SSH for this, as since versions 2.34 Git also supports signing tags and commits using SSH keys. This means that SSH keys can be used that are backed by FIDO security keys.

Configuring your Git client

To sign Git tags or Git commits, you will need to configure your Git client to use the SSH key format instead of the default PGP key format:

git config gpg.format ssh

In addition, you need to tell Git what SSH key to sign with. Let’s assume you generated an ECDSA key, using your security key with something like:

ssh-keygen -t ecdsa-sk -f ~/.ssh/id_ecdsa_sk

In that case, instruct Git to use the generated key for signing:

git config user.signingKey ~/.ssh/id_ecdsa_sk

To verify signatures, we also need to tell Git what SSH public keys are trusted for verification. In PGP this is done using your PGP key ring. For SSH signatures, trusted public keys are typically collected in an allowed_signers file.

The file contents is similar to an authorized_keys file, with entries starting with a signer’s email address. For instance:

cat ~/.ssh/allowed_signers AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIJhUXYvdwz3Dx45bWNmxHs1R21mlUm0o63+s4iCzRoFeAAAACnNzaDpnaXRodWI= user@host

The file format is described here.

Add this file to your Git configuration:

git config gpg.ssh.allowedSignersFile ~/.ssh/allowed_signers

See the git-config manpage for more details.

When collaborating in a team, all team members should add their colleague’s SSH keys in a similar way to be able to verify each other’s signatures.

Signing a Git commit

Signing a Git commit using SSH is no different than signing with PGP:

git commit -S -m'initial import'
[main (root-commit) 519ccdd] initial import
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 README

Your security key will blink, prompting for you to touch it.

The Git log can show and verify signatures:

git log --oneline --show-signature
519ccdd (HEAD -> main) Good "git" signature for with ED25519-SK key SHA256:FpybChVXHU/MnwIvOszDxV2yFSbDp9ZkYXxjQ2E+8x0
initial import

You can also instruct Git to reject commits without valid signatures when merging. See the Git documentation for more details.

Signing a Git tag

Similarly, you can sign tags using your security key:

git tag -s v1.0 -m'signed v1.0 tag'

Again, your security key will blink, prompting for you to touch it.

To view the tag signature:

git show v1.0
tag v1.0
Tagger: User <>
Date:   Fri Sep 23 16:10:41 2022 +0200

signed v1.0 tag

commit 519ccdda4ad0bdf6b4790a6a0d0972b12df0ef9c (HEAD -> main, tag: v1.0)

To verify the Git tag signature:

git tag -v v1.0
object 519ccdda4ad0bdf6b4790a6a0d0972b12df0ef9c
type commit
tag v1.0
tagger User <> 1663942241 +0200

signed v1.0 tag
Good "git" signature for with ED25519-SK key SHA256:FpybChVXHU/MnwIvOszDxV2yFSbDp9ZkYXxjQ2E+8x0

See the Git documentation for more details on signing Git tags and commits.

Using FIDO security keys with GitHub

GitHub supports FIDO security keys both for logging on using your browser, as well as for SSH Git operations using a git client.

You can use ECDSA as well as ED25519 keys stored on a security key such as the YubiKey. See their documentation for details.

In short, to access your GitHub repositories over SSH with your security key:

  • generate an SSH key pair backed by your security key:

    ssh-keygen -t ecdsa-sk
  • upload the public key to GitHub in your personal profile.

  • test access with

    ssh -T

Your security key should start to blink, waiting for you to touch it to approve authentication:

ssh -T
Confirm user presence for key ECDSA-SK SHA256:47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU
User presence confirmed
Hi user! You've successfully authenticated, but GitHub does not provide shell access.

You should now be able to perform Git operations protected with the FIDO credential on your security key.

If it doesn’t, verify that the SSH public keys on your security key match your registered GitHub SSH keys, available at the URL where username is your GitHub username.

GitHub also supports SSH commit verification for commits that are signed using FIDO security keys. See SSH commit verification and commit signature verification for more details.

For commit and tag signing, upload your SSH key as a Signing Key, instead of an Authentication Key.

Using FIDO security keys with GitLab

Like GitHub, GitLab supports FIDO security keys both for access using a browser (as a second factor) and when using a Git client.

As before, you can test access using your security key by initiating an SSH connection:

ssh -T
Confirm user presence for key ECDSA-SK SHA256:47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU
User presence confirmed
Welcome to GitLab, @user!

At the time of writing, GitLab doesn’t show verified tags or commits on their web interface.

Final remarks

When generating SSH keys backed by security keys, consider using additional options.

  • When you want to easily replicate the SSH key files for use on different systems, consider generating resident keys:

    ssh-keygen -t ecdsa-sk -O resident
  • For extra security whenever your credential is used for signing operations, consider requiring to always require the FIDO PIN:

    ssh-keygen -t ecdsa-sk -f ~/.ssh/id_ecdsa_sk -O verify-required
  • When storing multiple credentials on your security key, they can be hard to distinguish from one another. Consider adding an application name that starts with ssh: to tell them apart:

    ssh-keygen -t ecdsa-sk -f ~/.ssh/id_ecdsa_sk -O resident -O application=ssh:gitlab

This way, when listing the credentials stored on your security key, it becomes more easy to tell which is which:

ykman fido credentials list
Enter your PIN:
ssh: 0000000000000000000000000000000000000000000000000000000000000000 openssh
ssh:github 0000000000000000000000000000000000000000000000000000000000000000 openssh
ssh:gitlab 0000000000000000000000000000000000000000000000000000000000000000 openssh

This approach also protects you from accidentally overwriting your SSH credentials.

Supported Versions

Using your security key with git depends on specific versions of your software.

In particular:

  • SSH version 8.2 is required for FIDO support.

  • For resident keys, SSH version 8.4 is required.

  • For resident keys, you will need a security key with support for discoverable credentials (formerly known as resident keys) and the credProtect extension. For YubiKeys these are available in firmware versions 5.4 and up.

  • For using SSH signatures, Git version 2.34 is required.

Also note that:

  • the version of OpenSSH available on Windows does not support FIDO keys at the time this page was last updated.

  • the version of OpenSSH shipped with MacOS does not support FIDO keys. Use Homebrew to install a version that does.