Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
git: Support verifying commit signature post-sync
Add a new sync-git-verify-commit-signature option (defaulting to false)
that verifies the top commit signature after syncing. The verification
is currently done using built-in git routines.

The verification passes if the signature is good or untrusted.
In the latter case, a warning is printed. In any other case,
the verification causes sync to fail and an appropriate error is output.

Reviewed-by: Zac Medico <zmedico@gentoo.org>
  • Loading branch information
mgorny committed Feb 5, 2018
1 parent 8d99acd commit 7a50794
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 3 deletions.
4 changes: 4 additions & 0 deletions man/portage.5
Expand Up @@ -1007,6 +1007,10 @@ See also example for sync-git-clone-env.
.B sync\-git\-pull\-extra\-opts
Extra options to give to git when updating repository (git pull).
.TP
.B sync\-git\-verify\-commit\-signature = true|false
Require the top commit in the repository to contain a good OpenPGP
signature. Defaults to false.
.TP
.B sync\-hooks\-only\-on\-change
If set to true, then sync of a given repository will not trigger postsync
hooks unless hooks would have executed for a master repository or the
Expand Down
3 changes: 2 additions & 1 deletion pym/portage/sync/modules/git/__init__.py
@@ -1,4 +1,4 @@
# Copyright 2014-2017 Gentoo Foundation
# Copyright 2014-2018 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2

doc = """Git plug-in module for portage.
Expand Down Expand Up @@ -58,6 +58,7 @@ def _check_depth(self, attr):
'sync-git-env',
'sync-git-pull-env',
'sync-git-pull-extra-opts',
'sync-git-verify-commit-signature',
),
}
}
Expand Down
48 changes: 46 additions & 2 deletions pym/portage/sync/modules/git/git.py
@@ -1,4 +1,4 @@
# Copyright 2005-2017 Gentoo Foundation
# Copyright 2005-2018 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2

import logging
Expand All @@ -7,7 +7,7 @@
import portage
from portage import os
from portage.util import writemsg_level, shlex_split
from portage.output import create_color_func
from portage.output import create_color_func, EOutput
good = create_color_func("GOOD")
bad = create_color_func("BAD")
warn = create_color_func("WARN")
Expand Down Expand Up @@ -71,6 +71,7 @@ def new(self, **kwargs):
else:
# default
git_cmd_opts += " --depth 1"

if self.repo.module_specific_options.get('sync-git-clone-extra-opts'):
git_cmd_opts += " %s" % self.repo.module_specific_options['sync-git-clone-extra-opts']
git_cmd = "%s clone%s %s ." % (self.bin_command, git_cmd_opts,
Expand All @@ -85,6 +86,8 @@ def new(self, **kwargs):
self.logger(self.xterm_titles, msg)
writemsg_level(msg + "\n", level=logging.ERROR, noiselevel=-1)
return (exitcode, False)
if not self.verify_head():
return (1, False)
return (os.EX_OK, True)


Expand Down Expand Up @@ -125,12 +128,53 @@ def update(self):
self.logger(self.xterm_titles, msg)
writemsg_level(msg + "\n", level=logging.ERROR, noiselevel=-1)
return (exitcode, False)
if not self.verify_head():
return (1, False)

current_rev = subprocess.check_output(rev_cmd,
cwd=portage._unicode_encode(self.repo.location))

return (os.EX_OK, current_rev != previous_rev)

def verify_head(self):
if (self.repo.module_specific_options.get(
'sync-git-verify-commit-signature', 'false') != 'true'):
return True

rev_cmd = [self.bin_command, "log", "--pretty=format:%G?", "-1"]
try:
status = (portage._unicode_decode(
subprocess.check_output(rev_cmd,
cwd=portage._unicode_encode(self.repo.location)))
.strip())
except subprocess.CalledProcessError:
return False

out = EOutput()
if status == 'G': # good signature is good
out.einfo('Trusted signature found on top commit')
return True
elif status == 'U': # untrusted
out.ewarn('Top commit signature is valid but not trusted')
return True
else:
if status == 'B':
expl = 'bad signature'
elif status == 'X':
expl = 'expired signature'
elif status == 'Y':
expl = 'expired key'
elif status == 'R':
expl = 'revoked key'
elif status == 'E':
expl = 'unable to verify signature (missing key?)'
elif status == 'N':
expl = 'no signature'
else:
expl = 'unknown issue'
out.eerror('No valid signature found: %s' % (expl,))
return False

def retrieve_head(self, **kwargs):
'''Get information about the head commit'''
if kwargs:
Expand Down

0 comments on commit 7a50794

Please sign in to comment.