Skip to content

Commit 22f4aaa

Browse files
committed
Use systemd-confext instead of custom /etc overlay mount
For A/B-updated /etc contents we used a custom overlay mount that provides the default files through a lowerdir loaded from /usr. Since then we upstreamed mutable systemd-confext support and now we can switch to it. This pulls in flatcar/init#138 and flatcar/bootengine#115 together with backported systemd patches that have opened or merged upstream PRs to fix --root= issues and add a refresh skip check to prevent boot disruptions due to the multiple daemon reloads and - more important - the missing atomic remount that would mean /etc is gone for a few milliseconds during boot. The skip logic works best with verity hashes and thus the default confext must be a verity extension image. User-provided confext don't work well yet unless they use verity due to the missing atomic remount and reliance on the skipping logic. We also need to look into stacking order and other mutabiliy settings. The backported systemd patches relate to the following upstream PRs: systemd/systemd#39843 for vpick-Don-t-use-openat-directly-but-resolve-symlinks discover-image-Follow-symlinks-in-a-given-root sysext-Use-correct-image-name-for-extension-release test-Add-tests-for-handling-symlinks-with-systemd-sy Note that the patch in the PR relies on 0859fe3f32774f1e0c787974cc252ff922a1b868 but the backport patch not. systemd/systemd#39980 for sysext-Create-mutable-directory-with-the-right-mode sysext-Skip-refresh-if-no-changes-are-found systemd/systemd#39991 for sysext-Get-verity-user-certs-from-given-root systemd/systemd#40063 for sysext-Fix-config-file-support-with-root which relies on systemd/systemd#38250 for man-sysext.conf-add-systemd-sysext-config-files sysext-introduce-global-config-file sysext-support-ImagePolicy-global-config-option Signed-off-by: Kai Lueke <kailuke@microsoft.com>
1 parent 7ae6fab commit 22f4aaa

16 files changed

+2691
-19
lines changed

