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.
This commit is contained in:
Lennart Poettering
2025-03-12 09:49:52 +01:00
parent 0bdd5ccc81
commit f872373a26
8 changed files with 130 additions and 40 deletions

View File

@@ -281,6 +281,10 @@
automatically measured into PCR 15 on activation, via
<citerefentry><refentrytitle>systemd-pcrfs@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
<para>Mount constraint metadata contained in the file systems is validated by pulling in
<citerefentry><refentrytitle>systemd-validatefs@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
for generated mounts.</para>
<para><filename>systemd-gpt-auto-generator</filename> implements
<citerefentry><refentrytitle>systemd.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
</refsect1>
@@ -375,6 +379,7 @@
<member><citerefentry><refentrytitle>systemd-fstab-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd-cryptsetup@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd-pcrfs@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd-validatefs@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
<member><citerefentry project='die-net'><refentrytitle>cryptsetup</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><citerefentry project='man-pages'><refentrytitle>fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>

View File

@@ -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.</para></listitem>
</orderedlist>
<para>The <filename>systemd-validatefs@.service</filename> unit is automatically pulled into the initial
transaction by
<citerefentry><refentrytitle>systemd-gpt-auto-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
for all file systems it discovers and generates mounts
for. <citerefentry><refentrytitle>systemd-fstab-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
will do this for all mounts with the <option>x-systemd.validatefs</option> mount option in
<filename>/etc/fstab</filename>.</para>
</refsect1>
<refsect1>

View File

@@ -432,6 +432,22 @@
<xi:include href="version-info.xml" xpointer="v253"/></listitem>
</varlistentry>
<varlistentry>
<term><option>x-systemd.validatefs</option></term>
<listitem><para>Validates mount constraint metadata of the mounted file system after mounting
it. This ensures the
<citerefentry><refentrytitle>systemd-validatefs@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
service is pulled in by the mount unit.</para>
<para>Note that this option can only be used in <filename>/etc/fstab</filename>, and will be ignored
when part of the <varname>Options=</varname> setting in a unit file. It is also implied for all
partitions discovered by
<citerefentry><refentrytitle>systemd-gpt-auto-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
</varlistentry>
<varlistentry>
<term><option>x-systemd.rw-only</option></term>

View File

@@ -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 */

View File

@@ -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");

View File

@@ -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);

View File

@@ -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,

View File

@@ -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,