Signed Git commits offer an extra layer of security in the development process. By digitally signing your commits, you and others can verify the authenticity, integrity, and origin of the code changes.

We will explore what signed Git commits are, why you want them signed and how to actually sign commits.

Why Sign Commits? Link to heading

When working in a team, anyone with sufficient permissions can push code to a repository. While it is true that you’d need to be authenticated while doing so, this does not guarantee that the pushed commits were generated by the person pushing them. Anyone could just change their user.name and user.email to match yours and commit code with abysmal variable names, effectively impersonating you.

This kind of forgery can mostly be avoided if you consistently sign your commits, so anyone taking a look at git log (at least when doing so with the --show-signature flag) can see whether that commit “you” made that broke production was indeed signed with your key.

Let me be clear, this will in no way prevent people from using your username and email in malicious commits, but since all of yours are signed it will at least look suspicious and prompt one to think “maybe this is not authentic”.

In an ideal world, everyone would practice this, so any commit that was signed with an unknown key or not signed altogether would immediately stand out as bogus. Plus you’ll have a nice green verified badge in each of your commits, everyone likes that.

How Does it Work? Link to heading

If you already know how PKI works or are not interested in going a little bit deeper, you can skip this section, as it is not necessary to actually use signed commits. In case you are curious about how this actually works, read on.

For those that remain, let me tell you that cryptographic keys play a fundamental role in signing commits. You can find the long explanation in Wikipedia, but here’s a short primer.

There are two parts at play, a public and a private one.

  • Public Key: This is the one you’d share and is used for verification. It’s a bit like a lock that anyone can access. When you sign a commit, you use your private key to create a unique signature. Others can then use your public key to verify that signature and confirm that you’re the one who made the commit.

  • Private Key: This key is kept secret and should never be shared. It’s like the key to the lock mentioned earlier. You use your private key to create the signature for your commits. Since it’s known only to you, it ensures that only you can produce a valid signature for your commits.

Easy, right?

To actually get those, you’d most likely use GPG, which stands for GNU Privacy Guard, widely recognised for its reliability and security. With it, you can create the pair of keys mentioned above and perform various management tasks on them.

Now that you’re a cryptography master let’s see how to actually use all this.

Creating Your Keys Link to heading

First order of business is to actually have GPG available and ready to be used. If you are on Linux or MacOS, you either already have it installed or can install it very easily.

For Linux you will have to use your package manager, such as pacman, apt, yum or whichever one corresponds to your distro.

In MacOS the easiest way is to use brew.

If you’re on Windows, you’ll have to download it.

Now, for the actual instructions…

  1. Use gpg to create your key pair

    gpg --full-generate-key
    
  2. Pick your key type. RSA (sign only) will suffice

  3. Specify your key size. I recommend 4096, and never going below 2048[1]

  4. Set an expiry date. I recommend the default

  5. Fill in your data

  6. Optionally protect the key with a passphrase. Highly recommended

As of now, you are the proud owner of a GPG key pair.

For it to be of any use though, you need to let your git provider (or your team) know of your public key. This is how anyone will actually be able to corroborate your signatures, remember?

To do so you just need to run two commands.

The first one will give the information you need to later export the public key.

gpg --list-secret-keys --keyid-format long

Your output should look something like this.

/home/nahuel/.gnupg/pubring.kbx
------------------------
sec   rsa4096/C0C8AF0F2B994FA5 2015-10-21 [SC]
      2AA35F533E6A86C60F303250C0C8AF0F2B994FA5
uid                 [ultimate] Nahuel (Awesome key) <totallymyemail@trust.me>

From there we’re interested in the key ID, which in this example would be C0C8AF0F2B994FA5, i.e. what comes after the rsa4096/. Copy it as we will need it for the final step.

The last command you’ll need to run will output the key. Remember this one is the public key and we could put it in a billboard for all we care.

gpg --armor --export C0C8AF0F2B994FA5

Its output will look like this

-----BEGIN PGP PUBLIC KEY BLOCK-----

mQINBGXWjBwBEADCFaYuamcSIfyigZspureZFVuJTlTyGFVFaxuSx2me2rZBoJDp
LOTS OF SIMILAR LINES
PN8qmuUdyr8/k87CwLrtOwft2Rt3jV4=
=UQNM
-----END PGP PUBLIC KEY BLOCK-----

As the final step you’ll copy all of those lines and paste them in your Git provider. You can refer to GitHub or GitLab instructions for specifics.

Finally Signing Commits Link to heading

We have arrived at the last step. We’re going to let Git know the key it needs to use to sign commits with.

We will again need the key ID obtained on the last step with gpg --list-secret-keys --keyid-format long

With the key ID from the example, it would be like this

git config --global user.signingkey C0C8AF0F2B994FA5

This will instruct Git to use that key to sign commits from now on. You’d still have to request for commits to be signed though.

For this there are two options.

  • Let Git sign every single commit (recommended)

    git config --global commit.gpgsign true
    
  • Explicitly sign when committing

    git commit -S -m "Not an awful commit message"
    

This is entirely your decision, both will end up signing your commits with the key defined.

ⓘ MacOS Users: If you’re using a key with a password, you’ll need to enter it somewhere, the easiest method is installing pinentry-mac like so:

brew install pinentry-mac

Afterwards, let GPG know of this by editing (create if it does not exist) ~/.gnupg/gpg-agent.conf and adding this line pinentry-program /opt/homebrew/bin/pinentry-mac.

Lastly you’ll have to force a restart of the gpg-agent by killing it pkill -TERM gpg-agent.

ⓘ Note: By default, Git should automatically sign tags, but this is how you’d explicitly activate it

git config --global tag.gpgSign true

Conclusion Link to heading

In my experience a very small amount of committers actually sign them. This is of course not a requirement, and will not harm your code in any way.

Take into consideration that a malicious actor impersonating you and having access to a repository to commit in your name should be a fairly unlikely situation, still it is better to be safe than sorry. Especially when those commits are work-related.

I encourage you to familiarise yourself with this process and start signing your commits right away.

Additional Resources Link to heading