build_library/build_image_util.sh

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -748,12 +748,35 @@ EOF
748748
if [[ $(sudo find "${root_fs_dir}/usr/share/flatcar/etc" -size +0 ! -type d 2>/dev/null | wc -l) -gt 0 ]]; then
749749
die "Unexpected non-empty files in ${root_fs_dir}/usr/share/flatcar/etc"
750750
fi
751+
# Some backwards-compat symlinks still use this folder as target,
752+
# we can't remove it yet
751753
sudo rm -rf "${root_fs_dir}/usr/share/flatcar/etc"
752754
sudo cp -a "${root_fs_dir}/etc" "${root_fs_dir}/usr/share/flatcar/etc"
753-
754-
# Remove the rootfs state as it should be recreated through the
755-
# tmpfiles and may not be present on updating machines. This
756-
# makes sure our tests cover the case of missing files in the
755+
# Now set up a default confext and enable it.
756+
# It's important to use dm-verity not only for stricter image policies
757+
# but also because it allows us the refresh to identify this image and
758+
# skip setting it up again in the final boot, which not only saves us
759+
# a daemon-reload during boot but also from /etc contents shortly
760+
# disappearing until systemd-sysext uses mount beneath for an atomic
761+
# remount. Instead of a temporary directory we first prepare it as
762+
# folder and then convert it to a DDI and remove the folder.
763+
sudo rm -rf "${root_fs_dir}/usr/lib/confexts/00-flatcar-default"
764+
sudo mkdir -p "${root_fs_dir}/usr/lib/confexts/00-flatcar-default"
765+
# Do a copy because we keep /etc for the flatcar (.tar) container and the developer container
766+
sudo cp -a "${root_fs_dir}/etc" "${root_fs_dir}/usr/lib/confexts/00-flatcar-default/etc"
767+
sudo mkdir -p "${root_fs_dir}/usr/lib/confexts/00-flatcar-default/etc/extension-release.d/"
768+
echo ID=_any | sudo tee "${root_fs_dir}/usr/lib/confexts/00-flatcar-default/etc/extension-release.d/extension-release.00-flatcar-default" > /dev/null
769+
sudo systemd-repart \
770+
--private-key="${SYSEXT_SIGNING_KEY_DIR}/sysexts.key" \
771+
--certificate="${SYSEXT_SIGNING_KEY_DIR}/sysexts.crt" \
772+
--make-ddi=confext \
773+
--copy-source="${root_fs_dir}/usr/lib/confexts/00-flatcar-default" \
774+
"${root_fs_dir}/usr/lib/confexts/00-flatcar-default.raw"
775+
sudo rm -rf "${root_fs_dir}/usr/lib/confexts/00-flatcar-default"
776+
777+
# Remove the rootfs state as it should be recreated through tmpfiles
778+
# (and for /etc we use a confext) and may not be present on updating machines.
779+
# This makes sure our tests cover the case of missing files in the
757780
# rootfs and don't rely on the new image. Not done for the developer
758781
# container.
759782
if [[ -n "${image_kernel}" ]]; then
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
- Switched `/etc/` from a custom overlayfs for A/B updates to using a systemd-confext extension providing the default contents by using systemd-confext in the mutable mode where `/etc/` gets used as upperdir [scripts#3555](https://github.com/flatcar/scripts/pull/3555)
2+
- Moved systemd-sysext image mounting into the initrd, so that system extensions can better define the behavior of the final system at boot without workarounds to apply settings late at boot. This means `.wants` symlinks for systemd units work as expected now and, therefore, we dropped the `ensure-sysext.service` workaround. We still recommend extensions to keep their workarounds, e.g., using `.upholds` instead of `.wants`, to better support live reloading. A skipping logic prevents an extension refresh late at boot but only if no changes were found. For extensions that are not stored on a custom filesystem, such as a separate `/var` partition, the new extension mounting from the initrd won't be able to load them early but they will be picked up late at boot through the extension refresh. This is another case where it's good if extensions keep workarounds for late loading.

sdk_container/src/third_party/coreos-overlay/coreos-base/coreos-init/coreos-init-9999.ebuild

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ EGIT_REPO_URI="https://github.com/flatcar/init.git"
88
if [[ "${PV}" == 9999 ]]; then
99
KEYWORDS="~amd64 ~arm ~arm64 ~x86"
1010
else
11-
EGIT_COMMIT="8bd8a82fb22bc46ea2cf7da94d58655e102ca26d" # flatcar-master
11+
#EGIT_COMMIT="8bd8a82fb22bc46ea2cf7da94d58655e102ca26d" # flatcar-master
12+
EGIT_BRANCH="kai/default-confext"
1213
KEYWORDS="amd64 arm arm64 x86"
1314
fi
1415

sdk_container/src/third_party/coreos-overlay/coreos/config/env/sys-apps/systemd

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -160,19 +160,6 @@ EOF
160160
-e '/^C!* \/etc\/pam\.d/d' \
161161
-e '/^C!* \/etc\/issue/d' || die
162162

163-
(
164-
# Some OEMs prefer chronyd, so allow them to replace
165-
# systemd-timesyncd with it.
166-
insinto "$(systemd_get_systemunitdir)/systemd-timesyncd.service.d"
167-
newins - flatcar.conf <<'EOF'
168-
# Allow sysexts to ship timesyncd replacements which can have
169-
# a Conflicts=systemd-timesyncd directive that would result
170-
# in systemd-timesyncd not being started.
171-
[Unit]
172-
After=ensure-sysext.service
173-
EOF
174-
)
175-
176163
(
177164
# Allow @mount syscalls for systemd-udevd.service
178165
insinto "$(systemd_get_systemunitdir)/systemd-udevd.service.d"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
From 6f4b065b626edd8a06ff0c8028173e060b5e444b Mon Sep 17 00:00:00 2001
2+
From: Kai Lueke <kailuke@microsoft.com>
3+
Date: Thu, 20 Nov 2025 23:43:55 +0900
4+
Subject: [PATCH 03/10] vpick: Don't use openat directly but resolve symlinks
5+
in given root
6+
7+
With systemd-sysext --root= all symlinks should be followed relative to
8+
the given root and direct openat usage doesn't work.
9+
Change the openat call to use the chase helper function to resolve the
10+
symlink in the given root.
11+
---
12+
src/shared/vpick.c | 4 ++--
13+
1 file changed, 2 insertions(+), 2 deletions(-)
14+
15+
diff --git a/src/shared/vpick.c b/src/shared/vpick.c
16+
index b1b2d93054..dfe58cafa5 100644
17+
--- a/src/shared/vpick.c
18+
+++ b/src/shared/vpick.c
19+
@@ -471,9 +471,9 @@ static int make_choice(
20+
if (!p)
21+
return log_oom_debug();
22+
23+
- object_fd = openat(dir_fd, best_filename, O_CLOEXEC|O_PATH);
24+
+ object_fd = chase_and_openat(toplevel_fd, p, CHASE_AT_RESOLVE_IN_ROOT, O_PATH|O_CLOEXEC, NULL);
25+
if (object_fd < 0)
26+
- return log_debug_errno(errno, "Failed to open '%s/%s': %m",
27+
+ return log_debug_errno(object_fd, "Failed to open '%s/%s': %m",
28+
empty_to_root(toplevel_path), skip_leading_slash(inode_path));
29+
30+
return pin_choice(
31+
--
32+
2.52.0
33+

0 commit comments

Comments
 (0)