Github commit signing
This article mostly talks about how to set up GPG keys. GitHub just announced you can sign commits with ssh keys. https://github.blog/changelog/2022-08-23-ssh-commit-verification-now-supported/
Signing, or code signing specifically, is the process of using cryptography to digitally add a signature to data. The receiver of the data can verify that the signature is authentic, and therefore must’ve come from the signatory. It’s like physical signatures, but digital and more reliable.
You can sign commits and tags locally, to give other people confidence about the origin of a change you have made. If a commit or tag has a GPG or S/MIME signature that is cryptographically verifiable, GitHub marks the commit or tag “Verified” or “Partially verified.”
Identity Protection/Impersonation
Why is it a must that we sign our commits? The high level issue is that the author of a commit is whoever shows up in the Author: field, which can be any random string. GitHub manages permissions on a repo using a GitHub account, which may or may not use the same email addresses in a commit. Anyone can push commits to their own repositories with anyone else’s email address.
This came up quite publicly in early 2021 when a group of GitHub users started protesting the takedown of Youtube-dl, and “forged” a bunch of commits, misattributing them to Nat Friedman, the CEO of GitHub. GitHub even happily shows the profile photo from an account with the forged email address, adding to the confusion. GitHub has since made some improvements to make this a little easier to notice, but it’s still easy enough to play a joke on someone.
Why does GitHub allow you to push commits from other people? It’s not obvious at first, but this is actually core to the Git protocol. Any time you sync a remote repo to your fork, you’re pushing commits from other people. Anytime you rebase a branch, or cherry-pick something, you’re pushing commits from other people.
Git commit signing is the only in-band way to protect against this. If you sign all of your commits, distribute your public keys, and tell anyone that cares to check the signatures, you can protect against someone impersonating you.
Installing GPG
Besides Git, the only requirement is that you must have GPG installed. I recommend using GPG version 2.2 or higher
- On Windows, you can download the Gpg4win distribution from the GPG website
- On macOS, the easiest thing is to use Homebrew:
brew install gpg2 gnupg pinentry-mac
Once brew finishes the installation, update the gpg-agent.conf to know to use pin entry-mac to hold your passphrase. In your terminal run these commands:
mkdir -p ~/.gnupg
echo “pinentry-program $(brew — prefix)/bin/pinentry-mac” > ~/.gnupg/gpg-agent.conf
- Most Linux distributions come with GPG pre-installed; if not, you can always find it on their official repositories.
Note that in some Linux distributions, the application is called gpg2, so you might need to replace gpg with gpg2 in the commands below. In this case, you might also need to run
git config — global gpg.program $(which gpg2)
Additional configuration for Linux and macOS
On Linux and macOS, you can enable the GPG agent to avoid having to type the secret key’s password every time. To do that, we need to create our ~/.gnupg/gpg.conf file. In your terminal run these commands:
# This tells gpg to use the gpg-agent
mkdir -p ~/.gnupg
echo ‘use-agent’ >> ~/.gnupg/gpg.conf
# Modify the permissions to 700 to secure this directory.
chmod 700 ~/.gnupg
You will also need to add these two lines to your profile file (~/.bashrc, ~/.bash_profile, ~/.zprofile, or wherever appropriate), then relaunch your shell (or run source ~/.bashrc or similar):
export GPG_TTY=$(tty)
gpgconf — launch gpg-agent
Restart your Terminal or source your ~/.*rc file
# on the built-in bash on macos use
source ~/.bash_profile
# if using bash through homebrew over ssh use
source ~/.bashrc
# and if using zsh
source ~/.zshrc
Generate a GPG key pair
You don’t really need to generate a new key if you already have one
To start, generate a new GPG key pair (public and private):
gpg --full-gen-key
Configure the key with:
- Kind of key: type 4 for (4) RSA (sign only)
- Keysize: 4096
- Expiration: choose a reasonable value, for example 2y for 2 years (it can be renewed)
Then answer a few questions:
- Your real name. You could use your GitHub username here if you’d like.
- Email. Your email address. Which must match your email as configured in GitHub
You will be asked to type a passphrase which is used to encrypt your secret key on disk. This is important, otherwise attackers could steal your secret key, and then they’d be able to sign messages and Git commits pretending to be you.
» gpg — full-gen-key
gpg (GnuPG) 2.3.7; Copyright © 2021 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Please select what kind of key you want:
(1) RSA and RSA
(2) DSA and Elgamal
(3) DSA (sign only)
(4) RSA (sign only)
(9) ECC (sign and encrypt) *default*
(10) ECC (sign only)
(14) Existing key from card
Your selection? 4
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (3072) 4096
Requested keysize is 4096 bits
Please specify how long the key should be valid.
0 = key does not expire
<n> = key expires in n days
<n>w = key expires in n weeks
<n>m = key expires in n months
<n>y = key expires in n years
Key is valid for? (0) 0
Key does not expire at all
Is this correct? (y/N) y
GnuPG needs to construct a user ID to identify your key.
Real name: RobertKozak
Email address: robert.kozak@emburse.com
Comment: GitHub commit signing key
You selected this USER-ID:
“RobertKozak (GitHub commit signing key) <robert.kozak@emburse.com>”
Change (N)ame, ©omment, (E)mail or (O)kay/(Q)uit? O
You can verify your key was created with:
$ gpg — list-secret-keys — keyid-format=short
/Users/robert.kozak/.gnupg/pubring.kbx
— — — — — — — — — — — — — — — — — — —
sec rsa4096/B0985F51 2022–07–20 [SC]
3799B8BA62C44E16C3F014DD0134014AB0985F51
uid [ultimate] RobertKozak (GitHub commit signing key) <robert.kozak@emburse.com>
In the example above, my new key ID is rsa4096/B0985F51, or just B0985F51.
You can confirm that GPG is working and able to sign messages with:
# Replace B0985F51 with your key ID
echo “hello world” | gpg — clearsign — local-user B0985F51
If your GPG agent is having issues, you can restart it with:
gpgconf — kill gpg-agent
gpgconf — launch gpg-agent
Configure Git to sign your commits
Once you have your private key, you can configure Git to sign your commits with that:
# Replace B0985F51 with your key ID
git config — global user.signingkey B0985F51
Tell Git to automatically sign all your commits:
git config — global commit.gpgSign true
git config — global tag.gpgSign true
Adding the GPG key to GitHub
In order for GitHub to accept your GPG key and show your commits as “verified”, you first need to ensure that the email address you use when committing a code change is both included in the GPG key and verified on GitHub.
To set what email address Git uses when creating a commit use:
git config — global user.email <Your email address>
git config — global user.name <your name>
Once you’ve done it, upload your public GPG key to GitHub and associate it with your account. In the SSH and GPG Keys settings page, add a new GPG key and paste your public key, which you can get with:
# Replace B0985F51 with your key ID
gpg — armor — export B0985F51
Your public GPG key begins with — — -BEGIN PGP PUBLIC KEY BLOCK — — — and ends with — — -END PGP PUBLIC KEY BLOCK — — -.
After you add your GPG key and if you see an “unverified” after the email address please ensure that your email address is registered to your profile. https://github.com/settings/emails
Making a signed commit
After configuring all of the above, your Git commits can now be signed with your GPG key.
git commit -m “This is my signed commit”
You can check that the commit was signed with:
$ git log — show-signature -1
commit 245ab0482e6cf19197e24f5d04bbebd593fd2288 (HEAD -> master, origin/master, origin/HEAD)
gpg: Signature made Wed Jul 20 10:42:56 2022 EDT
gpg: using RSA key 3799B8BA62C44E16C3F014DD0134014AB0985F51
gpg: Good signature from “RobertKozak (GitHub commit signing key) <robert.kozak@emburse.com>” [ultimate]
Author: Robert Kozak <robert.kozak@emburse.com>
Date: Wed Jul 20 10:42:56 2022 -0400
This is my signed commit
Configuring Visual Studio Code for signing commits
If you’re using VS Code, you can configure it to sign your Git commits with the Git: Enable commit signing flag (git.enableCommitSigning).
Submitting Your Key to a Public Key Server (very optional)
Before you jump on submitting your key to a service such as the MIT PGP Key Server, you should consider the following:
- You cannot delete your key once submitted
- Spammers have been known to harvest email addresses from these servers
- If you’re only signing your Git commits to GitHub this isn’t necessary
References:
https://docs.github.com/en/authentication/managing-commit-signature-verification
https://dlorenc.medium.com/should-you-sign-git-commits-f068b07e1b1f
https://gist.github.com/troyfontaine/18c9146295168ee9ca2b30c00bd1b41e