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…
Use
gpgto create your key pairgpg --full-generate-keyPick your key type.
RSA (sign only)will sufficeSpecify your key size. I recommend
4096, and never going below2048[1]Set an expiry date. I recommend the default
Fill in your data
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 trueExplicitly 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-maclike so:brew install pinentry-macAfterwards, let GPG know of this by editing (create if it does not exist)
~/.gnupg/gpg-agent.confand adding this linepinentry-program /opt/homebrew/bin/pinentry-mac.Lastly you’ll have to force a restart of the
gpg-agentby killing itpkill -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
- GitLab provides some nice documentation and expanded topics, such as conditionally signing commits, revoking GPG keys, troubleshooting and useful topics in general.
- It is also possible to sign commits with an ssh key.