From f872373a26dcaa0818b49220abfe35611d12fa82 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 12 Mar 2025 09:49:52 +0100 Subject: [PATCH] generators: hook in validatefs on gpt-auto and fstab generator mounts Let's turn on validatefs automatically for all auto-discovered partitions. Let's add an x-systemd.validatefs option to optionally turn this on for fstab listed file systems. --- man/systemd-gpt-auto-generator.xml | 5 ++ man/systemd-validatefs@.service.xml | 8 +++ man/systemd.mount.xml | 16 ++++++ src/basic/special.h | 1 + src/fstab-generator/fstab-generator.c | 37 ++++++++---- src/gpt-auto-generator/gpt-auto-generator.c | 62 +++++++++++---------- src/shared/generator.c | 37 ++++++++++++ src/shared/generator.h | 4 ++ 8 files changed, 130 insertions(+), 40 deletions(-) diff --git a/man/systemd-gpt-auto-generator.xml b/man/systemd-gpt-auto-generator.xml index d3724e7e9dd..af450f9fbe6 100644 --- a/man/systemd-gpt-auto-generator.xml +++ b/man/systemd-gpt-auto-generator.xml @@ -281,6 +281,10 @@ automatically measured into PCR 15 on activation, via systemd-pcrfs@.service8. + Mount constraint metadata contained in the file systems is validated by pulling in + systemd-validatefs@.service8 + for generated mounts. + systemd-gpt-auto-generator implements systemd.generator7. @@ -375,6 +379,7 @@ systemd-fstab-generator8 systemd-cryptsetup@.service8 systemd-pcrfs@.service8 + systemd-validatefs@.service8 machine-id5 cryptsetup8 fstab5 diff --git a/man/systemd-validatefs@.service.xml b/man/systemd-validatefs@.service.xml index 6535ed463b8..b7c8adf43f3 100644 --- a/man/systemd-validatefs@.service.xml +++ b/man/systemd-validatefs@.service.xml @@ -58,6 +58,14 @@ GPT partition type UUID formatted as string. It is compared with the partition type UUID of the partition this file system is located on, and if different the validation will fail. + + The systemd-validatefs@.service unit is automatically pulled into the initial + transaction by + systemd-gpt-auto-generator8 + for all file systems it discovers and generates mounts + for. systemd-fstab-generator8 + will do this for all mounts with the mount option in + /etc/fstab. diff --git a/man/systemd.mount.xml b/man/systemd.mount.xml index 26bc116f975..c3d06d696aa 100644 --- a/man/systemd.mount.xml +++ b/man/systemd.mount.xml @@ -432,6 +432,22 @@ + + + + Validates mount constraint metadata of the mounted file system after mounting + it. This ensures the + systemd-validatefs@.service8 + service is pulled in by the mount unit. + + Note that this option can only be used in /etc/fstab, and will be ignored + when part of the Options= setting in a unit file. It is also implied for all + partitions discovered by + systemd-gpt-auto-generator8. + + + + diff --git a/src/basic/special.h b/src/basic/special.h index 166737a9ca8..a5cdfebae57 100644 --- a/src/basic/special.h +++ b/src/basic/special.h @@ -95,6 +95,7 @@ #define SPECIAL_GROWFS_ROOT_SERVICE "systemd-growfs-root.service" #define SPECIAL_PCRFS_SERVICE "systemd-pcrfs@.service" #define SPECIAL_PCRFS_ROOT_SERVICE "systemd-pcrfs-root.service" +#define SPECIAL_VALIDATEFS_SERVICE "systemd-validatefs@.service" #define SPECIAL_HIBERNATE_RESUME_SERVICE "systemd-hibernate-resume.service" /* Services systemd relies on */ diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c index 7e84be499d7..33c4c941f04 100644 --- a/src/fstab-generator/fstab-generator.c +++ b/src/fstab-generator/fstab-generator.c @@ -39,14 +39,15 @@ #include "volatile-util.h" typedef enum MountPointFlags { - MOUNT_NOAUTO = 1 << 0, - MOUNT_NOFAIL = 1 << 1, - MOUNT_AUTOMOUNT = 1 << 2, - MOUNT_MAKEFS = 1 << 3, - MOUNT_GROWFS = 1 << 4, - MOUNT_RW_ONLY = 1 << 5, - MOUNT_PCRFS = 1 << 6, - MOUNT_QUOTA = 1 << 7, + MOUNT_NOAUTO = 1 << 0, + MOUNT_NOFAIL = 1 << 1, + MOUNT_AUTOMOUNT = 1 << 2, + MOUNT_MAKEFS = 1 << 3, + MOUNT_GROWFS = 1 << 4, + MOUNT_RW_ONLY = 1 << 5, + MOUNT_PCRFS = 1 << 6, + MOUNT_QUOTA = 1 << 7, + MOUNT_VALIDATEFS = 1 << 8, } MountPointFlags; typedef struct Mount { @@ -249,9 +250,10 @@ static int add_swap( return true; } - log_debug("Found swap entry what=%s makefs=%s growfs=%s pcrfs=%s noauto=%s nofail=%s", + log_debug("Found swap entry what=%s makefs=%s growfs=%s pcrfs=%s validatefs=%s noauto=%s nofail=%s", what, - yes_no(flags & MOUNT_MAKEFS), yes_no(flags & MOUNT_GROWFS), yes_no(flags & MOUNT_PCRFS), + yes_no(flags & MOUNT_MAKEFS), yes_no(flags & MOUNT_GROWFS), + yes_no(flags & MOUNT_PCRFS), yes_no(flags & MOUNT_VALIDATEFS), yes_no(flags & MOUNT_NOAUTO), yes_no(flags & MOUNT_NOFAIL)); r = unit_name_from_path(what, ".swap", &name); @@ -304,6 +306,8 @@ static int add_swap( log_warning("%s: growing swap devices is currently unsupported.", what); if (flags & MOUNT_PCRFS) log_warning("%s: measuring swap devices is currently unsupported.", what); + if (flags & MOUNT_VALIDATEFS) + log_warning("%s: validating swap devices is currently unsupported.", what); if (!(flags & MOUNT_NOAUTO)) { r = generator_add_symlink(arg_dest, SPECIAL_SWAP_TARGET, @@ -688,6 +692,12 @@ static int add_mount( } } + if (flags & MOUNT_VALIDATEFS) { + r = generator_hook_up_validatefs(dest, where, target_unit); + if (r < 0) + return r; + } + if (flags & MOUNT_QUOTA) { r = generator_hook_up_quotacheck(dest, what, where, target_unit, fstype); if (r < 0) { @@ -842,6 +852,8 @@ static MountPointFlags fstab_options_to_flags(const char *options, bool is_swap) flags |= MOUNT_GROWFS; if (fstab_test_option(options, "x-systemd.pcrfs\0")) flags |= MOUNT_PCRFS; + if (fstab_test_option(options, "x-systemd.validatefs\0")) + flags |= MOUNT_VALIDATEFS; if (fstab_test_option(options, "usrquota\0" "grpquota\0" "quota\0" "usrjquota\0" "grpjquota\0" "prjquota\0")) flags |= MOUNT_QUOTA; if (fstab_test_yes_no_option(options, "noauto\0" "auto\0")) @@ -970,9 +982,10 @@ static int parse_fstab_one( free_and_replace(what, p); } - log_debug("Found entry what=%s where=%s type=%s makefs=%s growfs=%s pcrfs=%s noauto=%s nofail=%s", + log_debug("Found entry what=%s where=%s type=%s makefs=%s growfs=%s pcrfs=%s validatefs=%s noauto=%s nofail=%s", what, where, strna(fstype), - yes_no(flags & MOUNT_MAKEFS), yes_no(flags & MOUNT_GROWFS), yes_no(flags & MOUNT_PCRFS), + yes_no(flags & MOUNT_MAKEFS), yes_no(flags & MOUNT_GROWFS), + yes_no(flags & MOUNT_PCRFS), yes_no(flags & MOUNT_VALIDATEFS), yes_no(flags & MOUNT_NOAUTO), yes_no(flags & MOUNT_NOFAIL)); bool is_sysroot = in_initrd() && path_equal(where, "/sysroot"); diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c index 0addb5a82c9..54f674040be 100644 --- a/src/gpt-auto-generator/gpt-auto-generator.c +++ b/src/gpt-auto-generator/gpt-auto-generator.c @@ -38,6 +38,13 @@ #include "unit-name.h" #include "virt.h" +typedef enum MountPointFlags { + MOUNT_RW = 1 << 0, + MOUNT_GROWFS = 1 << 1, + MOUNT_MEASURE = 1 << 2, + MOUNT_VALIDATEFS = 1 << 3, +} MountPointFlags; + static const char *arg_dest = NULL; static bool arg_enabled = true; static GptAutoRoot arg_auto_root = _GPT_AUTO_ROOT_INVALID; @@ -57,9 +64,8 @@ static int add_cryptsetup( const char *id, const char *what, const char *mount_opts, - bool rw, + MountPointFlags flags, bool require, - bool measure, char **ret_device) { #if HAVE_LIBCRYPTSETUP @@ -97,7 +103,7 @@ static int add_cryptsetup( "After=%s\n", d, d); - if (!rw) { + if (!FLAGS_SET(flags, MOUNT_RW)) { options = strdup("read-only"); if (!options) return log_oom(); @@ -109,7 +115,7 @@ static int add_cryptsetup( if (!strextend_with_separator(&options, ",", "tpm2-device=auto")) return log_oom(); - if (measure) { + if (FLAGS_SET(flags, MOUNT_MEASURE)) { /* We only measure the root volume key into PCR 15 if we are booted with sd-stub (i.e. in a * UKI), and sd-stub measured the UKI. We do this in order not to step into people's own PCR * assignment, under the assumption that people who are fine to use sd-stub with its PCR @@ -178,9 +184,7 @@ static int add_mount( const char *what, const char *where, const char *fstype, - bool rw, - bool growfs, - bool measure, + MountPointFlags flags, const char *options, const char *description, const char *post) { @@ -203,7 +207,7 @@ static int add_mount( if (streq_ptr(fstype, "crypto_LUKS")) { /* Mount options passed are determined by partition_pick_mount_options(), whose result * is known to not contain timeout options. */ - r = add_cryptsetup(id, what, /* mount_opts = */ NULL, rw, /* require= */ true, measure, &crypto_what); + r = add_cryptsetup(id, what, /* mount_opts = */ NULL, flags, /* require= */ true, &crypto_what); if (r < 0) return r; @@ -270,13 +274,19 @@ static int add_mount( if (r < 0) return log_error_errno(r, "Failed to write unit %s: %m", unit); - if (growfs) { + if (FLAGS_SET(flags, MOUNT_VALIDATEFS)) { + r = generator_hook_up_validatefs(arg_dest, where, post); + if (r < 0) + return r; + } + + if (FLAGS_SET(flags, MOUNT_GROWFS)) { r = generator_hook_up_growfs(arg_dest, where, post); if (r < 0) return r; } - if (measure) { + if (FLAGS_SET(flags, MOUNT_MEASURE)) { r = generator_hook_up_pcrfs(arg_dest, where, post); if (r < 0) return r; @@ -353,9 +363,10 @@ static int add_partition_mount( p->node, where, p->fstype, - p->rw, - p->growfs, - /* measure= */ STR_IN_SET(id, "root", "var"), /* by default measure rootfs and /var, since they contain the "identity" of the system */ + (p->rw ? MOUNT_RW : 0) | + MOUNT_VALIDATEFS | + (p->growfs ? MOUNT_GROWFS : 0) | + (STR_IN_SET(id, "root", "var") ? MOUNT_MEASURE : 0), /* by default measure rootfs and /var, since they contain the "identity" of the system */ options, description, SPECIAL_LOCAL_FS_TARGET); @@ -383,7 +394,7 @@ static int add_partition_swap(DissectedPartition *p) { } if (streq_ptr(p->fstype, "crypto_LUKS")) { - r = add_cryptsetup("swap", p->node, /* mount_opts = */ NULL, /* rw= */ true, /* require= */ true, /* measure= */ false, &crypto_what); + r = add_cryptsetup("swap", p->node, /* mount_opts = */ NULL, MOUNT_RW, /* require= */ true, &crypto_what); if (r < 0) return r; what = crypto_what; @@ -427,8 +438,7 @@ static int add_automount( const char *what, const char *where, const char *fstype, - bool rw, - bool growfs, + MountPointFlags flags, const char *options, const char *description, usec_t timeout) { @@ -445,12 +455,10 @@ static int add_automount( what, where, fstype, - rw, - growfs, - /* measure= */ false, + flags, options, description, - NULL); + /* post= */ NULL); if (r < 0) return r; @@ -508,8 +516,7 @@ static int add_partition_xbootldr(DissectedPartition *p) { p->node, "/boot", p->fstype, - /* rw= */ true, - /* growfs= */ false, + MOUNT_RW, options, "Boot Loader Partition", LOADER_PARTITION_IDLE_USEC); @@ -571,8 +578,7 @@ static int add_partition_esp(DissectedPartition *p, bool has_xbootldr) { p->node, esp_path, p->fstype, - /* rw= */ true, - /* growfs= */ false, + MOUNT_RW, options, "EFI System Partition Automount", LOADER_PARTITION_IDLE_USEC); @@ -665,7 +671,7 @@ static int add_root_cryptsetup(void) { bdev = "/dev/gpt-auto-root-luks-ignore-factory-reset"; } - return add_cryptsetup("root", bdev, arg_root_options, /* rw= */ true, /* require= */ false, /* measure= */ true, NULL); + return add_cryptsetup("root", bdev, arg_root_options, MOUNT_RW|MOUNT_MEASURE, /* require= */ false, NULL); #else return 0; #endif @@ -752,9 +758,9 @@ static int add_root_mount(void) { bdev, in_initrd() ? "/sysroot" : "/", arg_root_fstype, - /* rw= */ arg_root_rw > 0, - /* growfs= */ false, - /* measure= */ true, + (arg_root_rw > 0 ? MOUNT_RW : 0) | + (in_initrd() ? MOUNT_VALIDATEFS : 0) | + MOUNT_MEASURE, options, "Root Partition", in_initrd() ? SPECIAL_INITRD_ROOT_FS_TARGET : SPECIAL_LOCAL_FS_TARGET); diff --git a/src/shared/generator.c b/src/shared/generator.c index 0bec2a2a277..70fcd6e935d 100644 --- a/src/shared/generator.c +++ b/src/shared/generator.c @@ -764,6 +764,43 @@ int generator_hook_up_pcrfs( return generator_add_symlink_full(dir, where_unit, "wants", pcrfs_unit_path, instance); } +int generator_hook_up_validatefs( + const char *dir, + const char *where, + const char *target) { + + _cleanup_free_ char *where_unit = NULL, *instance = NULL; + const char *validatefs_unit, *validatefs_unit_path; + int r; + + assert(dir); + assert(where); + + /* never hook this in for the actual root fs, because it's too late then, we already are running from + * the root fs, it makes no sense to validate it anymore */ + if (empty_or_root(where)) + return 0; + + r = unit_name_from_path(where, ".mount", &where_unit); + if (r < 0) + return log_error_errno(r, "Failed to make unit name from path '%s': %m", where); + + validatefs_unit = SPECIAL_VALIDATEFS_SERVICE; + validatefs_unit_path = SYSTEM_DATA_UNIT_DIR "/" SPECIAL_VALIDATEFS_SERVICE; + + r = unit_name_path_escape(where, &instance); + if (r < 0) + return log_error_errno(r, "Failed to escape path '%s': %m", where); + + if (target) { + r = generator_add_ordering(dir, target, "After", validatefs_unit, instance); + if (r < 0) + return r; + } + + return generator_add_symlink_full(dir, where_unit, "wants", validatefs_unit_path, instance); +} + int generator_hook_up_quotacheck( const char *dir, const char *what, diff --git a/src/shared/generator.h b/src/shared/generator.h index 4738fe90afc..47cccd9c592 100644 --- a/src/shared/generator.h +++ b/src/shared/generator.h @@ -67,6 +67,10 @@ int generator_hook_up_pcrfs( const char *dir, const char *where, const char *target); +int generator_hook_up_validatefs( + const char *dir, + const char *where, + const char *target); int generator_hook_up_quotacheck( const char *dir, const char *what, -- 2.47.3