Since git users can config their user.name and user.email freely and do commits, it's possible for John to fake a commit with Bob's name and email, which is not what we want. Is there any way to prevent this? I know in svn we need username and password to commit; is there any equivalent mechanism in Git?
Another way, also based on signed gpg, is to sign the push operation itself (and not a object, like a tag or a commit)
That is what proposes Git 2.2 (November 2014), with Git 2.2 (Q4 2014).
It allows "git push
"(man) request to be signed, so that it can be verified and audited, using the GPG signature of the person who pushed, that the tips of branches at a public repository really point the commits the pusher wanted to, without having to "trust" the server.
See commit 5732373 (05 Sep 2014), commit 0ea47f9 (15 Sep 2014), commit b89363e (21 Aug 2014), commit 9be8916 (22 Aug 2014), commit 4adf569, commit 20a7558 (18 Aug 2014), commit d05b961, commit a50e7ca (14 Aug 2014), commit a85b377 (12 Sep 2014), commit e543b3f, commit d7c6766, commit c67072b (19 Aug 2014), commit b783aa7, commit ab2b0c9, commit 887f353, commit 64de20a, commit 39895c7, commit c09b71c, commit 0e3c339, commit 3bfcb95 (15 Aug 2014), commit 52d2ae5 (04 Sep 2014), and commit e40671a, commit 621b059 (12 Aug 2014) by Junio C Hamano (gitster
).
See commit 6f5ef44 (25 Sep 2014) by Brian Gernhardt (bgernhardt
).
(Merged by Junio C Hamano -- gitster
-- in commit fb06b52, 08 Oct 2014)
push
: the beginning of "git push --signed"
While signed tags and commits assert that the objects thusly signed came from you, who signed these objects, there is not a good way to assert that you wanted to have a particular object at the tip of a particular branch.
My signing v2.0.1 tag only means I want to call the version v2.0.1, and it does not mean I want to push it out to my 'master' branch---it is likely that I only want it in 'maint', so the signature on the object alone is insufficient.The only assurance to you that 'maint' points at what I wanted to place there comes from your trust on the hosting site and my authentication with it, which cannot easily audited later.
Introduce a mechanism that allows you to sign a "push certificate" (for the lack of better name) every time you push, asserting that what object you are pushing to update which ref that used to point at what other object.
Think of it as a cryptographic protection for ref updates, similar to signed tags/commits but working on an orthogonal axis.The basic flow based on this mechanism goes like this:
- You push out your work with "
git push
"(man) --signed".- The sending side learns where the remote refs are as usual, together with what protocol extension the receiving end supports.
If the receiving end does not advertise the protocol extension "push-cert", an attempt to "git push --signed
"(man) fails.Otherwise, a text file, that looks like the following, is prepared in core:
certificate version 0.1 pusher Junio C Hamano <[email protected]> 1315427886 -0700 7339ca65... 21580ecb... refs/heads/master 3793ac56... 12850bec... refs/heads/next
The file begins with a few header lines, which may grow as we gain more experience.
The 'pusher
' header records the name of the signer (the value of user.signingkey configuration variable, falling back toGIT_COMMITTER_{NAME|EMAIL}
) and the time of the certificate generation.
After the header, a blank line follows, followed by a copy of the protocol message lines.Each line shows the old and the new object name at the tip of the ref this push tries to update, in the way identical to how the underlying "git push" protocol exchange tells the ref updates to the receiving end (by recording the "old" object name, the push certificate also protects against replaying).
It is expected that new command packet types other than the old-new-refname kind will be included in push certificate in the same way as would appear in the plain vanilla command packets in unsigned pushes.
The user then is asked to sign this push certificate using GPG, formatted in a way similar to how signed tag objects are signed, and the result is sent to the other side (i.e. receive-pack).
In the protocol exchange, this step comes immediately before the sender tells what the result of the push should be, which in turn comes before it sends the pack data.
- When the receiving end sees a push certificate, the certificate is written out as a blob.
The pre-receive hook can learn about the certificate by checkingGIT_PUSH_CERT
environment variable, which, if present, tells the object name of this blob, and make the decision to allow or reject this push.
Additionally, the post-receive hook can also look at the certificate, which may be a good place to log all the received certificates for later audits.Because a push certificate carry the same information as the usual command packets in the protocol exchange, we can omit the latter when a push certificate is in use and reduce the protocol overhead.
This however is not included in this patch to make it easier to review (in other words, the series at this step should never be released without the remainder of the series, as it implements an interim protocol that will be incompatible with the final one).
As such, the documentation update for the protocol is left out of this step.
config
now includes in its man page:
receive.acceptpushcert
By default,
git receive-pack
will advertise that it acceptsgit push --signed
.
Setting this variable to false disables it (this is a tentative variable that will go away at the end of this series).
git push
now includes in its man page:
[--repo=] [-f | --force] [--prune] [-v | --verbose] [-u | --set-upstream] [--signed]
git push
now includes in its man page:
--signed
GPG-sign the push request to update refs on the receiving side, to allow it to be checked by the hooks and/or be logged. See
git receive-pack
for the details on the receiving end.
git receive-pack
now includes in its man page:
When accepting a signed push (see
git push
), the signed push certificate is stored in a blob and an environment variableGIT_PUSH_CERT
can be consulted for its object name.
See the description ofpost-receive
hook for an example.
git receive-pack
now includes in its man page:
The
GIT_PUSH_CERT
environment variable can be inspected, just as inpre-receive
hook, after accepting a signed push.
git receive-pack
now includes in its man page:
ref listing the commits pushed to the repository, and logs the push certificates of signed pushes to a logger service:
git receive-pack
now includes in its man page:
log signed push certificate, if any
if test -n "${GIT_PUSH_CERT-}" then ( git cat-file blob ${GIT_PUSH_CERT} ) | mail -s "push certificate" push-log@mydomain fi
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With