Gentoo Websites Logo
Go to: Gentoo Home Documentation Forums Lists Bugs Planet Store Wiki Get Gentoo!
Bug 502060 - git update hook to validate gpg signatures on commits
Summary: git update hook to validate gpg signatures on commits
Status: RESOLVED FIXED
Alias: None
Product: Gentoo Infrastructure
Classification: Unclassified
Component: Git (show other bugs)
Hardware: All Linux
: High normal with 3 votes (vote)
Assignee: Gentoo Infrastructure
URL:
Whiteboard:
Keywords:
Depends on:
Blocks: 333531 502062
  Show dependency tree
 
Reported: 2014-02-22 00:05 UTC by Robin Johnson
Modified: 2021-08-25 21:27 UTC (History)
16 users (show)

See Also:
Package list:
Runtime testing required: ---


Attachments
My update hook (update,2.36 KB, text/plain)
2014-09-14 08:29 UTC, Michał Górny
Details
update hook, v2 (update,2.35 KB, text/plain)
2014-09-14 13:02 UTC, Michał Górny
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Robin Johnson archtester Gentoo Infrastructure gentoo-dev Security 2014-02-22 00:05:27 UTC
This bug was split from bug 333685.

On the server side, we need to have an update hook that validates GPG signatures on commits, and denies the push if any new commit is unsigned.

Option 1: global keyring
Summary: There is a single Gentoo dev keyring that all new commit signatures must be validated against.

Option 2: per-user keyring enforcement
Summary: Gitolite will provide the username in the environment, and that will be used to pull which keys the commit should be used with.
Side-effects:
- you cannot push for another key without re-signing their commit (you're likely to need to rebase and merge anyway)
Comment 1 Robin Johnson archtester Gentoo Infrastructure gentoo-dev Security 2014-02-22 01:12:41 UTC
See bug 502062 as well, there it was found that the gitolite update hook already does almost everything we need.
Comment 2 Robin Johnson archtester Gentoo Infrastructure gentoo-dev Security 2014-02-22 01:55:54 UTC
Relatedly, on the Git front, I see it got easier to do signing in Git, and these will definitely help in the gentoo front, as if you always do pull/rebase/am with --gpg-sign, then you'll be good to push with less resigning.

http://thread.gmane.org/gmane.comp.version-control.git/242412

* bc/gpg-sign-everywhere (2014-02-11) 9 commits
  (merged to 'next' on 2014-02-13 at 390376c)
 + pull: add the --gpg-sign option.
 + rebase: add the --gpg-sign option
 + rebase: parse options in stuck-long mode
 + rebase: don't try to match -M option
 + rebase: remove useless arguments check
 + am: add the --gpg-sign option
 + am: parse options in stuck-long mode
 + git-sh-setup.sh: add variable to use the stuck-long mode
 + cherry-pick, revert: add the --gpg-sign option

 Teach "--gpg-sign" option to many commands that create commits.

 Changes to some scripted Porcelains use unsafe variable
 substitutions and still need to be tightened.

 Will cook in 'next'.

* nv/commit-gpgsign-config (2013-12-17) 3 commits
  (merged to 'next' on 2014-01-03 at 9780cbb)
 + test the commit.gpgsign config option
 + commit-tree: add and document --no-gpg-sign
 + Add the commit.gpgsign option to sign all commits

 Introduce commit.gpgsign configuration variable to force every
 commit to be GPG signed.  The variable cannot be overriden from the
 command line of some of the commands that create commits except for
 "git commit" and "git commit-tree", but I am not convinced that it
 is a good idea to sprinkle support for --no-gpg-sign everywhere.

 Will cook in 'next'.
Comment 3 Alex Xu (Hello71) 2014-02-22 22:55:10 UTC
I swear I posted this somewhere.

http://mikegerwitz.com/papers/git-horror-story has a use case for, explanation of, and implementation of required git commit signing.

It was "Last updated 2013-11-10 00:16:01 EST". I have not verified whether the information is still correct, but it should be reasonably so.
Comment 4 Robin Johnson archtester Gentoo Infrastructure gentoo-dev Security 2014-02-22 23:32:02 UTC
(In reply to Alex Xu (Hello71) from comment #3)
> I swear I posted this somewhere.
> 
> http://mikegerwitz.com/papers/git-horror-story has a use case for,
> explanation of, and implementation of required git commit signing.
> 
> It was "Last updated 2013-11-10 00:16:01 EST". I have not verified whether
> the information is still correct, but it should be reasonably so.
What was the point of posting that link? We KNOW it's required.

We just need to write the update.secondary hook and deploy it for the repo, per:
http://stackoverflow.com/questions/13870607/using-gitolite-vrefs-for-update-hook
http://gitolite.com/gitolite/dev-notes.html#update-hook

Here's somebody else's regular update hook that checks GPG signatures and enforces some other policies (eg merge-only master):
https://github.com/isislovecruft/scripts/blob/master/check-commit-signature
Comment 5 Alex Xu (Hello71) 2014-02-22 23:41:59 UTC
(In reply to Robin Johnson from comment #4)
> (In reply to Alex Xu (Hello71) from comment #3)
> > I swear I posted this somewhere.
> > 
> > http://mikegerwitz.com/papers/git-horror-story has a use case for,
> > explanation of, and implementation of required git commit signing.
> > 
> > It was "Last updated 2013-11-10 00:16:01 EST". I have not verified whether
> > the information is still correct, but it should be reasonably so.
> What was the point of posting that link? We KNOW it's required.
> 
> We just need to write the update.secondary hook and deploy it for the repo,
> per:
> http://stackoverflow.com/questions/13870607/using-gitolite-vrefs-for-update-
> hook
> http://gitolite.com/gitolite/dev-notes.html#update-hook
> 
> Here's somebody else's regular update hook that checks GPG signatures and
> enforces some other policies (eg merge-only master):
> https://github.com/isislovecruft/scripts/blob/master/check-commit-signature

I was pointing out a resource where an example script might be found.

In hindsight, such resources are likely readily accessible on the internet.
Comment 6 Brian Harring (RETIRED) gentoo-dev 2014-02-23 07:37:58 UTC
@Robin: I've been operating under the assumption option #2 was the intent; resigning the commit being the thought.

In hindsight, that may be academically pure, but less friendly to actual workflows.  I don't really like the notion of you pushing some commit I shared into the tree on my behalf, and having the history look like I did it- my view, the pusher is the one truly making the change, thus the signing has to be theirs (they own the fallout).

Comments/thoughts?
Comment 7 Alex Xu (Hello71) 2014-02-23 12:59:56 UTC
(In reply to Brian Harring from comment #6)
> @Robin: I've been operating under the assumption option #2 was the intent;
> resigning the commit being the thought.
> 
> In hindsight, that may be academically pure, but less friendly to actual
> workflows.  I don't really like the notion of you pushing some commit I
> shared into the tree on my behalf, and having the history look like I did
> it- my view, the pusher is the one truly making the change, thus the signing
> has to be theirs (they own the fallout).
> 
> Comments/thoughts?

I think we should use option 1 along with logging the pusher in git notes (bug 502062).
Comment 8 Robin Johnson archtester Gentoo Infrastructure gentoo-dev Security 2014-02-23 20:04:30 UTC
(In reply to Alex Xu (Hello71) from comment #7)
> (In reply to Brian Harring from comment #6)
> > @Robin: I've been operating under the assumption option #2 was the intent;
> > resigning the commit being the thought.
Both of you are delving into workflows, which is a separate issue than actual validation, and I'd like to keep separate from this bug. I'm going to start a thread on -dev to discuss it, because I've been wondering about merge-only master, plus branches for each dev & team (or even multiple branches if a dev wanted them).

> > In hindsight, that may be academically pure, but less friendly to actual
> > workflows.  I don't really like the notion of you pushing some commit I
> > shared into the tree on my behalf, and having the history look like I did
> > it- my view, the pusher is the one truly making the change, thus the signing
> > has to be theirs (they own the fallout).
> > Comments/thoughts?
The case of pusher != committer will be very rare. It comes down to who is creating the merge commit? 

Here is a scenario (host, repo, branch, commits):
gentoo:gentoo-x86:origin/master:A
localhost:gentoo-x86:dev/robbat2:A-B-Z
github:gentoo-x86:dev/ferringb:A-B-C

I find that commit C is useful, and does fix correctly. It is also already signed by your developer key, but NOT yet pushed to gentoo.

I pull it to my tree, explicitly signing the merge commit D. My tree now looks like:
localhost:gentoo-x86:dev/robbat2:A-B-Z-C-D

If I want to push that as is, the GPG validation must accept signatures from all devs.
If I must re-sign C, then it the tree is actually A-B-Z-C'-D, and what happens when ferringb tries to pull that sequence into his repo?


> I think we should use option 1 along with logging the pusher in git notes
> (bug 502062).
I'm actually more in favour of #3 in his option, but again, as I say, this bug is about the actual signing, not the policies.
Comment 9 Alex Xu (Hello71) 2014-06-24 23:00:24 UTC
(In reply to Robin Johnson from comment #8)
> (In reply to Alex Xu (Hello71) from comment #7)
> > (In reply to Brian Harring from comment #6)
> > > @Robin: I've been operating under the assumption option #2 was the intent;
> > > resigning the commit being the thought.
> Both of you are delving into workflows, which is a separate issue than
> actual validation, and I'd like to keep separate from this bug. I'm going to
> start a thread on -dev to discuss it, because I've been wondering about
> merge-only master, plus branches for each dev & team (or even multiple
> branches if a dev wanted them).

it's possible I'm just dense, but I don't think you ever started that thread. how does funtoo do it?

> > > In hindsight, that may be academically pure, but less friendly to actual
> > > workflows.  I don't really like the notion of you pushing some commit I
> > > shared into the tree on my behalf, and having the history look like I did
> > > it- my view, the pusher is the one truly making the change, thus the signing
> > > has to be theirs (they own the fallout).
> > > Comments/thoughts?
> The case of pusher != committer will be very rare. It comes down to who is
> creating the merge commit? 
> 
> Here is a scenario (host, repo, branch, commits):
> gentoo:gentoo-x86:origin/master:A
> localhost:gentoo-x86:dev/robbat2:A-B-Z
> github:gentoo-x86:dev/ferringb:A-B-C
> 
> I find that commit C is useful, and does fix correctly. It is also already
> signed by your developer key, but NOT yet pushed to gentoo.
> 
> I pull it to my tree, explicitly signing the merge commit D. My tree now
> looks like:
> localhost:gentoo-x86:dev/robbat2:A-B-Z-C-D
> 
> If I want to push that as is, the GPG validation must accept signatures from
> all devs.

yes, that's how it must be to keep hashes equal.

> If I must re-sign C, then it the tree is actually A-B-Z-C'-D, and what
> happens when ferringb tries to pull that sequence into his repo?

don't re-sign.

> > I think we should use option 1 along with logging the pusher in git notes
> > (bug 502062).
> I'm actually more in favour of #3 in his option, but again, as I say, this
> bug is about the actual signing, not the policies.

i'm pretty sure we're going around in circles confusing the Options 1 and 2 in description and my link.
Comment 10 Michał Górny archtester Gentoo Infrastructure gentoo-dev Security 2014-09-14 08:29:57 UTC
Created attachment 384710 [details]
My update hook

This is how I see it. I've tested it a bit, and seems to refuse correctly what needs to be refused.

Specifically:

1. disallows creating tags in the repo,

2. in 'master', disallows:

  a. forced updates,

  b. mistakenly committed ChangeLogs,

  c. first-line commits with no, invalid or untrusted signatures,

3. for other (developer/project) branches, everything is allowed -- we don't give any support for them.

This assumes that GPG verification infra is in place, i.e. something else fetches, trusts and revokes keys as necessary.
Comment 11 Michał Górny archtester Gentoo Infrastructure gentoo-dev Security 2014-09-14 13:02:50 UTC
Created attachment 384726 [details]
update hook, v2

Small fix: I meant git rev-list "${new_rev}" "^${old_rev}" for the commits-to-check-for-signature.
Comment 12 Robin Johnson archtester Gentoo Infrastructure gentoo-dev Security 2014-09-14 18:31:12 UTC
(In reply to Michał Górny from comment #10)
> Created attachment 384710 [details]
...
> This assumes that GPG verification infra is in place, i.e. something else
> fetches, trusts and revokes keys as necessary.
I don't think this catches some of the things that the other validation script does:
https://github.com/isislovecruft/scripts/blob/master/check-commit-signature

Also, we can  have multiple scripts, and I'm wondering if it would be best to seperate the checks in them:
1. If master or other trust-required branch: Validate signature (probably using a modified check-commit-signature, because we need to check against a list of allowed keys)
1.1. Since we are allowing unsigned branches, we must validate entire branches on merge, ALL commits therein need to be signed (otherwise we are open to the same substitution attack).
2. Disallow forced-update
3. Changelog/QA stuff
4. Existing checks for commits, like metadata.xml
Comment 13 Julian Ospald 2014-09-14 19:09:33 UTC
(In reply to Robin Johnson from comment #12)
> (In reply to Michał Górny from comment #10)
> > Created attachment 384710 [details]
> ...
> > This assumes that GPG verification infra is in place, i.e. something else
> > fetches, trusts and revokes keys as necessary.
> I don't think this catches some of the things that the other validation
> script does:
> https://github.com/isislovecruft/scripts/blob/master/check-commit-signature
> 

The only differences in signature validation I see are:
* collaborators fingerprints are hardcoded, which I don't think makes sense for our use case. We are not randomly pulling gpg keys from servers, are we?
* uses fragile grep/cut methods to extract the key id (--pretty=format:'%GK' prints the long key id here, so what's the problem?)
* calls gpg manually instead of using --pretty=format:'%G?', because the fingerprints are hardcoded.

So that's a lot of additional code that's unnecessary if we don't hardcode fingerprints and rely on git to verify the signatures.
Comment 14 Julian Ospald 2014-09-14 19:20:23 UTC
(In reply to Robin Johnson from comment #12)
> 1.1. Since we are allowing unsigned branches, we must validate entire
> branches on merge, ALL commits therein need to be signed (otherwise we are
> open to the same substitution attack).

I could be misunderstanding you, but I don't see a reason to sign or cryptographyically valiate branch 'B' of a merge. The only important point is the merge commit which indicates that branch 'B' has been reviewed by the developer.

The posted script skips those branches and will only check the signatures of branch 'A' which also holds the merge commit.

How would you be able to exploit this fact, unless you find a way to
* inject code into branch 'B' without changing any of the SHA1 ids
* somehow force push that to origin (which is not possible)
Comment 15 Michał Górny archtester Gentoo Infrastructure gentoo-dev Security 2014-09-14 19:44:23 UTC
(In reply to Robin Johnson from comment #12)
> (In reply to Michał Górny from comment #10)
> > Created attachment 384710 [details]
> ...
> > This assumes that GPG verification infra is in place, i.e. something else
> > fetches, trusts and revokes keys as necessary.
> I don't think this catches some of the things that the other validation
> script does:
> https://github.com/isislovecruft/scripts/blob/master/check-commit-signature

Not really. The difference is that mine reuses GPG features, while the other reinvents the wheel, poorly.

> Also, we can  have multiple scripts, and I'm wondering if it would be best
> to seperate the checks in them:
> 1. If master or other trust-required branch: Validate signature (probably
> using a modified check-commit-signature, because we need to check against a
> list of allowed keys)
> 1.1. Since we are allowing unsigned branches, we must validate entire
> branches on merge, ALL commits therein need to be signed (otherwise we are
> open to the same substitution attack).
> 2. Disallow forced-update
> 3. Changelog/QA stuff
> 4. Existing checks for commits, like metadata.xml

It may be feasible to do so. However, I would start with putting everything into one hook first and seeing how splittable it ends up.
Comment 16 Robin Johnson archtester Gentoo Infrastructure gentoo-dev Security 2014-09-14 20:55:21 UTC
(In reply to Julian Ospald (hasufell) from comment #14)
> (In reply to Robin Johnson from comment #12)
> > 1.1. Since we are allowing unsigned branches, we must validate entire
> > branches on merge, ALL commits therein need to be signed (otherwise we are
> > open to the same substitution attack).
> 
> I could be misunderstanding you, but I don't see a reason to sign or
> cryptographyically valiate branch 'B' of a merge. The only important point
> is the merge commit which indicates that branch 'B' has been reviewed by the
> developer.
> 
> The posted script skips those branches and will only check the signatures of
> branch 'A' which also holds the merge commit.
> 
> How would you be able to exploit this fact, unless you find a way to
> * inject code into branch 'B' without changing any of the SHA1 ids
I'm concerned about the existing collision and and chosen-prefix attacks against SHA1 being used here, just like the Flame malware successfully used a chosen-prefix against MD5 in a code-signing cert.

Here's a chosen-prefix attack against our repo:
1. Attacker constructs a init.d script, regular part at the start, malicious part at the end
1.1. This would be fairly simple, just construct two start() functions, one of which is mundane, the other is malicious.
1.2. Both variants of the script have the same SHA1...
2. Attacker gets the the script committed, unmodified, by a developer to that dev's branch, with copy A that is entirely innocuous.
2.1. Let this script be stored as blob B, referenced by H(B) = ...sha1-id...
2.2. The developer is lazy and doesn't sign this commit, because it's on their own branch.
3. Dev merges to the master branch, with only the merge commit signed; not the prior commit.
4. Attacker breaks into read-only git mirror, and replaces the mundane variant of blob in the git pack with a malicious variant.
5. All new clones/pulls that fetch that blob will contain the exploit.
6. Profit.

Counter-measures:
a. Require commits to ALL branches to be signed by a dev.
b. Require ALL commits on the master branch to be signed by a dev.
Comment 17 Michał Górny archtester Gentoo Infrastructure gentoo-dev Security 2014-09-14 21:07:45 UTC
Well, I'm not a crypto specialist but if the blob containing the file has the same hash, the commit doesn't change. And if the commit doesn't change, the signature is still valid.
Comment 18 Robin Johnson archtester Gentoo Infrastructure gentoo-dev Security 2014-09-14 21:18:47 UTC
(In reply to Michał Górny from comment #17)
> Well, I'm not a crypto specialist but if the blob containing the file has
> the same hash, the commit doesn't change. And if the commit doesn't change,
> the signature is still valid.
The Git commit-signing design explicitly signs the entire commit, including blob contents, to avoid this security problem.

The catch is that merge commits only have references and a changelog, not any file content.
Comment 19 W. Trevor King 2014-09-14 22:34:40 UTC
(In reply to Robin Johnson from comment #18)
> The Git commit-signing design explicitly signs the entire commit, including
> blob contents, to avoid this security problem.

The Git gommit-signing design explicitly signs the commit, which contains the tree SHA1.  It does not re-hash all of the subtrees and blobs underneath that tree [1].  However, constructing both a useful blob *and* a malicious blob that share the same SHA1 is a lot harder than just coming up with some gibberish that shares a SHA1 with a good blob [2].

[1]: http://mid.gmane.org/robbat2-20120603T073705-606889647Z@orbis-terrarum.net
[2]: http://thread.gmane.org/gmane.comp.version-control.git/210320
Comment 20 Alec Warner (RETIRED) archtester gentoo-dev Security 2021-08-25 21:27:17 UTC
We have signed commits now and sigs are required on most repos.