`ExtensionImages=` and `ExtensionDirectories=` now let you specify
vpick-named extensions; however, since they just get set up once when
the service is started, you can't see newer versions without restarting
the service entirely. Here, also reload confext extensions when you
reload a service. This allows you to deploy a new version of some
configuration and have it picked up at reload time without interruption
to your workload.
Right now, we would only reload confext extensions and leave the sysext
ones behind, since it didn't seem prudent to swap out what is likely
program code at reload. This is made possible by only going for the
`SYSTEMD_CONFEXT_HIERARCHIES` overlays (which only contains `/etc`).
Implementation wise, this uses the new kernel API and two collaborating
child processes under the host & child namespaces in order to gather the
right FDs needed:
- (1) In child, set up the extension images and directories in a slave
mountns, and obtain their FDs.
- (2) Fork into a grandchild under target process namespace, and do a
"fake" unmount to obtain the FD of the underlying target folder
say /etc).
- (3) In the child again, set up new overlay under host NS rights.
We do not want to do I/O heavy jobs inline in PID1 blocking the state
machine, so add separate async states to handle this case.
Co-authored-by: Luca Boccassi <luca.boccassi@gmail.com>
DDIs may contain multiple versions of the same OS, or even multiple
OSes. Hence it makes sense to not just pick the "newest", whatever that
might be, but only partitions associated with specific images, or in a
specific version.
Let's a concept for such filtering: a per-designator glob expression
that can be applied to the partition label string, and can be used for
such filtering.
Usecase: when picking UKI belonging to OS image X in version Y, make
sure we only pick a /usr/ partition belonging to X in version Y, and a
root and home partition belonging to X in any version.
This only adds the basic infrastructure, but doesn't actually expose it
anywhere.
When dissecting an image, let's make use of the Verity data even if we
got told no root hash explicitly: we can simply determine it by
concatenating the data partition uuid with the verity partition uuid.
Of course, on first thought this doesn't really add much: if the root
hash is not pinned from somewhere, this does not guarantee trust in
the image.
However, this is very useful for attestation: if we have the root hash
we can measure it before mounting things, even if we don't actually
authenticate it.
Hence, at best this helps us with attestation, at worst it doesn't improve
security but certainly doesn't hurt it.
It turns out OverlayFS doesn't handle gracefully when the same source is
specified multiple times in lowerdir= and it fails with ELOOP:
Failed to mount overlay (type overlay) on /run/systemd/mount-rootfs/opt (MS_RDONLY "lowerdir=/run/systemd/unit-extensions/1/opt:/run/systemd/unit-extensions/0/opt:/run/systemd/mount-rootfs/opt"): Too many levels of symbolic links
This happens even if we mount each image in a different internal mount
path, as OverlayFS will resolve it and look for the backing device, which
will be the same device mapper entity, and return a hard error.
This error does not appear if dm-verity is not used, so it is very
confusing for users, and unnecessary.
When mounting ExtensionImages, check if an image is dm-veritied,
and drop duplicates if the root hashes match, to avoid this user-unfriendly
hard error.
This opens the door for making the call work without privileges: if we
pass in a userns fd and DissectedImage that has mount fds then we can
acquire all information without privs.
let's make userspace verity signature checking optional. This adds a
dissection flag to enable the logic and patches through all our users to
enable it by default, thus effectively not changing anything from the
status quo ante. However, know we have a knob to turn this off in
certain scenarios.
So far, if some component mounts a DDI in some local mount namespace we
created a temporary mountpoint in /tmp/ for that. Let's instead use the
same directory inode in /run/ instead. This is safe, since if everything
runs in a local mount namespace (with propagation on /run/ off) then
they shouldn't fight for the inode. And it relieves us from having to
clean up the directory after use. Morever, it allows us to run without
/tmp/ mounted.
This only moves dissect-image.c and the dissec tool over. More stuff is
moved over later.
The image name is extracted from the image path originally passed in,
i.e. not the contents of the image. And the image UUID is directly
retrieved from the partition table, hence also not from the contents.
Let's hence move the comment to separate out the stuff extract from the
file systems (and thus only available when mounting/with privs/with
block devices) from the data available without any of that.
Instead of mounting over, do an atomic swap using mount beneath, if
available. This way assets can be mounted again and again (e.g.:
updates) without leaking mounts.
The parsing is done after the image has been opened, not before, as it
cannot be done on an block device. Also fix returning on any error for
ExtensionDirectories, not just ENOENT.
Follow-up for 55ea4ef096
These tests are already done at two places, let's unify them in one
place, and tweak them slightly (specifically: require for considering a
DDI bootable in UEFI we also need need an init system inside).
This reworks the image discovery logic, and conceptually allows DDIs
that are both confext and sysext to exist. Previously we'd only extract
one type of exension data from a DDI, with this we allow to extract both
if both exist.
This doesn't add support for true "multi-modal" DDIs, that qualify as
various things at once, it just lays some ground work that ensures we at
least can dissect such images.
This reworks 484d26dac1 quite a bit.
This changes systemd-dissect's JSON output, but given the
version with the fields it changes/dops has never been released (as the
above patch was merged post-v254) this shouldn't be an issue.
This adds support for the new fsmount() logic of the kernel: we'll first
create an unattached fsmount fd, and then in a second step attach this
to some real file system inode – as opposed to attaching file system
directly. The benefit of this is that we can pass the open fsmount fds
over some sockets if need be, to isolate the mounting code from the
attaching code.
E.g. in logs on jammy-ppc64el in https://github.com/systemd/systemd/pull/27294:
Apr 16 17:42:50 H systemd-gpt-auto-generator[300]: Failed to dissect partition table of block device /dev/sda: No message of desired type
Apr 16 17:42:50 H (sd-execu[295]: /usr/lib/systemd/system-generators/systemd-gpt-auto-generator failed with exit status 1.
ee0e6e476e made this particular condition not an
error. But for other errnos we want to print a better message too.
dissect_loop_device_and_warn() already does this, but it always prints the
error at error level. We want to suppress some of the errors, so let's make the
print helper public and do the error suppression in the caller.
If we don't find a single useful partition table, refusing dissection.
(Except in systemd-dissect, when we are supposed to show DDI
information, in that case allow this to run and show general DDI
information, i.e. size, UUID and name at least)
This is to dissect_image_file() what dissect_loop_device_and_warn() is
to dissect_loop_device(), i.e. it dissects the image file and logs an
error string if that fails instead of just returning an error.
When we mount a DDI, let's set MS_NOSYMFOLLOW for ESP/XBOOTLDR. They are
generally untrusted territory, (i.e. outside of
encryption/authentication via dm-crypt/dm-verity). Moreover they are
generally FAT, where symlinks don't exist anyway. Let's hence disable
symlinks for them.
This slightly refactors how we put together mount options for mounts,
splitting this out into a new helper call
dissected_partition_pick_options(), which we should be able to reuse
later in gpt-auto-generator, to ensure mounts via loopback as DDI and
those on bare metal get the same options.
When we operate with DDIs with sector sizes != 512 we need to configure
the loopback device to match it, otherwise the image and the kernel
block device will disagree what things are.
Let's add a prober that tries to determine the sector size of a GPT DDI.
It does this by looking for the GPT partition table header at the
various byte offsets they must be located on, given a specific sector
size. It will try sector size 512, 1024, 2048 and 4096. Of these only
the 512 and 4096 really make sense IRL I guess, but let's be thorough.
This is useful to make the dissection logic at boot a bit safer, as we
can reference device nodes by diskseq.
This locks down dissection a bit, since it makes it harder to swap out
the backing device between the time we dissected and validated it, until
we actually mounted it.
This is not complete though, as /bin/mount would have to verify the
diskseq after opening the diskseq symlink again.
See: https://github.com/util-linux/util-linux/issues/1786
When we dissect images automatically, let's be a bit more conservative
with the file system types we are willing to mount: only mount common
file systems automatically.
Explicit mounts requested by admins should always be OK, but when we do
automatic mounts, let's not permit barely maintained, possibly legacy
file systems.
The list for now covers the four common writable and two common
read-only file systems. Sooner or later we might want to add more to the
list.
Also, it might make sense to eventually make this configurable via the
image dissection policy logic.
-1 was used everywhere, but -EBADF or -EBADFD started being used in various
places. Let's make things consistent in the new style.
Note that there are two candidates:
EBADF 9 Bad file descriptor
EBADFD 77 File descriptor in bad state
Since we're initializating the fd, we're just assigning a value that means
"no fd yet", so it's just a bad file descriptor, and the first errno fits
better. If instead we had a valid file descriptor that became invalid because
of some operation or state change, the other errno would fit better.
In some places, initialization is dropped if unnecessary.
This function checks if the external verity data referenced in
VeritySettings covers the specified partition (indicated via
designator).
Right now, we'll use that at one place, but in a later commit in more.
Let's store the GPT partition flags in the dissected partition info.
Right now we won't actually use them for anything yet, but later we'll
add that, when enforcing policy on dissection.
let's make sure we can probe file systems also when unprivileged:
instead of probing the partition block devices for file system
signatures, let's go via the original "whole" fd.
libblkid makes this easy actually, as it allows us to specify the
offset/size of the area to probe. And we have the partition
offsets/sizes anyway, so it's trivial for us to make use of.
This thus enables fs probing also when lacking privs and operating on
naked regular files without loopback devices or anything like this.
Curently, these two flags were implied by dissect_loop_device(), but
that's not right, because this means systemd-gpt-auto-generator will
dissect the root block device with these flags set and that's not
desirable: the generator should not cause the partition devices to be
created (we don't intend to use them right-away after all, but expect
udev to find/probe them first, and then mount them though .mount units).
And there's no point in opening the partition devices, since we do not
intend to mount them via fds either.
Hence, rework this: instead of implying the flags, specify them
explicitly.
While we are at it, let's also rename the flags to make them more
descriptive:
DISSECT_IMAGE_MANAGE_PARTITION_DEVICES becomes
DISSECT_IMAGE_ADD_PARTITION_DEVICES, since that's really all this does:
add the partition devices via BLKPG.
DISSECT_IMAGE_OPEN_PARTITION_DEVICES becomes
DISSECT_IMAGE_PIN_PARTITION_DEVICES, since we not only open the devices,
but keep the devices open continously (i.e. we "pin" them).
Also, drop the DISSECT_IMAGE_BLOCK_DEVICE combination flag, since it is
misleading, i.e. it suggests it was appropriate to specify on all
dissected blocking devices, but that's precisely not the case, see the
systemd-gpt-auto-generator case. My guess is that the confusion around
this was actually the cause for this bug we are addressing here.
Fixes: #25528