commit: 4ccea757a4a7f7153fbc1769a9019978d1a9fafc Author: Sam James <sam <AT> gentoo <DOT> org> AuthorDate: Sat Jan 3 04:46:51 2026 +0000 Commit: Andreas K. Hüttel <dilfridge <AT> gentoo <DOT> org> CommitDate: Tue Jan 6 15:31:29 2026 +0000 URL: https://gitweb.gentoo.org/proj/catalyst.git/commit/?id=4ccea757
Implement OpenPGP verification for git repos Introduces `repo_openpgp_key_path` for config which defaults to `/usr/share/openpgp-keys/gentoo-release.asc` which can verify the stable repo. Uses app-portage/gemato. Fail noisily if gemato isn't installed and repo_openpgp_key_path is either set or is the default, as we don't want people to accidentally have an unverified repo. They can set it to blank instead if they wish. Bug: https://bugs.gentoo.org/966073 Signed-off-by: Sam James <sam <AT> gentoo.org> Signed-off-by: Andreas K. Hüttel <dilfridge <AT> gentoo.org> catalyst/defaults.py | 2 ++ catalyst/targets/snapshot.py | 36 ++++++++++++++++++++++++++++++++++++ doc/catalyst-config.5.txt | 4 ++++ 3 files changed, 42 insertions(+) diff --git a/catalyst/defaults.py b/catalyst/defaults.py index f4d48fef..f47fcf7a 100644 --- a/catalyst/defaults.py +++ b/catalyst/defaults.py @@ -21,6 +21,7 @@ valid_config_file_values = frozenset([ "options", "port_logdir", "repo_basedir", + "repo_openpgp_key_path", "repo_name", "repos_storedir", "sharedir", @@ -51,6 +52,7 @@ confdefaults = { "pkgdir": "/var/cache/binpkgs", "port_tmpdir": "/var/tmp/portage", "repo_basedir": "/var/db/repos", + "repo_openpgp_key_path": "/usr/share/openpgp-keys/gentoo-release.asc", "repo_name": "gentoo", "repos_storedir": "%(storedir)s/repos", "sharedir": "/usr/share/catalyst", diff --git a/catalyst/targets/snapshot.py b/catalyst/targets/snapshot.py index ef68765d..8d4145fd 100644 --- a/catalyst/targets/snapshot.py +++ b/catalyst/targets/snapshot.py @@ -55,12 +55,45 @@ class snapshot(TargetBase): repouri, self.gitdir], ] + + env = os.environ.copy() + pgp_keyring = self.settings["repo_openpgp_key_path"] + if pgp_keyring: + try: + import gemato.openpgp + except ImportError as e: + raise CatalystError( + f"gemato could not be imported but repo_openpgp_key_path was non-empty." + ) + + pgp_path = Path(pgp_keyring) + if not pgp_path.exists() or not pgp_path.is_file(): + raise CatalystError( + f"OpenPGP keyring at repo_openpgp_key_path={pgp_path} does not exist. Is sec-keys/openpgp-keys-gentoo-release installed?" + ) + + git_cmds.append(self.git, '-C', self.gitdir, 'verify-commit', 'HEAD') + + openpgp_env = gemato.openpgp.OpenPGPEnvironment + try: + with open(pgp_path, "rb") as f: + openpgp_env.import_key(f) + openpgp_env.refresh_keys() + except (GematoException, asyncio.TimeoutError) as e: + raise CatalystError( + f"OpenPGP verification via gemato failed: {e}" + ) + openpgp_env.close() + + env["GNUPGHOME"] = openpgp_env.home + try: for cmd in git_cmds: log.notice('>>> ' + ' '.join(cmd)) subprocess.run(cmd, capture_output=True, check=True, + env=env, encoding='utf-8', close_fds=False) @@ -75,6 +108,9 @@ class snapshot(TargetBase): raise CatalystError(f'{e.cmd} failed with return code' f'{e.returncode}\n' f'{e.output}\n') from e + finally: + if pgp_keyring: + openpgp_env.close() def run(self): if self.settings['snapshot_treeish'] == 'stable': diff --git a/doc/catalyst-config.5.txt b/doc/catalyst-config.5.txt index ca9335d6..7dd44cd8 100644 --- a/doc/catalyst-config.5.txt +++ b/doc/catalyst-config.5.txt @@ -124,6 +124,10 @@ The name of the main repository (e.g. gentoo). The git repository at `${repos_storedir}/${repo_name}.git` will be used to produce the portdir sqfs snapshot. +*repo_openpgp_key_path*:: +OpenPGP keyring for verifying commits in the git repository. Defaults to +`/usr/share/openpgp-keys/gentoo-release.asc`. + *target_distdir*:: Defines the location of the local source file repository in the target. This will be written to the target's make.conf if it is not
