Git is a tool for controlling versions of source code and nowadays of anything as code. This article is intended to help you get started in using the option to sign your commits and tags.
As a reminder I would like to point out that when you clone a git repository, you clone all of the original repository including all commits, tags and changes from the start. Each commit contains meta information like the user name and user email. However, these user names are just set in your own git client configuration:
git config --global user.name "Someone" git config --global user.email firstname.lastname@example.org
When you make changes and create a commit this name and email address will be stored in the git repository. And when you push these changes, this information will be available in the remote repositories as well. So no real proof of who really made the commit.
Authentication of the committer
A first solution to this problem is to use a centrally managed git server. Examples of these are GitHub, Bitbucket, GitLab, and others. All of these managed git servers offer authentication of user access to the repositories as well as other possible restrictions (like rewriting history). All of this extra information is not stored in the git repository itself, but in a datastore used by the managed git server. With this information, you can find out the user id which was used to authenticate with when the commit was pushed. All of these managed git environments use username/password or SSH key based access for authentication.
Is this enough? Do we need signing as well?
The original concept of git was based on a collection of equal git repositories. So in this concept there was a need to be able to trust commits of participants in a repository. Therefore, Git supports signing of commits and tags. And, the signing information is also stored in the Git repositories. The whole concept is based on Pretty Good Privacy, which has a well known implementation of GPG (GnuPG) on Linux, Windows, MacOS, and others. So these signatures in combination with the exchange of the public keys between trusted parties, makes it possible to trust the commit or tag. As these are stored in the repository, you could even move from one managed git server/cloud to another git server/cloud and still have the same trust as it is all contained within the repository.
Example: Signing tag in a CICD pipeline
One use case where signing is also of value even when a managed git server is used, is pushing a signed label as an audit step in a CICD (continuous integration/continuous delivery) pipeline. A tag is a unique label that points to a certain commit which represents the code as it was at that moment in time. Signing the tag means that this label has been used by the owner of the private key and that all others can verify that the label is valid and signed by the expected person. A CICD service account could for instance sign and push this tag as part of the processes of delivering software to production.
Example: GitHub signs commits made from the GUI
Another example is GitHub, which automatically signs commits on behalf of you when you make changes directly from the WEB UI.
Step 1: Install GPG
Before you can do anything with gpg based signatures in git, you must install gpg and configure git to know which gpg program to use:
#e.g. on a Mac you can install gpg using brew brew install gpg #now instruct git to use this: git config --global gpg.program gpg
Now, you can check to see the signature data from the commits, by:
git log --show-signature
The output on a GitHub based repository could already contain some entries like this:
commit d8c7e72cbed11411b80995d415311c7fb6399d9a gpg: Signature made do 10 dec 19:23:46 2020 CET gpg: using RSA key 4AEE18F83AFDEB23 gpg: Can't check signature: No public key Author: Someone <email@example.com> Date: Thu Dec 10 19:23:46 2020 +0100
Step 2: download the GPG Public key of GitHub
The highlighted key is referring to the id of the public key of the GitHub web-flow agent that automatically signs commits when you are logged in and make commits from the GitHub UI. As you can see, you do not have the public key from the GitHub signer yet. You can get this key and store it in your GPG public keys like this:
curl https://github.com/web-flow.gpg | gpg --import % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 919 100 919 0 0 3829 0 --:--:-- --:--:-- --:--:-- 3829 gpg: key 4AEE18F83AFDEB23: public key "GitHub (web-flow commit signing) <firstname.lastname@example.org>" imported gpg: Total number processed: 1 gpg: imported: 1
Now the result from git log –show-signature will be something like:
commit d8c7e72cbed11411b80995d415311c7fb6399d9a gpg: Signature made do 10 dec 19:23:46 2020 CET gpg: using RSA key 4AEE18F83AFDEB23 gpg: Good signature from "GitHub (web-flow commit signing) <email@example.com>" [unknown] gpg: WARNING: This key is not certified with a trusted signature! gpg: There is no indication that the signature belongs to the owner. Primary key fingerprint: 5DE3 E050 9C47 EA3C F04A 42D3 4AEE 18F8 3AFD EB23 Author: René Zubcevic <firstname.lastname@example.org> Date: Thu Dec 10 19:23:46 2020 +0100
Step 3: Trust a public key
You can see that you can now verify the signature, but you still do not have trust in the used public key, and therefore have no trust in the signature [unknown] and no one certified this trust. PGP has a web of trust model which is quite extensive. As you have downloaded the GPG public key from a trusted connection, you can safely trust this public key. This can be done by:
gpg --edit-key email@example.com gpg (GnuPG/MacGPG2) 2.2.24; Copyright (C) 2020 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. pub rsa2048/4AEE18F83AFDEB23 created: 2017-08-16 expires: never usage: SC trust: unknown validity: unknown [ unknown] (1). GitHub (web-flow commit signing) <firstname.lastname@example.org> gpg> trust pub rsa2048/4AEE18F83AFDEB23 created: 2017-08-16 expires: never usage: SC trust: unknown validity: unknown [ unknown] (1). GitHub (web-flow commit signing) <email@example.com> Please decide how far you trust this user to correctly verify other users' keys (by looking at passports, checking fingerprints from different sources, etc.) 1 = I don't know or won't say 2 = I do NOT trust 3 = I trust marginally 4 = I trust fully 5 = I trust ultimately m = back to the main menu Your decision? 4 pub rsa2048/4AEE18F83AFDEB23 created: 2017-08-16 expires: never usage: SC trust: full validity: unknown [ unknown] (1). GitHub (web-flow commit signing) <firstname.lastname@example.org> Please note that the shown key validity is not necessarily correct unless you restart the program. gpg> quit
Now [unknown] will have changed to [full] trust, but the message about certification will still be there. In order to get rid of these messages, you have to sign this trusted public key with your own private GPG key which is the process of certification by you. So you also need a private GPG key, which then can be used for signing your own commits in your local repository as well.
Step 4: generate your own private GPG key
For GitHub you should choose RSA and a key strength of 4096. Choose no expiry and it is easy to not use a passphrase as long as you are the only one with access to the key. Then the menu will ask for a realm and an email. Make sure that these match the user.name and user.email you use on your GitHub account.
Step 5: Sign the public GitHub key
With this newly generated key, you can sign the GitHub public key:
gpg --lsign-key email@example.com
Now the git log –show-signature will no longer show warnings about these signatures.
Step 6: Sign your own commits and tags
Now you can sign commits and tags as follows:
gpg --list-secret-keys # get the id of your secret key git config --global user.signingkey 8503774BDD1AB580AC91CFE69716041EBCCEAFF1 # refer git to this key git commit -S -m "updated Readme and content" git tag -s -a v1.0 -m "my v1 version"
Notice that the commit uses a capital S and the tag a lowercase s. If you always want to sign every commit, you can set the config:
git config --global commit.gpgsign true
Step 7: Export and share your public GPG key
Now if you want GitHub to show that your commits can be trusted and if you want GitHub to be used as a means to share your public key, you must extract your public key and enter it in the GitHub section of your profile where you share the public keys.
gpg --armor --export 8503774BDD1AB580AC91CFE69716041EBCCEAFF1
Now upload the output to GitHub. Now anyone, can download and install this public key in the same way we did with the GitHub public key. However, now you will have to use your own GitHub account as part of the url:
#import public key curl https://github.com/zubcevic.gpg | gpg --import
More information and examples can be found here: