From: Daan De Meyer Date: Thu, 1 Aug 2024 10:37:50 +0000 (+0200) Subject: Introduce RepositoryKeyFetch= X-Git-Tag: v25~374^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=refs%2Fpull%2F2931%2Fhead;p=thirdparty%2Fmkosi.git Introduce RepositoryKeyFetch= This setting controls whether we'll fetch GPG keys remotely or not. We disable it by default so that we only rely on locally available GPG keys for checking package and repository metadata signatures. This new setting only affects dnf/zypper based distributions as apt and pacman do not support retrieving GPG keys remotely in the first place. zypper does not trust GPG keys listed in gpgkey= by default so we import local GPG keys manually with rpm to work around that. Fixes #757 --- diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3f71fe4f6..9ee7a9827 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -155,6 +155,14 @@ jobs: QemuKvm=yes EOF + # TODO: Drop once distribution-gpg-keys is in noble-backports. + if [[ "${{ matrix.tools }}" =~ opensuse|fedora|ubuntu ]]; then + tee --append mkosi.local.conf < Co "--repositories", ",".join(config.tools_tree_repositories), "--package-manager-tree", ",".join(str(t) for t in config.tools_tree_package_manager_trees), "--repository-key-check", str(config.repository_key_check), + "--repository-key-fetch", str(config.repository_key_fetch), "--cache-only", str(config.cacheonly), *(["--output-dir", str(config.output_dir)] if config.output_dir else []), *(["--workspace-dir", str(config.workspace_dir)] if config.workspace_dir else []), diff --git a/mkosi/config.py b/mkosi/config.py index 2e17be0b4..96285ee73 100644 --- a/mkosi/config.py +++ b/mkosi/config.py @@ -1402,6 +1402,7 @@ class Config: mirror: Optional[str] local_mirror: Optional[str] repository_key_check: bool + repository_key_fetch: bool repositories: list[str] cacheonly: Cacheonly package_manager_trees: list[ConfigTree] @@ -1961,6 +1962,16 @@ SETTINGS = ( help="Controls signature and key checks on repositories", universal=True, ), + ConfigSetting( + dest="repository_key_fetch", + metavar="BOOL", + nargs="?", + section="Distribution", + default=False, + parse=config_parse_boolean, + help="Controls whether distribution GPG keys can be fetched remotely", + universal=True, + ), ConfigSetting( dest="repositories", metavar="REPOS", @@ -4066,6 +4077,7 @@ def summary(config: Config) -> str: Mirror: {none_to_default(config.mirror)} Local Mirror (build): {none_to_none(config.local_mirror)} Repo Signature/Key check: {yes_no(config.repository_key_check)} + Fetch Repository Keys: {yes_no(config.repository_key_fetch)} Repositories: {line_join_list(config.repositories)} Use Only Package Cache: {config.cacheonly} Package Manager Trees: {line_join_list(config.package_manager_trees)} diff --git a/mkosi/distributions/alma.py b/mkosi/distributions/alma.py index ac6285cac..16345ebb7 100644 --- a/mkosi/distributions/alma.py +++ b/mkosi/distributions/alma.py @@ -16,7 +16,8 @@ class Installer(centos.Installer): find_rpm_gpgkey( context, f"RPM-GPG-KEY-AlmaLinux-{context.config.release}", - ) or f"https://repo.almalinux.org/almalinux/RPM-GPG-KEY-AlmaLinux-{context.config.release}", + f"https://repo.almalinux.org/almalinux/RPM-GPG-KEY-AlmaLinux-{context.config.release}", + ), ) @classmethod diff --git a/mkosi/distributions/centos.py b/mkosi/distributions/centos.py index e6cd985de..e324620bc 100644 --- a/mkosi/distributions/centos.py +++ b/mkosi/distributions/centos.py @@ -100,7 +100,7 @@ class Installer(DistributionInstaller): def gpgurls(context: Context) -> tuple[str, ...]: rel = "RPM-GPG-KEY-CentOS-Official" if context.config.release == "9" else "RPM-GPG-KEY-CentOS-Official-SHA256" return tuple( - find_rpm_gpgkey(context, key) or f"https://www.centos.org/keys/{key}" + find_rpm_gpgkey(context, key, f"https://www.centos.org/keys/{key}") for key in (rel, "RPM-GPG-KEY-CentOS-SIG-Extras") ) @@ -196,7 +196,8 @@ class Installer(DistributionInstaller): find_rpm_gpgkey( context, f"RPM-GPG-KEY-EPEL-{context.config.release}", - ) or f"https://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-{context.config.release}", + f"https://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-{context.config.release}", + ), ) if context.config.local_mirror: @@ -299,7 +300,7 @@ class Installer(DistributionInstaller): ) for sig, components, keys in sigs: - gpgurls = tuple(find_rpm_gpgkey(context, key) or f"https://www.centos.org/keys/{key}" for key in keys) + gpgurls = tuple(find_rpm_gpgkey(context, key, f"https://www.centos.org/keys/{key}") for key in keys) for c in components: if mirror := context.config.mirror: diff --git a/mkosi/distributions/fedora.py b/mkosi/distributions/fedora.py index 551d31905..97008311a 100644 --- a/mkosi/distributions/fedora.py +++ b/mkosi/distributions/fedora.py @@ -20,8 +20,8 @@ from mkosi.util import listify, startswith, tuplify @tuplify def find_fedora_rpm_gpgkeys(context: Context) -> Iterable[str]: - key1 = find_rpm_gpgkey(context, key=f"RPM-GPG-KEY-fedora-{context.config.release}-primary") - key2 = find_rpm_gpgkey(context, key=f"RPM-GPG-KEY-fedora-{context.config.release}-secondary") + key1 = find_rpm_gpgkey(context, key=f"RPM-GPG-KEY-fedora-{context.config.release}-primary", required=False) + key2 = find_rpm_gpgkey(context, key=f"RPM-GPG-KEY-fedora-{context.config.release}-secondary", required=False) if key1: # During branching, there is always a kerfuffle with the key transition. @@ -38,9 +38,15 @@ def find_fedora_rpm_gpgkeys(context: Context) -> Iterable[str]: yield key3 yield key1 + if key2: yield key2 + if not key1 and not key2: + if not context.config.repository_key_fetch: + die("Fedora GPG keys not found in /usr/share/distribution-gpg-keys", + hint="Make sure the distribution-gpg-keys package is installed") + yield "https://fedoraproject.org/fedora.gpg" diff --git a/mkosi/distributions/mageia.py b/mkosi/distributions/mageia.py index c0e3eb2b7..4b817fb51 100644 --- a/mkosi/distributions/mageia.py +++ b/mkosi/distributions/mageia.py @@ -34,7 +34,8 @@ class Installer(fedora.Installer): find_rpm_gpgkey( context, "RPM-GPG-KEY-Mageia", - ) or "https://mirrors.kernel.org/mageia/distrib/$releasever/$basearch/media/core/release/media_info/pubkey", + "https://mirrors.kernel.org/mageia/distrib/$releasever/$basearch/media/core/release/media_info/pubkey", + ), ) if context.config.local_mirror: diff --git a/mkosi/distributions/openmandriva.py b/mkosi/distributions/openmandriva.py index 15a76c8c4..5238864a3 100644 --- a/mkosi/distributions/openmandriva.py +++ b/mkosi/distributions/openmandriva.py @@ -36,7 +36,8 @@ class Installer(fedora.Installer): find_rpm_gpgkey( context, "RPM-GPG-KEY-OpenMandriva", - ) or "https://raw.githubusercontent.com/OpenMandrivaAssociation/openmandriva-repos/master/RPM-GPG-KEY-OpenMandriva", + "https://raw.githubusercontent.com/OpenMandrivaAssociation/openmandriva-repos/master/RPM-GPG-KEY-OpenMandriva", + ), ) if context.config.local_mirror: diff --git a/mkosi/distributions/opensuse.py b/mkosi/distributions/opensuse.py index 382f54eb3..f089a97eb 100644 --- a/mkosi/distributions/opensuse.py +++ b/mkosi/distributions/opensuse.py @@ -94,11 +94,28 @@ class Installer(DistributionInstaller): mirror = context.config.mirror or "https://download.opensuse.org" if context.config.release == "tumbleweed" or context.config.release.isdigit(): - gpgkeys = ( - *([p] if (p := find_rpm_gpgkey(context, key="RPM-GPG-KEY-openSUSE-Tumbleweed")) else []), - *([p] if (p := find_rpm_gpgkey(context, key="RPM-GPG-KEY-openSUSE")) else []), + gpgkeys = tuple( + p + for key in ("RPM-GPG-KEY-openSUSE-Tumbleweed", "RPM-GPG-KEY-openSUSE") + if (p := find_rpm_gpgkey(context, key, required=False)) ) + if not gpgkeys and not context.config.repository_key_fetch: + die("OpenSUSE GPG keys not found in /usr/share/distribution-gpg-keys", + hint="Make sure the distribution-gpg-keys package is installed") + + if zypper and gpgkeys: + run( + ["rpm", "--root=/buildroot", "--import", *(key.removeprefix("file://") for key in gpgkeys)], + sandbox=context.sandbox( + binary="rpm", + mounts=[ + Mount(context.root, "/buildroot"), + *finalize_crypto_mounts(context.config) + ], + ) + ) + if context.config.release == "tumbleweed": if context.config.architecture == Architecture.x86_64: subdir = "" diff --git a/mkosi/distributions/rhel.py b/mkosi/distributions/rhel.py index a70875199..ccc1e4a27 100644 --- a/mkosi/distributions/rhel.py +++ b/mkosi/distributions/rhel.py @@ -24,7 +24,8 @@ class Installer(centos.Installer): find_rpm_gpgkey( context, f"RPM-GPG-KEY-redhat{major}-release", - ) or "https://access.redhat.com/security/data/fd431d51.txt", + "https://access.redhat.com/security/data/fd431d51.txt", + ), ) @staticmethod diff --git a/mkosi/distributions/rhel_ubi.py b/mkosi/distributions/rhel_ubi.py index 78bfc233b..c921ecd1f 100644 --- a/mkosi/distributions/rhel_ubi.py +++ b/mkosi/distributions/rhel_ubi.py @@ -21,7 +21,8 @@ class Installer(centos.Installer): find_rpm_gpgkey( context, f"RPM-GPG-KEY-redhat{major}-release", - ) or "https://access.redhat.com/security/data/fd431d51.txt", + "https://access.redhat.com/security/data/fd431d51.txt", + ), ) @classmethod diff --git a/mkosi/distributions/rocky.py b/mkosi/distributions/rocky.py index af2c4d186..0a2cdfd08 100644 --- a/mkosi/distributions/rocky.py +++ b/mkosi/distributions/rocky.py @@ -16,7 +16,8 @@ class Installer(centos.Installer): find_rpm_gpgkey( context, f"RPM-GPG-KEY-Rocky-{context.config.release}", - ) or f"https://download.rockylinux.org/pub/rocky/RPM-GPG-KEY-Rocky-{context.config.release}", + f"https://download.rockylinux.org/pub/rocky/RPM-GPG-KEY-Rocky-{context.config.release}", + ), ) @classmethod diff --git a/mkosi/installer/rpm.py b/mkosi/installer/rpm.py index 94d07697a..185db91c6 100644 --- a/mkosi/installer/rpm.py +++ b/mkosi/installer/rpm.py @@ -4,9 +4,10 @@ import dataclasses import subprocess import textwrap from pathlib import Path -from typing import Optional +from typing import Literal, Optional, overload from mkosi.context import Context +from mkosi.log import die from mkosi.run import run from mkosi.types import PathString @@ -23,7 +24,33 @@ class RpmRepository: priority: Optional[int] = None -def find_rpm_gpgkey(context: Context, key: str) -> Optional[str]: +@overload +def find_rpm_gpgkey( + context: Context, + key: str, + fallback: Optional[str] = None, + *, + required: Literal[True] = True, +) -> str: ... + + +@overload +def find_rpm_gpgkey( + context: Context, + key: str, + fallback: Optional[str] = None, + *, + required: Literal[False] +) -> Optional[str]: ... + + +def find_rpm_gpgkey( + context: Context, + key: str, + fallback: Optional[str] = None, + *, + required: bool = True +) -> Optional[str]: root = context.config.tools() if context.config.tools_tree_certificates else Path("/") if gpgpath := next((root / "usr/share/distribution-gpg-keys").rglob(key), None): @@ -32,6 +59,13 @@ def find_rpm_gpgkey(context: Context, key: str) -> Optional[str]: if gpgpath := next(Path(context.pkgmngr / "etc/pki/rpm-gpg").rglob(key), None): return (Path("/") / gpgpath.relative_to(context.pkgmngr)).as_uri() + if context.config.repository_key_fetch: + return fallback + + if required: + die(f"{key} GPG key not found in /usr/share/distribution-gpg-keys", + hint="Make sure the distribution-gpg-keys package is installed") + return None diff --git a/mkosi/installer/zypper.py b/mkosi/installer/zypper.py index d58c6a6b8..90ef6ad34 100644 --- a/mkosi/installer/zypper.py +++ b/mkosi/installer/zypper.py @@ -109,9 +109,10 @@ class Zypper(PackageManager): "zypper", "--installroot=/buildroot", "--cache-dir=/var/cache/zypp", - "--gpg-auto-import-keys" if context.config.repository_key_check else "--no-gpg-checks", "--non-interactive", "--no-refresh", + *(["--gpg-auto-import-keys"] if context.config.repository_key_fetch else []), + *(["--no-gpg-checks"] if not context.config.repository_key_check else []), *([f"--plus-content={repo}" for repo in context.config.repositories]), ] diff --git a/mkosi/resources/mkosi.md b/mkosi/resources/mkosi.md index 41ee2b61d..02fdd1fa6 100644 --- a/mkosi/resources/mkosi.md +++ b/mkosi/resources/mkosi.md @@ -452,6 +452,19 @@ boolean argument: either `1`, `yes`, or `true` to enable, or `0`, `no`, Useful to disable checks when combined with `--local-mirror=` and using only a repository from a local filesystem. +`RepositoryKeyFetch=`, `--repository-key-fetch=` +: Controls whether mkosi will fetch distribution GPG keys remotely. Disabled + by default. When disabled, the distribution GPG keys for the target distribution + have to be installed locally on the host system alongside the package manager for + that distribution. + + This setting is only implemented for distributions using dnf or zypper as their + package manager. For other distributions the distribution GPG keys are always looked + up locally regardless of the value of this setting. To make the distribution GPG keys + for distributions available without enabling this setting, the corresponding package + has to be installed on the host. This is usually one of `archlinux-keyring`, + `debian-keyring`, `ubuntu-keyring` or `distribution-gpg-keys` (for rpm-based distributions). + `Repositories=`, `--repositories=` : Enable package repositories that are disabled by default. This can be used to enable the EPEL repos for CentOS or different components of the Debian/Ubuntu repositories. diff --git a/tests/test_json.py b/tests/test_json.py index 43a51f1ab..783790412 100644 --- a/tests/test_json.py +++ b/tests/test_json.py @@ -264,6 +264,7 @@ def test_config() -> None: "RepartOffline": true, "Repositories": [], "RepositoryKeyCheck": false, + "RepositoryKeyFetch": true, "RootPassword": [ "test1234", false @@ -465,6 +466,7 @@ def test_config() -> None: repart_offline=True, repositories=[], repository_key_check=False, + repository_key_fetch=True, root_password=("test1234", False), root_shell="/bin/tcsh", runtime_build_sources=True, diff --git a/tests/test_sysext.py b/tests/test_sysext.py index 6b5cf244e..3d07c8ced 100644 --- a/tests/test_sysext.py +++ b/tests/test_sysext.py @@ -24,6 +24,7 @@ def test_sysext(config: ImageConfig) -> None: options=[ "--directory", "", "--incremental=no", + "--repository-key-fetch=yes", "--base-tree", Path(image.output_dir) / "image", "--overlay", "--package=dnsmasq",