Sign my commits, git!

How to configure git to sign commits with ssh, how to verify them locally, and how to make sure GitHub also verifies the commits with that key.

White paper on a wooden table with three signatures with a fountain pen.

Git can sign commits with your ssh key instead of using gpg. That’s excellent, because gpg needs extra tooling to be installed on mac. For most things the default configuration you can find on GitHub’s documentation is totally fine.

The short of it is that once you have an ssh key, you need to do the following to get it to work:

$ git config --global gpg.format ssh
$ git config --global user.signingkey ~/.ssh/<MY_KEY>.pub
$ git config --global commit.gpgsign true

This will tell git that your commit signs are going to be ssh format, using the specified ssh public key, and that every commit should be signed automatically.

The assumption here is that you also have your name and email set.

$ git config --global user.name "Jon Doe"
$ git config --global user.email "jondoe@example.org"

The relevant config file so far at ~/.gitconfig should look like this:

[user]
	signingkey = /Users/<user>/.ssh/<MY_KEY>.pub
	name = Jon Doe
	email = jondoe@example.org

[gpg]
	format = ssh

[commit]
	gpgsign = true

How do I know it actually signed the commits?

An exercise in confusion

Okay, so you have the above set, you work on some work, add your changes, and commit them. Naturally you want to be sure that your commit is a) signed, and b) signed correctly!

Luckily, there’s a command for that!

$ git log --show-signature

And then that tells you that there’s no signature. At all. In my case it was this:

commit 3edfbd4d8073062582837414325950cfd4ce6942 (HEAD -> main, origin/main)
No signature
Author: Gabor Javorszky <my@email>
Date:   Fri Oct 20 00:30:36 2023 +0100

    No need to add the scheme to the flydomain method

That No signature does not tell me that it’s signed, but can’t verify the signature, or it’s not been signed at all! This was confusion #1.

To solve this one, the actual command you want to use is this to verify the commits:

$ git verify-commit <sha>

That will give you an error message, which by the way was also part of the output of the git log from above, but hard to notice. The error is this one:

error: gpg.ssh.allowedSignersFile needs to be configured and exist for ssh signature verification

The allowed signers file contains the public keys of ssh keys that are allowed to sign commits.

Create it someplace and add your ssh public key you use for the signing key from above:

$ touch ~/.ssh/allowed_signers
$ echo "$(git config --get user.email) namespaces=\"git\" $(cat ~/.ssh/<MY_KEY>.pub)" >> ~/.ssh/allowed_signers

If you then run the verify-commit command again for your last git sha, you should get a response similar to this:

$ git verify-commit 3edfbd4d
Good "git" signature for <my@email> with ED25519 key SHA256:<sha of public key>

Time to push and celebrate!

GitHub still says unverified

Once you pushed the commit to a branch and look in GitHub, you may still see that it’s unverified, even though you know that it’s signed correctly with the ssh key. There are two more things you need to do to get that green Verified badge on the commit:

  1. make sure you’ve added the ssh key (the private one) to GitHub to at least the signing key section.
  2. you also added the email address that you used to push the commit with. The commit may be signed, but if it’s not from an email address that GitHub recognises, then it’s still going to flag it as unverified
💡
As an aside, it might be a good idea to have different ssh keys for access and commit signature. If you work for a company and you add a new company specific ssh key to your account, which you also use to sign commits with, when you leave the company and your access is revoked, and you remove the ssh key, all historical commits that you used that same key to sign them with are going to turn unverified.

To recap

Have the correct configuration. Here’s a sample .gitconfig file you can edit for yourself:

[user]
	signingkey = /Users/<username>/.ssh/<yourkey>.pub
	name = Your Name
	email = youremail@example.com

[gpg]
	format = ssh

[gpg "ssh"]
	allowedSignersFile = /Users/<username>/.ssh/allowed_signers

[commit]
	gpgsign = true

Make sure the allowed_signers file exists. You can put this anywhere. Sometimes it’s in ~/.config/git/allowed_signers, it doesn’t matter. Have your public key’s content in there on a new line.

Make sure your signing ssh key and email are both added to GitHub.

Same documentation elsewhere on the web

In case you want to double check things:

Photo by Signature Pro on Unsplash