Signed git commits. The why and the how

In our last post of our series "Technical pills about Platform Engineering: beyond what you find in the documentation", we talked about Leveraging AWS Step functions while optimising large-scale data transformations; today we would like to explain the importance about signing commits.

Having your commits signed adds an additional security layer to the development process. By doing so, you and others can verify the integrity, authenticity, and origin of the code changes.

We will delve into what signed Git commits are, why you'd want them signed in the first place and how to actually sign them.

Why signed commits?

When developing as part of a team, anyone with sufficient permissions can push code to your repository. While being authenticated is necessary, it alone does not guarantee that the pushed code was generated by the person pushing it. Anyone could potentially change their user.name and user.email to match yours and create a commit, effectively impersonating you.

This can mostly be avoided if you get into the habit of signing your commits, so anyone taking a look at git log (at least when doing so with the --show-signature flag) can at a glance see whether the commits attributed to you were indeed signed with your key.

Unfortunately, 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 stand out and invite one to think "maybe this is not authentic".

Ideally, everyone would sign every single commit, so any commit that was signed with an unknown key or not signed altogether would look suspicious.

How does it work?

If you already know how PKI works, you can skip this section, as you don't need to know how it works under the hood to actually sign commits.

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

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

  • Public Key: This is the one you can safely share and is used for verification. It's like a lock that anyone can access. When signing a commit, you'd use your private key to create a unique signature. Others can then use your public key to verify the signature and confirm that you're the one who created the commit.
  • Private Key: This key is kept secret and should never be shared. It's like the key to the lock mentioned before. 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.

To actually get those keys, you'd probably 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.

Getting your keys

First and foremost, you'd need 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, etc.

In MacOS the easiest way is to use brew.

If you're on Windows, you'll have to download it from their site.

Once you have it, we can proceed.

  1. Use gpg to create your key pair 
    gpg --full-generate-key
  2. Pick your key type. RSA (sign only) will do in this case
  3. Specify your key size. I'd recommend 4096, and not going below 2048 [1]
  4. Define an expiry date. I recommend the default
  5. Fill in your data
  • Optionally protect the key with a passphrase. This is highly recommended

For it to be useful, you need to inform your git provider (or your team) of your public key. This is how anyone will actually be able to corroborate your signatures.

You can do so with just two commands.

This first one will give you the information needed 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 (My key) <email@example.com>
</email@example.com>

 

From there we're interested in the key ID, which in this example would be C0C8AF0F2B994FA5. Copy it as we will need it for the final step.

The last command you'll need to run will actually output the public key.

gpg --armor --export C0C8AF0F2B994FA5

 

Its will look something like this

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

mQINBGXWjBwBEADCFaYuamcSIfyigZspureZFVuJTlTyGFVFaxuSx2me2rZBoJDp
MUCHAS LÍNEAS SIMILARES
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.

Signing commits

We have reached the last step. We're going to configure Git with the key it needs to use to sign commits.

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

It should look similar to the following

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

 

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

git config --global tag.gpgSign true

 

  • Firmar explicitamente cada commit
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.

Conclusion

Unfortunately, a very small amount of committers actually sign them. This is by no means a requirement, and will not harm your code in any way.

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

I encourage you to get used to this process and start signing your commits right away.

Additional Resources

  • GitLab provides documentation and expanded topics, such as conditionally signing commits, revoking gpg keys and troubleshooting here
  • It is also possible to sign commits with an ssh key. Again, GitLab