From 0a03092cb5104b3aa39c2206994faf43011beeea Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 13 Nov 2025 10:09:50 +0100 Subject: [PATCH 1/4] repart: avoid label string clashes between LUKS superblocks and the filesystems on them Let's make sure that by default /dev/disk/by-label/ symlinks avoid ambiguities, and the LUKS volume carries a different one than the file system inside it. Alternative-to: #39536 --- man/repart.d.xml | 11 ++++++++ src/repart/repart.c | 62 +++++++++++++++++++++++++++++++++------------ 2 files changed, 57 insertions(+), 16 deletions(-) diff --git a/man/repart.d.xml b/man/repart.d.xml index 486fea96b9..22d50e2c63 100644 --- a/man/repart.d.xml +++ b/man/repart.d.xml @@ -256,6 +256,17 @@ + + VolumeLabel= + + The textual label to assign to the LUKS superblock if applicable. If not specified + defaults to the same string as the partition label (see Label= above), however + prefixed with luks-. This setting has no effect if encryption is not enabled for + this partition. + + + + UUID= diff --git a/src/repart/repart.c b/src/repart/repart.c index d0087771d4..46ab021625 100644 --- a/src/repart/repart.c +++ b/src/repart/repart.c @@ -398,7 +398,8 @@ typedef struct Partition { GptPartitionType type; sd_id128_t current_uuid, new_uuid; bool new_uuid_is_set; - char *current_label, *new_label; + char *current_label, *new_label; /* Used for the GPT partition label + fs superblock label */ + char *new_volume_label; /* used for LUKS superblock */ sd_id128_t fs_uuid, luks_uuid, verity_uuid; uint8_t verity_salt[SHA256_DIGEST_SIZE]; @@ -714,6 +715,7 @@ static Partition* partition_free(Partition *p) { free(p->current_label); free(p->new_label); + free(p->new_volume_label); free(p->definition_path); strv_free(p->drop_in_files); @@ -2750,6 +2752,7 @@ static int partition_read_definition( ConfigTableItem table[] = { { "Partition", "Type", config_parse_type, 0, &p->type }, { "Partition", "Label", config_parse_label, 0, &p->new_label }, + { "Partition", "VolumeLabel", config_parse_label, 0, &p->new_volume_label }, { "Partition", "UUID", config_parse_uuid, 0, p }, { "Partition", "Priority", config_parse_int32, 0, &p->priority }, { "Partition", "Weight", config_parse_weight, 0, &p->weight }, @@ -3976,6 +3979,27 @@ static const char *partition_label(const Partition *p) { return gpt_partition_type_uuid_to_string(p->type.uuid); } +static int volume_label(const Partition *p, char **ret) { + assert(p); + assert(ret); + + if (p->new_volume_label) + return strdup_to(ret, p->new_volume_label); + + const char *e = partition_label(p); + if (!e) + return -ENODATA; + + /* Let's prefix "luks-" for the label string used for LUKS superblocks. We do this so that the + * /dev/disk/by-label/ symlink to the LUKS volume and the file system inside it do not clash */ + char *j = strjoin("luks-", e); + if (!j) + return -ENOMEM; + + *ret = j; + return 0; +} + static int context_dump_partitions(Context *context) { _cleanup_(table_unrefp) Table *t = NULL; uint64_t sum_padding = 0, sum_size = 0; @@ -4966,21 +4990,6 @@ static int partition_target_sync(Context *context, Partition *p, PartitionTarget static int partition_encrypt(Context *context, Partition *p, PartitionTarget *target, bool offline) { #if HAVE_LIBCRYPTSETUP - const char *node = partition_target_path(target); - struct crypt_params_luks2 luks_params = { - .label = strempty(ASSERT_PTR(p)->new_label), - .sector_size = partition_fs_sector_size(context, p), - .data_device = offline ? node : NULL, - }; - struct crypt_params_reencrypt reencrypt_params = { - .mode = CRYPT_REENCRYPT_ENCRYPT, - .direction = CRYPT_REENCRYPT_BACKWARD, - .resilience = "datashift", - .data_shift = LUKS2_METADATA_SIZE / 512, - .luks2 = &luks_params, - .flags = CRYPT_REENCRYPT_INITIALIZE_ONLY|CRYPT_REENCRYPT_MOVE_FIRST_SEGMENT, - }; - _cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL; #if HAVE_TPM2 _cleanup_(erase_and_freep) char *base64_encoded = NULL; #endif @@ -5001,6 +5010,26 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta log_info("Encrypting future partition %" PRIu64 "...", p->partno); + _cleanup_free_ char *vl = NULL; + r = volume_label(p, &vl); + if (r < 0) + return log_error_errno(r, "Failed to generate volume label: %m"); + + const char *node = partition_target_path(target); + struct crypt_params_luks2 luks_params = { + .label = vl, + .sector_size = partition_fs_sector_size(context, p), + .data_device = offline ? node : NULL, + }; + struct crypt_params_reencrypt reencrypt_params = { + .mode = CRYPT_REENCRYPT_ENCRYPT, + .direction = CRYPT_REENCRYPT_BACKWARD, + .resilience = "datashift", + .data_shift = LUKS2_METADATA_SIZE / 512, + .luks2 = &luks_params, + .flags = CRYPT_REENCRYPT_INITIALIZE_ONLY|CRYPT_REENCRYPT_MOVE_FIRST_SEGMENT, + }; + if (offline) { r = var_tmp_dir(&vt); if (r < 0) @@ -5023,6 +5052,7 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta return log_oom(); } + _cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL; r = sym_crypt_init(&cd, offline ? hp : node); if (r < 0) return log_error_errno(r, "Failed to allocate libcryptsetup context for %s: %m", hp); From d343a044a26d6ca4771187a8ad1bd7059c82531b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 13 Nov 2025 10:11:00 +0100 Subject: [PATCH 2/4] NEWS: mention the LUKS superblock default labelling change. This is a (weak) compat break, hence document it in NEWS. --- NEWS | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/NEWS b/NEWS index b0cc3618b2..db6645e5ca 100644 --- a/NEWS +++ b/NEWS @@ -49,6 +49,15 @@ CHANGES WITH 259 in spe: image. Hence, effectively they have been read-only anyway already, this change makes this official. + * The LUKS volume label string set by systemd-repart no longer defaults + to the literal same as the partition and file system label, but is + prefixed with "luks-". This is done so that on LUKS enabled images a + conflict between /dev/disk/by-label/ symlinks is removed, as this + symlink is generated both for file system and LUKS superblock + labels. There's a new VolumeLabel= setting for partitions that can be + used to expicitly choose a LUKS superblock label, which can be used + to explicitly revert to the old naming, if required. + Service manager/PID1: * The service manager will now show the wallclock time a service ran From e30738c20c0651b2d4222f551047dea93c800f33 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 17 Nov 2025 16:42:58 +0100 Subject: [PATCH 3/4] dissect-tool: accept encryption password via $PASSWORD --- docs/ENVIRONMENT.md | 4 ++-- src/dissect/dissect.c | 9 ++++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/docs/ENVIRONMENT.md b/docs/ENVIRONMENT.md index 5732c217aa..f2a7d25edb 100644 --- a/docs/ENVIRONMENT.md +++ b/docs/ENVIRONMENT.md @@ -578,8 +578,8 @@ disk images with `--image=` or similar: environment variable to the build directory and you are set. This variable is only supported when systemd is compiled in developer mode. -Various tools that read passwords from the TTY, such as `systemd-cryptenroll` -and `homectl`: +Various tools that read passwords from the TTY, such as `systemd-cryptenroll`, +`systemd-dissect` and `homectl`: * `$PASSWORD` — takes a string: the literal password to use. If this environment variable is set it is used as password instead of prompting the diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c index 1a8fa52fce..d244822c13 100644 --- a/src/dissect/dissect.c +++ b/src/dissect/dissect.c @@ -2152,8 +2152,15 @@ static int run(int argc, char *argv[]) { return log_error_errno(r, "Failed to guess verity root hash: %m"); if (arg_action != ACTION_DISSECT) { + _cleanup_(erase_and_freep) char *envpw = NULL; + + r = getenv_steal_erase("PASSWORD", &envpw); + if (r < 0) + return log_error_errno(r, "Failed to acquire password from environment: %m"); + r = dissected_image_decrypt_interactively( - m, NULL, + m, + envpw, &arg_verity_settings, arg_image_policy, arg_flags); From 0d24626631a54a599631284b454c761bb7697abe Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 17 Nov 2025 16:43:21 +0100 Subject: [PATCH 4/4] test: add testcase for VolumeLabel= --- test/units/TEST-58-REPART.sh | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/test/units/TEST-58-REPART.sh b/test/units/TEST-58-REPART.sh index 251c337425..6ae6ec5a29 100755 --- a/test/units/TEST-58-REPART.sh +++ b/test/units/TEST-58-REPART.sh @@ -123,6 +123,7 @@ last-lba: 2097118" tee "$defs/root.conf" </dev/null umount "$imgs/mount" + + # Validate that the VolumeLabel= had the desired effect + PASSWORD="" systemd-dissect "$imgs/zzz" -M "$imgs/mount" + udevadm info /dev/disk/by-label/schrupfel | grep -q ID_FS_TYPE=crypto_LUKS + systemd-dissect -U "$imgs/mount" } testcase_dropin() {