From bbd04569611c50e1f68070390e1a1b57db8a2a92 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Wed, 15 Jan 2025 23:07:07 +0100 Subject: [PATCH] Add a keyring cache and use it for Arch Linux Currently, the Arch keyring from the host or tools tree is always used directly without any option to modify it (except by modifying it directly on the host or in the tools tree). We also don't support RepositoryKeyFetch= for the Arch Linux keyring and it's one of the sources of annoying mounts outside of /usr which complicates "mkosi sandbox". Let's improve the situation by switching to our own pacman keyring instead of using the one from the tools tree or host. Instead, we'll only use /usr/share/pacman/keyrings from the host or tools tree to populate the keyring we maintain ourselves. Users can extend the keyring created by mkosi with their own keys via sandbox trees. If RepositoryKeyFetch= is enabled, we'll download the archlinux-keyring package and extract the keyring from there into the sandbox. The keyring cache is maintained together with the repository metadata cache. The only difference is that sync_repository_metadata() will update the global package cache whereas the keyring directory is always maintained per cache directory instead of globally. We take the opportunity to stop using Michel's kernel-utils ppa with a more recent archlinux-keyring package in favor of enabling RepositoryKeyFetch= by default for Arch builds on Ubuntu. --- action.yaml | 3 +- .../20-ubuntu/mkosi.conf.d/20-jammy.conf | 7 - .../apt/sources.list.d/kernel-utils.sources | 36 ----- mkosi.prepare.chroot | 12 -- mkosi/__init__.py | 133 ++++++++++++++---- mkosi/archive.py | 2 + mkosi/config.py | 21 ++- mkosi/context.py | 2 + mkosi/distributions/__init__.py | 7 + mkosi/distributions/arch.py | 27 +++- mkosi/installer/__init__.py | 2 + mkosi/installer/pacman.py | 35 +++++ mkosi/mounts.py | 5 - mkosi/resources/man/mkosi.1.md | 10 +- .../mkosi-tools/mkosi.prepare.chroot | 12 -- 15 files changed, 194 insertions(+), 120 deletions(-) delete mode 100644 mkosi.conf.d/20-ubuntu/mkosi.conf.d/20-jammy.conf delete mode 100644 mkosi.conf.d/20-ubuntu/mkosi.sandbox/etc/apt/sources.list.d/kernel-utils.sources delete mode 100755 mkosi.prepare.chroot delete mode 100755 mkosi/resources/mkosi-tools/mkosi.prepare.chroot diff --git a/action.yaml b/action.yaml index 8a5b574ed..c57df133b 100644 --- a/action.yaml +++ b/action.yaml @@ -56,7 +56,7 @@ runs: - name: Create missing mountpoints shell: bash run: | - for p in /etc/pki /etc/pacman.d/gnupg /etc/ssl /etc/ca-certificates /var/lib/ca-certificates /etc/crypto-policies; do + for p in /etc/pki /etc/ssl /etc/ca-certificates /var/lib/ca-certificates /etc/crypto-policies; do if [[ ! -e "$p" ]]; then sudo mkdir -p "$p" fi @@ -88,7 +88,6 @@ runs: # This is added by default, and it is often broken, but we don't need anything from it sudo rm -f /etc/apt/sources.list.d/microsoft-prod.list # For archlinux-keyring and pacman - sudo add-apt-repository ppa:michel-slm/kernel-utils sudo apt-get update mkosi dependencies | xargs -d '\n' sudo apt-get install --assume-yes --no-install-recommends diff --git a/mkosi.conf.d/20-ubuntu/mkosi.conf.d/20-jammy.conf b/mkosi.conf.d/20-ubuntu/mkosi.conf.d/20-jammy.conf deleted file mode 100644 index dbb5f8c6d..000000000 --- a/mkosi.conf.d/20-ubuntu/mkosi.conf.d/20-jammy.conf +++ /dev/null @@ -1,7 +0,0 @@ -# SPDX-License-Identifier: LGPL-2.1-or-later - -[Match] -Release=jammy - -[Build] -SandboxTrees=mkosi.sandbox diff --git a/mkosi.conf.d/20-ubuntu/mkosi.sandbox/etc/apt/sources.list.d/kernel-utils.sources b/mkosi.conf.d/20-ubuntu/mkosi.sandbox/etc/apt/sources.list.d/kernel-utils.sources deleted file mode 100644 index c79e0c46d..000000000 --- a/mkosi.conf.d/20-ubuntu/mkosi.sandbox/etc/apt/sources.list.d/kernel-utils.sources +++ /dev/null @@ -1,36 +0,0 @@ -# SPDX-License-Identifier: LGPL-2.1-or-later -Enabled: yes -Types: deb -URIs: https://ppa.launchpadcontent.net/michel-slm/kernel-utils/ubuntu -Suites: jammy -Components: main -Signed-By: - -----BEGIN PGP PUBLIC KEY BLOCK----- - . - xsFNBGJyEb0BEADR6CoHmzotr+Z1UmqkNQZ/z+tm4u3/KbjD/UKHOloAJK2L2Mf6 - Eq1hbs2MlEYa7VtYcfq+NqluvTtqHckgE6sTFGbxQXMUDK+bcxqXmQUQcRw/Wytl - BgRr6fCA+pK82W6Z5eFyYsfbZMqnIqw3rbtx02K43KFGOiP8Pj/FFcPXzf9q3+3k - EXELs8y6N7OEYeloEs45RnBwkKETvMX08zbTZfn5owfYZRd4VpIZJ2BnZprzdzfP - Z1ZTkkDTAUpZvpXFi5WtDx6rVP92+7OYxOjUKzQ9wFbKdkZVzqhfJIR2SHM2tHVz - GwIZl6vYsAqLLZccQSS4nBCXIzUwRO28LwqzacjKabl197fk0C/IKFw2Z4/ZsCHb - VcrnttD3G1AvQ+DMvEPTzB9vD4R7uNEkmg4UYokzk6yW9/KQm3lNbQBS+D5jZREv - hRyz4ZLW9wzz13H655nXzJCIZvLWVpRLNzQDscxYlkYBONoH+HiGafsgZqw+ITc3 - tDPTw0HqRNe+/oendXqOhtKhY2PRhD3vu5NgXLX7GuAHr7Dq9HMUyA5MxKH90e28 - vaYUmwU0jfYGgNnSCpRrAOx0SlqKpMlwW9VkpJctGrYMZ/ts6yPdJC2OSWiJRlMa - Xmf4IvsLrCGobd27y03TYl5Uq6OOpD0DSP2hGZQxYHOgMhhZxT99IJUlMwARAQAB - zShMYXVuY2hwYWQgUFBBIGZvciBNaWNoZWwgQWxleGFuZHJlIFNhbGltwsGOBBMB - CgA4FiEE+8ojhpUuuImlrBJjySgIhqCuA8oFAmJyEb0CGwMFCwkIBwIGFQoJCAsC - BBYCAwECHgECF4AACgkQySgIhqCuA8orpw//YWpifbMc2F3zNx8oW2UyTsr2IXtu - 4/pHVtDroYokMOvCy2IR6FhzXSMM35yQBVfn92T5MiG0pHqXNUIZstt/m3Qo7tnj - 1AR9f0mRLTKHONQCUP91CiVHGGKfaYHiyQ9Pxxp+LUxFkoEXUfQPl6N0wfGCd0Rb - k9gcFlOo3+duOFsd+1Aw3Gi83SFcl6Bc4P/3i+dfB4g1Nbte0ZzPnFWKKYRV0K6z - 5uDbJJYdIS+nwIxVXb7cnMUrrHBr4cUDsIXnAwVN+zeK7Q4CrJOpR6ZDGNb5SGcO - TaJPEOpCIcIKkzW9IzYm5NTzxhQHg7jvCPrGBuX3nTt1fEzCn5L2se9iwehtsMat - WXwi+yIYlpce2vHRPZEb8ILMoCL50veAAZ2tAlHx9UnAPNtT+1PPzrKPcIVCAB2e - fBgUBcCaQ62LWsIQX1B9qL4xhGX7Z4nFk2aXNlrHjnnf5gwFCJ/XiVuFgGetfRrV - r2PgfFFOfUanJ4LMu8sfqurrNJXrYMHfA8+qIbTLyltlqsOiEROOa/Qje5KEqmbe - vg/hbqRpGNHdYKP1OynqBK8VAgG9/g5qGR8FLXr1DXl5dzlqyiIkRQINd9O6XjnX - LWPl1wsOXOCY/jWgMxktt8Mv9qaaZ4CT9cuwsm/aml270A3GKRYHLDFP3CkuMnqd - 0vsZgWMIQtgQmXU= - =7vwW - -----END PGP PUBLIC KEY BLOCK----- diff --git a/mkosi.prepare.chroot b/mkosi.prepare.chroot deleted file mode 100755 index 1749c0914..000000000 --- a/mkosi.prepare.chroot +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh -# SPDX-License-Identifier: LGPL-2.1-or-later -set -e - -# Users might set $CONFIG but that influences pacman -# so we unset it here to avoid any issues. -unset CONFIG - -if [ "$1" = "final" ] && command -v pacman-key; then - pacman-key --init - pacman-key --populate archlinux -fi diff --git a/mkosi/__init__.py b/mkosi/__init__.py index 7ac87b324..1033e8022 100644 --- a/mkosi/__init__.py +++ b/mkosi/__init__.py @@ -1312,7 +1312,9 @@ def build_default_initrd(context: Context) -> Path: config, workspace=workspace, resources=context.resources, - # Reuse the repository metadata snapshot from the main image for the initrd. + # Reuse the keyring, repository metadata and local package repository from the main image for + # the default initrd. + keyring_dir=context.keyring_dir, metadata_dir=context.metadata_dir, package_dir=context.package_dir, ) @@ -4365,8 +4367,8 @@ def run_clean(args: Args, config: Config) -> None: rmtree(*config.build_dir.iterdir(), sandbox=sandbox) if remove_image_cache and config.cache_dir: - metadata = [metadata_cache(config)] if not config.image else [] - remove_cache_entries(config, extra=metadata) + extra = [keyring_cache(config), metadata_cache(config)] if not config.image else [] + remove_cache_entries(config, extra=extra) if remove_package_cache and any(config.package_cache_dir_or_default().glob("*")): subdir = config.distribution.package_manager(config).subdir(config) @@ -4402,6 +4404,13 @@ def ensure_directories_exist(config: Config) -> None: config.build_dir.chmod(stat.S_IMODE(st.st_mode) & ~(stat.S_ISGID | stat.S_ISUID)) +def keyring_cache(config: Config) -> Path: + assert config.cache_dir + fragments = [config.distribution, config.release, config.architecture] + + return config.cache_dir / f"{'~'.join(str(s) for s in fragments)}.keyring.cache" + + def metadata_cache(config: Config) -> Path: assert config.cache_dir fragments = [config.distribution, config.release, config.architecture] @@ -4409,19 +4418,42 @@ def metadata_cache(config: Config) -> Path: return config.cache_dir / f"{'~'.join(str(s) for s in fragments)}.metadata.cache" -def sync_repository_metadata(args: Args, images: Sequence[Config], *, resources: Path, dst: Path) -> None: +def sync_repository_metadata( + args: Args, + images: Sequence[Config], + *, + resources: Path, + keyring_dir: Path, + metadata_dir: Path, +) -> None: last = images[-1] # If we have a metadata cache and any cached image and using cached metadata is not explicitly disabled, # reuse the metadata cache. if ( last.incremental + and keyring_cache(last).exists() and metadata_cache(last).exists() and last.cacheonly != Cacheonly.never and any(have_cache(config) for config in images) ): + if any(keyring_cache(last).iterdir()): + with complete_step("Copying cached package manager keyring"): + copy_tree( + keyring_cache(last), + keyring_dir, + use_subvolumes=last.use_subvolumes, + sandbox=last.sandbox, + ) + with complete_step("Copying cached package manager metadata"): - copy_tree(metadata_cache(last), dst, use_subvolumes=last.use_subvolumes, sandbox=last.sandbox) + copy_tree( + metadata_cache(last), + metadata_dir, + use_subvolumes=last.use_subvolumes, + sandbox=last.sandbox, + ) + return subdir = last.distribution.package_manager(last).subdir(last) @@ -4431,16 +4463,13 @@ def sync_repository_metadata(args: Args, images: Sequence[Config], *, resources: # Sync repository metadata unless explicitly disabled. if last.cacheonly not in (Cacheonly.always, Cacheonly.metadata): - with ( - complete_step("Syncing package manager metadata"), - lock_repository_metadata(last), - setup_workspace(args, last) as workspace, - ): + with setup_workspace(args, last) as workspace: context = Context( args, last, workspace=workspace, resources=resources, + keyring_dir=keyring_dir, metadata_dir=last.package_cache_dir_or_default(), ) context.root.mkdir(mode=0o755) @@ -4448,24 +4477,36 @@ def sync_repository_metadata(args: Args, images: Sequence[Config], *, resources: install_sandbox_trees(context.config, context.sandbox_tree) context.config.distribution.setup(context) - context.config.distribution.package_manager(context.config).sync( - context, - force=context.args.force > 1 or context.config.cacheonly == Cacheonly.never, - ) + context.config.distribution.keyring(context) + + with complete_step("Syncing package manager metadata"), lock_repository_metadata(last): + context.config.distribution.package_manager(context.config).sync( + context, + force=context.args.force > 1 or context.config.cacheonly == Cacheonly.never, + ) src = last.package_cache_dir_or_default() / "cache" / subdir for p in last.distribution.package_manager(last).cache_subdirs(src): p.mkdir(parents=True, exist_ok=True) - # If we're in incremental mode and caching metadata is not explicitly disabled, cache the synced - # repository metadata so we can reuse it later. + # If we're in incremental mode and caching metadata is not explicitly disabled, cache the keyring and the + # synced repository metadata so we can reuse them later. if last.incremental and last.cacheonly != Cacheonly.never: - rmtree(metadata_cache(last), sandbox=last.sandbox) - make_tree(metadata_cache(last), use_subvolumes=last.use_subvolumes, sandbox=last.sandbox) + rmtree(keyring_cache(last), metadata_cache(last), sandbox=last.sandbox) + + for p in (keyring_cache(last), metadata_cache(last)): + make_tree(p, use_subvolumes=last.use_subvolumes, sandbox=last.sandbox) + + copy_tree(keyring_dir, keyring_cache(last), use_subvolumes=last.use_subvolumes, sandbox=last.sandbox) copy_repository_metadata(last, metadata_cache(last)) - copy_tree(metadata_cache(last), dst, use_subvolumes=last.use_subvolumes, sandbox=last.sandbox) + copy_tree( + metadata_cache(last), + metadata_dir, + use_subvolumes=last.use_subvolumes, + sandbox=last.sandbox, + ) else: - copy_repository_metadata(last, dst) + copy_repository_metadata(last, metadata_dir) def run_build( @@ -4473,6 +4514,7 @@ def run_build( config: Config, *, resources: Path, + keyring_dir: Path, metadata_dir: Path, package_dir: Optional[Path] = None, ) -> None: @@ -4520,6 +4562,7 @@ def run_build( config, workspace=workspace, resources=resources, + keyring_dir=keyring_dir, metadata_dir=metadata_dir, package_dir=package_dir, ) @@ -4710,12 +4753,31 @@ def run_verb(args: Args, images: Sequence[Config], *, resources: Path) -> None: check_tools(tools, Verb.build) ensure_directories_exist(tools) - with tempfile.TemporaryDirectory( - dir=tools.workspace_dir_or_default(), - prefix="mkosi-metadata-", - ) as metadata_dir: - sync_repository_metadata(args, [tools], resources=resources, dst=Path(metadata_dir)) - fork_and_wait(run_build, args, tools, resources=resources, metadata_dir=Path(metadata_dir)) + with ( + tempfile.TemporaryDirectory( + dir=tools.workspace_dir_or_default(), + prefix="mkosi-keyring-", + ) as keyring_dir, + tempfile.TemporaryDirectory( + dir=tools.workspace_dir_or_default(), + prefix="mkosi-metadata-", + ) as metadata_dir, + ): + sync_repository_metadata( + args, + [tools], + resources=resources, + keyring_dir=Path(keyring_dir), + metadata_dir=Path(metadata_dir), + ) + fork_and_wait( + run_build, + args, + tools, + resources=resources, + keyring_dir=Path(keyring_dir), + metadata_dir=Path(metadata_dir), + ) if not args.verb.needs_build(): return { @@ -4759,13 +4821,25 @@ def run_verb(args: Args, images: Sequence[Config], *, resources: Path) -> None: with ( tempfile.TemporaryDirectory( - dir=last.workspace_dir_or_default(), prefix="mkosi-metadata-" + dir=last.workspace_dir_or_default(), + prefix="mkosi-keyring-", + ) as keyring_dir, + tempfile.TemporaryDirectory( + dir=last.workspace_dir_or_default(), + prefix="mkosi-metadata-", ) as metadata_dir, tempfile.TemporaryDirectory( - dir=last.workspace_dir_or_default(), prefix="mkosi-packages-" + dir=last.workspace_dir_or_default(), + prefix="mkosi-packages-", ) as package_dir, ): - sync_repository_metadata(args, images, resources=resources, dst=Path(metadata_dir)) + sync_repository_metadata( + args, + images, + resources=resources, + keyring_dir=Path(keyring_dir), + metadata_dir=Path(metadata_dir), + ) for config in images: run_sync_scripts(config) @@ -4785,6 +4859,7 @@ def run_verb(args: Args, images: Sequence[Config], *, resources: Path) -> None: args, config, resources=resources, + keyring_dir=Path(keyring_dir), metadata_dir=Path(metadata_dir), package_dir=Path(package_dir), ) diff --git a/mkosi/archive.py b/mkosi/archive.py index be323ec78..7a42d9a9e 100644 --- a/mkosi/archive.py +++ b/mkosi/archive.py @@ -67,6 +67,7 @@ def extract_tar( dst: Path, *, log: bool = True, + dirs: Sequence[PathString] = (), options: Sequence[PathString] = (), sandbox: SandboxProtocol = nosandbox, ) -> None: @@ -93,6 +94,7 @@ def extract_tar( "--force-local", *tar_exclude_apivfs_tmp(), *options, + *dirs, ], sandbox=sandbox( # Make sure tar uses user/group information from the root directory instead of the host. diff --git a/mkosi/config.py b/mkosi/config.py index 525a73a35..afecd05e9 100644 --- a/mkosi/config.py +++ b/mkosi/config.py @@ -902,26 +902,25 @@ def config_default_tools_tree_distribution(namespace: argparse.Namespace) -> Dis def config_default_repository_key_fetch(namespace: argparse.Namespace) -> bool: + def needs_repository_key_fetch(distribution: Distribution) -> bool: + return distribution == Distribution.arch or distribution.is_rpm_distribution() + if detect_distribution()[0] != Distribution.ubuntu: return False if namespace.tools_tree is None: - return cast(bool, namespace.distribution.is_rpm_distribution()) + return needs_repository_key_fetch(namespace.distribution) if namespace.tools_tree != Path("default"): return ( detect_distribution(namespace.tools_tree)[0] == Distribution.ubuntu - and namespace.distribution.is_rpm_distribution() - ) + and needs_repository_key_fetch(namespace.distribution) + ) # fmt: skip - return cast( - bool, - ( - namespace.tools_tree_distribution == Distribution.ubuntu - and namespace.distribution.is_rpm_distribution() - ) - or namespace.tools_tree_distribution.is_rpm_distribution(), - ) + return ( + namespace.tools_tree_distribution == Distribution.ubuntu + and needs_repository_key_fetch(namespace.distribution) + ) or needs_repository_key_fetch(namespace.tools_tree_distribution) def config_default_source_date_epoch(namespace: argparse.Namespace) -> Optional[int]: diff --git a/mkosi/context.py b/mkosi/context.py index 2f8d32374..109cdc556 100644 --- a/mkosi/context.py +++ b/mkosi/context.py @@ -19,6 +19,7 @@ class Context: *, workspace: Path, resources: Path, + keyring_dir: Path, metadata_dir: Path, package_dir: Optional[Path] = None, ) -> None: @@ -26,6 +27,7 @@ class Context: self.config = config self.workspace = workspace self.resources = resources + self.keyring_dir = keyring_dir self.metadata_dir = metadata_dir self.package_dir = package_dir or (self.workspace / "packages") diff --git a/mkosi/distributions/__init__.py b/mkosi/distributions/__init__.py index 4d7bb4b3c..0800bafc0 100644 --- a/mkosi/distributions/__init__.py +++ b/mkosi/distributions/__init__.py @@ -31,6 +31,10 @@ class DistributionInstaller: def package_manager(cls, config: "Config") -> type["PackageManager"]: raise NotImplementedError + @classmethod + def keyring(cls, context: "Context") -> None: + pass + @classmethod def setup(cls, context: "Context") -> None: raise NotImplementedError @@ -123,6 +127,9 @@ class Distribution(StrEnum): def package_manager(self, config: "Config") -> type["PackageManager"]: return self.installer().package_manager(config) + def keyring(self, context: "Context") -> None: + return self.installer().keyring(context) + def setup(self, context: "Context") -> None: return self.installer().setup(context) diff --git a/mkosi/distributions/arch.py b/mkosi/distributions/arch.py index 86711f88b..cd9a81f50 100644 --- a/mkosi/distributions/arch.py +++ b/mkosi/distributions/arch.py @@ -1,13 +1,17 @@ # SPDX-License-Identifier: LGPL-2.1-or-later +import tempfile from collections.abc import Iterable, Sequence +from pathlib import Path +from mkosi.archive import extract_tar from mkosi.config import Architecture, Config from mkosi.context import Context +from mkosi.curl import curl from mkosi.distributions import DistributionInstaller, PackageType from mkosi.installer import PackageManager from mkosi.installer.pacman import Pacman, PacmanRepository -from mkosi.log import die +from mkosi.log import complete_step, die from mkosi.util import sort_packages @@ -32,6 +36,27 @@ class Installer(DistributionInstaller): def package_manager(cls, config: "Config") -> type[PackageManager]: return Pacman + @classmethod + def keyring(cls, context: Context) -> None: + if context.config.repository_key_fetch: + with ( + complete_step(f"Downloading {cls.pretty_name()} keyring"), + tempfile.TemporaryDirectory() as d, + ): + curl( + context.config, + "https://archlinux.org/packages/core/any/archlinux-keyring/download", + Path(d), + ) + extract_tar( + next(Path(d).iterdir()), + context.sandbox_tree, + dirs=["usr/share/pacman/keyrings"], + sandbox=context.sandbox, + ) + + Pacman.keyring(context) + @classmethod def setup(cls, context: Context) -> None: Pacman.setup(context, list(cls.repositories(context))) diff --git a/mkosi/installer/__init__.py b/mkosi/installer/__init__.py index 5f78a07ee..18352c6c3 100644 --- a/mkosi/installer/__init__.py +++ b/mkosi/installer/__init__.py @@ -109,6 +109,8 @@ class PackageManager: "--suppress-chown", # Make sure /etc/machine-id is not overwritten by any package manager post install scripts. "--ro-bind-try", Path(root) / "etc/machine-id", "/buildroot/etc/machine-id", + # Nudge gpg to create its sockets in /run by making sure /run/user/0 exists. + "--dir", "/run/user/0", # Some package managers (e.g. dpkg) read from the host's /etc/passwd instead of the buildroot's # /etc/passwd so we symlink /etc/passwd from the buildroot to make sure it gets used. *(finalize_passwd_symlinks("/buildroot") if apivfs else []), diff --git a/mkosi/installer/pacman.py b/mkosi/installer/pacman.py index e8b7b81af..56e354f42 100644 --- a/mkosi/installer/pacman.py +++ b/mkosi/installer/pacman.py @@ -4,13 +4,17 @@ import dataclasses import shutil import textwrap from collections.abc import Sequence +from contextlib import AbstractContextManager from pathlib import Path from mkosi.config import Config from mkosi.context import Context +from mkosi.distributions import detect_distribution from mkosi.installer import PackageManager +from mkosi.log import complete_step from mkosi.run import run, workdir from mkosi.sandbox import umask +from mkosi.tree import copy_tree from mkosi.types import _FILE, CompletedProcess, PathString from mkosi.versioncomp import GenericVersion @@ -55,6 +59,7 @@ class Pacman(PackageManager): # pacman writes downloaded packages to the first writable cache directory. We don't want it to # write to our local repository directory so we expose it as a read-only directory to pacman. "--ro-bind", context.repository, "/var/cache/pacman/mkosi", + "--ro-bind", context.keyring_dir, "/etc/pacman.d/gnupg", ] # fmt: skip if (context.root / "var/lib/pacman/local").exists(): @@ -175,6 +180,36 @@ class Pacman(PackageManager): stdout=stdout, ) + @classmethod + def keyring(cls, context: Context) -> None: + def sandbox() -> AbstractContextManager[list[PathString]]: + return cls.sandbox( + context, + apivfs=False, + # By default the keyring is mounted read-only so we override the read-only mount with a + # writable mount to make it writable for the following pacman-key commands. + options=["--bind", context.keyring_dir, "/etc/pacman.d/gnupg"], + ) + + if ( + (d := detect_distribution(context.config.tools())[0]) + and d.is_apt_distribution() + and (context.sandbox_tree / "usr/share/pacman/keyrings").exists() + ): + # pacman on Debian/Ubuntu looks for keyrings in /usr/share/keyrings so make sure all sandbox + # trees keyrings are available in that location as well. + (context.sandbox_tree / "usr/share").mkdir(parents=True, exist_ok=True) + copy_tree( + context.sandbox_tree / "usr/share/pacman/keyrings", + context.sandbox_tree / "usr/share/keyrings", + dereference=True, + sandbox=context.sandbox, + ) + + with complete_step("Populating pacman keyring"): + run(["pacman-key", "--init"], sandbox=sandbox()) + run(["pacman-key", "--populate"], sandbox=sandbox()) + @classmethod def sync(cls, context: Context, force: bool) -> None: cls.invoke(context, "--sync", ["--refresh", *(["--refresh"] if force else [])]) diff --git a/mkosi/mounts.py b/mkosi/mounts.py index a04d7f4b9..78ab8fc62 100644 --- a/mkosi/mounts.py +++ b/mkosi/mounts.py @@ -103,11 +103,6 @@ def finalize_crypto_mounts(config: Config, relaxed: bool = False) -> list[PathSt ] if not relaxed or config.tools() != Path("/"): - # This contains the Arch Linux keyring, which isn't certificates so ToolsTreeCertificates= doesn't - # apply. - if (config.tools() / "etc/pacman.d/gnupg").exists(): - mounts += [(config.tools() / "etc/pacman.d/gnupg", Path("/etc/pacman.d/gnupg"))] - if (config.tools() / "etc/crypto-policies").exists(): mounts += [(config.tools() / "etc/crypto-policies", Path("/etc/crypto-policies"))] diff --git a/mkosi/resources/man/mkosi.1.md b/mkosi/resources/man/mkosi.1.md index b3edb7dff..f2bd013bb 100644 --- a/mkosi/resources/man/mkosi.1.md +++ b/mkosi/resources/man/mkosi.1.md @@ -492,12 +492,12 @@ boolean argument: either `1`, `yes`, or `true` to enable, or `0`, `no`, `RepositoryKeyFetch=`, `--repository-key-fetch=` : Controls whether **mkosi** will fetch distribution GPG keys remotely. Enabled by default on Ubuntu when not using a tools tree or when using Ubuntu tools trees to build - RPM-based distributions. Disabled by default on all other distributions. 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. + Arch Linux or RPM-based distributions. Disabled by default on all other distributions. + 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 + This setting is only implemented for distributions using **dnf**, **pacman** 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`, diff --git a/mkosi/resources/mkosi-tools/mkosi.prepare.chroot b/mkosi/resources/mkosi-tools/mkosi.prepare.chroot deleted file mode 100755 index 1749c0914..000000000 --- a/mkosi/resources/mkosi-tools/mkosi.prepare.chroot +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh -# SPDX-License-Identifier: LGPL-2.1-or-later -set -e - -# Users might set $CONFIG but that influences pacman -# so we unset it here to avoid any issues. -unset CONFIG - -if [ "$1" = "final" ] && command -v pacman-key; then - pacman-key --init - pacman-key --populate archlinux -fi -- 2.47.2