Luca Boccassi [Wed, 28 Jan 2026 22:43:21 +0000 (22:43 +0000)]
Add MakeScriptsExecutable= setting to optionally try to make scripts executable before bailing out
If it fails, it was going to die() anyway.
OBS sources defined inline (ie, not in a tarball) cannot have the mode preserved,
so it's not possible to have mkosi.build or so as a bare script
in an OBS project, one needs to tar it up and extract it again later,
which means it cannot be edited by the inline editor, which is very
convenient for small and trivial builds like an addon.
Daan De Meyer [Wed, 21 Jan 2026 09:05:24 +0000 (10:05 +0100)]
rpm: Set pkgverify_level to digest
This was changed to all in rpm 6.0.0, which means that rpm is checking
for signatures from dnf/zypper repos that have gpgcheck=0. dnf5 was updated
to deal with this but for some reason the fix isn't working in Arch and zypper
doesn't deal with this at all, so revert back to the previous level until
package managers can actually deal with this.
Daan De Meyer [Tue, 20 Jan 2026 18:25:35 +0000 (19:25 +0100)]
dnf: Give advanced users some control over plugins
Let's add some environment variables to control plugins for cases
where users have some dnf plugin they can't touch on their host system
which doesn't behave properly in mkosi-sandbox and which they can't
remove themselves.
Daan De Meyer [Sun, 18 Jan 2026 10:53:30 +0000 (11:53 +0100)]
Allow specifying "default" value for Initrds=
Also use it as the default value for Initrds= when we decide a default
initrd is needed. This allows both using the default initrd alongside
other initrds as well as disabling building the default initrd by assigning
the empty string to Initrds=.
Daan De Meyer [Thu, 15 Jan 2026 12:13:04 +0000 (13:13 +0100)]
opensuse: More GPG key handling fixes
- Pass GPG keys to rpm --import as paths inside the sandbox. This
makes sure that overrides from mkosi.sandbox are taken into account.
e.g. atm we pass mkosi.tools/usr/share/distribution-gpg-keys/... whereas
now we pass /usr/share/distribution-gpg-keys/...
- Make sure we figure out keys once when using zypper. zypper downloads
GPG keys (when fetching is enabled) when refreshing repositories. These
keys are stored in the rpm database in the temporary root we use when
syncing repository metadata. To make sure they are not lost, we extract
the keys using rpmkeys and store them in the keyring directory which we
use from then onwards. For all image builds we then simply import the
keys from the keyring directory.
Daan De Meyer [Wed, 14 Jan 2026 18:24:05 +0000 (19:24 +0100)]
opensuse: Import GPG keys downloaded by zypper as well
When syncing repository metadata, zypper will download keys if
--gpg-auto-import-keys is specified. When installing packages, we need
to make sure these keys are imported into the rpmdb as well.
DaanDeMeyer [Thu, 8 Jan 2026 17:06:16 +0000 (18:06 +0100)]
Make sure we pass the right context to finalize_default_initrd()
We mess around with the context object to make it for for use when
reading the subimages. But we need the full context again for parsing
the default initrd later on, so make a copy before we delete stuff and
pass that to finalize_default_initrd()
DaanDeMeyer [Tue, 6 Jan 2026 20:29:04 +0000 (21:29 +0100)]
Stop running auxiliary programs in systemd scopes
Similar to the same change made in systemd-vmspawn, let's stop running
virtiofsd, systemd-journal-remote and swtpm in scopes. Nobody ever makes
use of the features this provides and it simplifies our code quite a bit.
This also means we drop the UnitProperties setting, which was effectively
unused anyway.
This allows us to get rid of the --suspend setting in mkosi-sandbox, which
only really existed to allow waiting for systemd-run to finish its setup
before registering the machine. Because registering a machine means it needs
a cgroup, we allow systemd-machined to create the scope itself if needed.
DaanDeMeyer [Mon, 29 Dec 2025 18:54:01 +0000 (19:54 +0100)]
nixos: Use repository key fetching by default on nixos
nixos generally won't have any keys in the expected locations so let's
use repository key fetching by default if we're building from a nixos
host (or tools tree).
Maximilian Bosch [Mon, 29 Dec 2025 17:06:18 +0000 (18:06 +0100)]
mkosi/run: pass through LD_LIBRARY_PATH
The sandbox expects that the host has a `libseccomp.so` in its global
search-path (usually `/usr/lib`). However, that path doesn't exist on
NixOS. Another standard way of passing lookup paths to `dlopen()` is
using LD_LIBRARY_PATH which is now passed through to the sandbox.
Maximilian Bosch [Mon, 29 Dec 2025 17:04:21 +0000 (18:04 +0100)]
treewide: use `/usr/bin/env bash` instead of `/bin/bash` as shebang
E.g. NixOS doesn't have a `/bin/bash` and some of the scripts are seemingly
running inside the host's context. Hence, use the more cautious variant of
`/usr/bin/env bash` and just do it everywhere for consistency.
DaanDeMeyer [Sat, 27 Dec 2025 12:00:28 +0000 (13:00 +0100)]
docs: Update unprivileged user namespace docs
Let's not recommend fiddling around with apparmor profiles, but
instead recommend enabling unprivileged user namespaces unconditionally.
Users that care about security can figure out apparmor profiles on their own.
Also reorder and reword the REQUIREMENTS section in general.
DaanDeMeyer [Fri, 26 Dec 2025 09:55:24 +0000 (10:55 +0100)]
arch: Download archlinux-keyring with pacman
curl-ing the Arch Linux website fails quite often due to connection
issues. Let's try downloading the archlinux-keyring package with
Pacman so we go directly to a mirror and avoid hitting the Arch
Linux website.
DaanDeMeyer [Thu, 25 Dec 2025 20:41:11 +0000 (21:41 +0100)]
kmod: Reorder ko extension list
It's unsure whether python uses a hash based lookup for this or not,
so let's list the most commonly expected extensions first. Kernel
modules tend to be compressed these days, so those are the ones we
list first, with preference to zstd and xz.
DaanDeMeyer [Thu, 25 Dec 2025 19:48:47 +0000 (20:48 +0100)]
kmod: Stop retrieving dependency info of all modules
Instead of running modinfo once to retrieve the dependency information
of all modules, let's only retrieve the dependency information of the
modules that are to be included in the image and their transitive
dependencies. This means we have to run modinfo multiple times, but with
far fewer modules than before. This ends up being faster than retrieving
the dependency information of all modules, especially after the optimization
from e276dac87a530efac4376a5059b980f2d43460f5.
For the mkosi default image build on Arch Linux this reduces the time for
calculating the required kernel modules and firmware on my laptop from 5s
to 0.5s.
Robert Sturla [Thu, 25 Dec 2025 18:02:13 +0000 (18:02 +0000)]
compressor_command: Use gzip -n for reproducible output
The gzip format includes an MTIME field in its header that stores the
modification time of the original file. This causes compressed archives
to differ between builds even when the uncompressed content is identical.
Add the --no-name flag to gzip which suppresses storing the original
filename and timestamp, making gzip output reproducible.
Robert Sturla [Thu, 25 Dec 2025 18:00:22 +0000 (18:00 +0000)]
build_microcode_initrd: Normalize timestamps for reproducible builds
When building the microcode initrd, files are created in a temporary
directory with current timestamps. These timestamps are then embedded
in the CPIO archive, causing non-reproducible builds even when
SourceDateEpoch is set.
Fix this by normalizing the modification times of all files in the
microcode root directory to source_date_epoch before creating the
CPIO archive.
DaanDeMeyer [Wed, 24 Dec 2025 10:53:57 +0000 (11:53 +0100)]
Fix --debug-shell
While we're at it, let's save ourselves from having to reason about
Python's capture rules for nested functions by moving _preexec() out
of spawn() and passing in arguments via functools.partial().
DaanDeMeyer [Wed, 24 Dec 2025 10:35:32 +0000 (11:35 +0100)]
sandbox: Drop --proc
This is trivially replaced with --bind /proc $DST, so let's drop the
separate option. Maybe in the future we'll add --proc back but have it
actually mount a new procfs instance.
DaanDeMeyer [Wed, 24 Dec 2025 08:35:39 +0000 (09:35 +0100)]
run: Remove hack to keep packed file descriptors intact
Now that we execute execvp() in the preexec function and therefore
skip python's close file descriptors logic, we don't need our hack
anymore to tell python the right file descriptors to close so let's
drop it.
DaanDeMeyer [Tue, 23 Dec 2025 16:17:01 +0000 (17:17 +0100)]
run: Call execvpe() from preexec function
Python does its own executable lookup in $PATH before executing the preexec function, and
hence before we have set up the sandbox which influences the lookup results. To get around
that, let's call execvpe() ourselves inside the preexec() function, and not give Python the
chance to do it itself. This ensures we can do the proper executable lookup after setting
up the sandbox. If we can't find the executable, do nothing, and let Python do its own
search logic so it can return a proper error, which we cannot do from the preexec function.
Note that by doing this we also skip Python closing all open file descriptors except the
ones specified by the user in pass_fds, but since Python opens all file descriptors with
O_CLOEXEC anyway, we'll assume we're good and don't need to close open file descriptors
explicitly.
DaanDeMeyer [Mon, 22 Dec 2025 19:33:08 +0000 (20:33 +0100)]
Configure pyright included files
Let's configure the files on which pyright should run to avoid long
startup times where it tries to check every single file in the workspace
directory.
Daan De Meyer [Mon, 22 Dec 2025 13:54:52 +0000 (14:54 +0100)]
qemu: Register with systemd-machined in user session
Now that machine registration works unprivileged
since systemd v259, let's switch to unconditionally
registering machines with the user session
systemd-machined instance.
This breaks compat but the previous implementation
arguably wasn't useful or used, since registration
would only be done when running as root or if the
Register= feature was explicitly enabled. And if
not running as root, you'd have to authenticate
every time when booting the image to register it
which is arguably too annoying that anyone actually
bothered with it.
As vmspawn doesn't yet support registering with the
user machined instance, we stop registering vmspawn
machines for now. https://github.com/systemd/systemd/pull/40185
will add support for user machined regisration to
vmspawn.
For nspawn we stick with system machined registration
for now.
Daan De Meyer [Fri, 19 Dec 2025 20:02:30 +0000 (21:02 +0100)]
Cache hwdb step
Running hwdb takes roughly a second and is
unlikely to ever rely on files added by extra
trees or such, so let's cache the step instead of
re-running it every single time.
distribution: do not default to release=VERSION_ID for openSUSE Tumbleweed
`config_default_release()` calls `detect_distribution()` to get the default
release if it's not set, which picks the value from os-release's `VERSION_ID`.
In openSUSE Tumbleweed this property has the snapshot number. Since
`mkosi-initrd` does not set `Release=` via config, mkosi thinks that it's Leap
and fails:
```
$ mkosi-initrd
‣ Validating certificates and keys
‣ Building main image
‣ Copying in sandbox trees…
‣ Installing openSUSE
Warning: Enforced setting: $releasever=20251217
Loading repository data...
Reading installed packages...
'Leap-release' not found in package names. Trying capabilities.
No provider of 'Leap-release' found.
‣ "zypper --installroot=/buildroot --cache-dir=/var/cache/zypp --non-interactive --no-refresh --releasever=20251217 --no-gpg-checks install --download in-advance --no-recommends --force-resolution filesystem Leap-release" returned non-zero exit code 104.
‣ "mkosi --force --directory= --format=cpio --output=initrd --output-directory=/tmp/tmpcvx9let7 --extra-tree=/usr/lib/modules/6.17.0-2-default:/usr/lib/modules/6.17.0-2-default --extra-tree=/usr/lib/firmware:/usr/lib/firmware '--remove-files=/usr/lib/firmware/*-ucode' --build-sources= --include=mkosi-initrd --kernel-modules=host --extra-tree=/usr/lib/modules/6.17.0-1-default/updates/hdaps.ko:/usr/lib/modules/6.17.0-1-default/updates/hdaps.ko --extra-tree=/usr/lib/modules/6.17.0-1-default/updates/thinkpad_ec.ko:/usr/lib/modules/6.17.0-1-default/updates/thinkpad_ec.ko --extra-tree=/usr/lib/modules/6.17.0-1-default/updates/tp_smapi.ko:/usr/lib/modules/6.17.0-1-default/updates/tp_smapi.ko --package-cache-dir=/var --cache-only=metadata --output-mode=600 --include /usr/lib/mkosi-initrd --include /etc/mkosi-initrd --sandbox-tree=/tmp/tmp0tjr7mwr --extra-tree=/etc/vconsole.conf:/etc/vconsole.conf" returned non-zero exit code 104.
```
Luca Boccassi [Wed, 17 Dec 2025 20:38:46 +0000 (20:38 +0000)]
mkosi-addon: drop Output=addon, addon.py already has a default
addon.py already passes mkosi-local.addon.efi by default if nothing
else is given, so it's not necessary to override it here. And it makes
it impossible to give custom names via Output= in your own config.
Yu Watanabe [Wed, 17 Dec 2025 16:44:42 +0000 (01:44 +0900)]
sandbox: return raw error code from the kernel and friends on failure
When a system error occurs, the libseccomp returns -ECANCELED and
hides the original error code. That makes harder to debug the failure.
Let's make libseccomp propagate the original error code.