commit: 7a507942b669b6a157612e8f7ce3fe362c39b38f
Author: Michał Górny <mgorny <AT> gentoo <DOT> org>
AuthorDate: Thu Feb 1 13:18:28 2018 +0000
Commit: Michał Górny <mgorny <AT> gentoo <DOT> org>
CommitDate: Mon Feb 5 18:43:24 2018 +0000
URL: https://gitweb.gentoo.org/proj/portage.git/commit/?id=7a507942
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 <AT> gentoo.org>
man/portage.5 | 4 +++
pym/portage/sync/modules/git/__init__.py | 3 +-
pym/portage/sync/modules/git/git.py | 48 ++++++++++++++++++++++++++++++--
3 files changed, 52 insertions(+), 3 deletions(-)
diff --git a/man/portage.5 b/man/portage.5
index 54ce7eec9..549c51c73 100644
--- a/man/portage.5
+++ b/man/portage.5
@@ -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
diff --git a/pym/portage/sync/modules/git/__init__.py
b/pym/portage/sync/modules/git/__init__.py
index 2f1d35226..270d97186 100644
--- a/pym/portage/sync/modules/git/__init__.py
+++ b/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.
@@ -58,6 +58,7 @@ module_spec = {
'sync-git-env',
'sync-git-pull-env',
'sync-git-pull-extra-opts',
+ 'sync-git-verify-commit-signature',
),
}
}
diff --git a/pym/portage/sync/modules/git/git.py
b/pym/portage/sync/modules/git/git.py
index 8b4cab273..7e5ddf3b5 100644
--- a/pym/portage/sync/modules/git/git.py
+++ b/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
@@ -7,7 +7,7 @@ import subprocess
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")
@@ -71,6 +71,7 @@ class GitSync(NewBase):
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,
@@ -85,6 +86,8 @@ class GitSync(NewBase):
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)
@@ -125,12 +128,53 @@ class GitSync(NewBase):
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: