Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Any way to guarantee that a git user doesn't use fake account info when commiting and pushing?

Tags:

git

security

svn

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?

like image 612
adamsmith Avatar asked Nov 05 '22 09:11

adamsmith


1 Answers

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:

  1. You push out your work with "git push"(man) --signed".
  2. 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 to GIT_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.

  1. 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 checking GIT_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 accepts git 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 variable GIT_PUSH_CERT can be consulted for its object name.
See the description of post-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 in pre-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
like image 116
VonC Avatar answered Nov 11 '22 12:11

VonC