]> git.ipfire.org Git - thirdparty/mkosi.git/commitdiff
Introduce RepositoryKeyFetch= 2931/head
authorDaan De Meyer <daan.j.demeyer@gmail.com>
Thu, 1 Aug 2024 10:37:50 +0000 (12:37 +0200)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Fri, 2 Aug 2024 07:32:04 +0000 (09:32 +0200)
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

17 files changed:
.github/workflows/ci.yml
mkosi/__init__.py
mkosi/config.py
mkosi/distributions/alma.py
mkosi/distributions/centos.py
mkosi/distributions/fedora.py
mkosi/distributions/mageia.py
mkosi/distributions/openmandriva.py
mkosi/distributions/opensuse.py
mkosi/distributions/rhel.py
mkosi/distributions/rhel_ubi.py
mkosi/distributions/rocky.py
mkosi/installer/rpm.py
mkosi/installer/zypper.py
mkosi/resources/mkosi.md
tests/test_json.py
tests/test_sysext.py

index 3f71fe4f669f42bdd198e5253fd3bfd5d9751b10..9ee7a982722a712cc7ff3d8254f8a641546bac9c 100644 (file)
@@ -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 <<EOF
+        [Distribution]
+        RepositoryKeyFetch=yes
+        EOF
+        fi
+
         # TODO: Remove once all distros have recent enough systemd that knows systemd.default_device_timeout_sec.
         mkdir -p mkosi-initrd/mkosi.extra/usr/lib/systemd/system.conf.d
         tee mkosi-initrd/mkosi.extra/usr/lib/systemd/system.conf.d/device-timeout.conf <<EOF
index 90dbfc8c3012d432a344753fbfa24548cc85d3a6..186af45758e40fac32aa9699e4a74f72a204f25e 100644 (file)
@@ -1807,6 +1807,7 @@ def finalize_default_initrd(
         "--architecture", str(config.architecture),
         *(["--mirror", config.mirror] if config.mirror else []),
         "--repository-key-check", str(config.repository_key_check),
+        "--repository-key-fetch", str(config.repository_key_fetch),
         "--repositories", ",".join(config.repositories),
         "--package-manager-tree", ",".join(str(t) for t in config.package_manager_trees),
         # Note that when compress_output == Compression.none == 0 we don't pass --compress-output which means the
@@ -4477,6 +4478,7 @@ def finalize_default_tools(args: Args, config: Config, *, resources: Path) -> 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 []),
index 2e17be0b4fba8e6877ce5cf3f523433d44d31eef..96285ee73380faee9d86f53c184c456a1726816b 100644 (file)
@@ -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)}
index ac6285cacc1a3f0bd6c92eacb6340163f5d68b97..16345ebb7f71bb2045ca2627f9f56b3e0e273c92 100644 (file)
@@ -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
index e6cd985de6b4e211f8a47527c61ffbd4654a994c..e324620bc7af0dccabae67eee6d7da545d835bf3 100644 (file)
@@ -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:
index 551d3190519eff3e331538667f4127393dab00ab..97008311a3bada37ed62f5accac1b4804888ce10 100644 (file)
@@ -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"
 
 
index c0e3eb2b7d312dd197a0983278afaa03be64eb35..4b817fb5190c27ad4841a8067bf81b68d0270c3e 100644 (file)
@@ -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:
index 15a76c8c41a89e0082222494bde56eab448c3e98..5238864a3a3cd3bcb7dab6edf4b702658e370fa6 100644 (file)
@@ -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:
index 382f54eb3b27c286fdfbf88b57346ad54a2affc1..f089a97ebef5bd385ba311cb455a56c481fc554e 100644 (file)
@@ -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 = ""
index a708751992bdf1b4fe78e09025a7987d81644a23..ccc1e4a27bad1215d308a7763a96aeab44f8fc47 100644 (file)
@@ -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
index 78bfc233b8c77f3af4d97bc586801abadd198b34..c921ecd1fa5eab81129af7b974214063a081c6f1 100644 (file)
@@ -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
index af2c4d18629f451ca1ababaa13da86dc742590f1..0a2cdfd080863cee2dfbcfc0f3b7a95e01aa3565 100644 (file)
@@ -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
index 94d07697a033a15cd895269e14eb4727b0306ced..185db91c644a438e6e2deca1384d12fd1bdbf108 100644 (file)
@@ -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
 
 
index d58c6a6b888e5c821c62010caf82bbb56e9456a0..90ef6ad34974fda15851308ffc7b18cd944fba7a 100644 (file)
@@ -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]),
         ]
 
index 41ee2b61d685f349b8ac3156bf38a05acac97c59..02fdd1fa6c4afb6268237a0931961079d8710683 100644 (file)
@@ -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.
index 43a51f1ab121f2966206307fb012d752209bdffe..783790412cb37a9d12fa6a3d4cdf119d6f66b55a 100644 (file)
@@ -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,
index 6b5cf244eaa04c74ca6c11e8172b76c08be3f88a..3d07c8ced1dc297e27bd644ca1a8b1b3b198ed76 100644 (file)
@@ -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",