- 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
# 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
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1-or-later
-
-[Match]
-Release=jammy
-
-[Build]
-SandboxTrees=mkosi.sandbox
+++ /dev/null
-# 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-----
+++ /dev/null
-#!/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
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,
)
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)
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]
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)
# 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)
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(
config: Config,
*,
resources: Path,
+ keyring_dir: Path,
metadata_dir: Path,
package_dir: Optional[Path] = None,
) -> None:
config,
workspace=workspace,
resources=resources,
+ keyring_dir=keyring_dir,
metadata_dir=metadata_dir,
package_dir=package_dir,
)
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 {
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)
args,
config,
resources=resources,
+ keyring_dir=Path(keyring_dir),
metadata_dir=Path(metadata_dir),
package_dir=Path(package_dir),
)
dst: Path,
*,
log: bool = True,
+ dirs: Sequence[PathString] = (),
options: Sequence[PathString] = (),
sandbox: SandboxProtocol = nosandbox,
) -> None:
"--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.
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]:
*,
workspace: Path,
resources: Path,
+ keyring_dir: Path,
metadata_dir: Path,
package_dir: Optional[Path] = None,
) -> None:
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")
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
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)
# 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
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)))
"--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 []),
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
# 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():
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 [])])
]
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"))]
`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`,
+++ /dev/null
-#!/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