aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichał Górny <mgorny@gentoo.org>2018-02-01 14:18:28 +0100
committerMichał Górny <mgorny@gentoo.org>2018-02-05 19:43:24 +0100
commit7a507942b669b6a157612e8f7ce3fe362c39b38f (patch)
tree3839bf58da62bb6a9e2f158a5717f1805bebd54c
parentrsync: Issue an explicit warning if Manifest timestamp is >24hr old (diff)
downloadportage-7a507942.tar.gz
portage-7a507942.tar.bz2
portage-7a507942.zip
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>
-rw-r--r--man/portage.54
-rw-r--r--pym/portage/sync/modules/git/__init__.py3
-rw-r--r--pym/portage/sync/modules/git/git.py48
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: