Recently for a project with tight regulatory requirements we decided that git signing throughout the project was a good idea. There’s a debate about it’s level of effectiveness, given that all it tells you is that a particular commit was made from a particular developers machine, and if they’re not careful, they can end up committing code they didn’t actually write (especially in larger commits). Most of the time this other code is cruft from build systems, but it could potentially also include malicious items. On the other hand, this does mean you can reduce the ability of external actors to add commits if they compromise the revision control system, and at least conclude that all the affected code came from a specific developer X if you have a single malicious actor.
These are useful features from a defense-in-depth approach to this problem, especially given that the standard git author/email stuff is trivially spoofable, but we all tend to trust it by default. There’s some existing work out there regarding signature verification e.g. just “git –verify-commit“, and Github now supports signature verification inline, but really we need to automatically check your entire git tree and fail CI if someone’s forgotten to sign things.
We’re releasing Clincher which is a tool we built to do this. Mostly it’s a wrapper around “git –verify-commit”, but it also does a number of other tasks:
- If you have an unsigned merge commit (e.g. if it was done by clicking “merge pull request” in Bitbucket, as Github signs its merges), then it also checks that the merge was a clean merge i.e. the exact same result as you would have gotten by locally merging and so can be regarded as a safe action assuming all the other commits it’s built from are signed
- If there’s a commit that isn’t signed (e.g you only enforced code signing part way through), then provide a file equivalent to the unsigned commit, which can itself be signed as a replacement for the missing signed commit without having to rewrite all your revision history
- Lets you only check part of the commit tree e.g. for CI purposes, we find checking “HEAD…master” is useful, as it will only cause problems if the commits on your branch are unsigned as opposed to any commits on other unmerged branches. This is also useful if you’ve got a historical part of the revision history that hasn’t yet been fully signed that you need to ignore for the moment.
- Checks that expired keys were valid at the time of signing (although this timestamp could be spoofed by a malicious committer)
At the moment it assumes that the git repo is itself a good source of keys (which leads to the interesting pattern of a new developers first commit often being them committing their own key), and expanding this to a web of trust model, either with external keyservers or by enforcing some sort of history mechanism where early keys are implicitly trusted somehow and then later keys must be committed by existingly trusted committers to be trusted is a possible area of future expansion.
For the moment though, it’s a pretty good mechanism for checking signing, and certainly an improvement over repeatedly eyeballing “git log –show-signature”