]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
repart: Add fixate_volume_key crypttab option generation
authorVitaly Kuznetsov <vkuznets@redhat.com>
Wed, 14 Jan 2026 08:51:33 +0000 (09:51 +0100)
committerVitaly Kuznetsov <vkuznets@redhat.com>
Mon, 19 Jan 2026 16:50:25 +0000 (17:50 +0100)
Add an option to capture the expected LUKS volume key hash and
record it to the generated crypttab.

man/crypttab.xml
man/repart.d.xml
src/repart/repart.c

index dbbeb3fd5bf00eccd8f9076dd4d8a85c1afd7dd7..f0d0ead947cd255d5c4d4c3afc42794d15659a88 100644 (file)
         if it has a different one. The expected hash matches the digest which is measured to the sha256 PCR bank of the
         TPM2 when <option>tpm2-measure-pcr=</option> is used.</para>
 
+        <para>For newly created LUKS volumes, the expected hash can be generated by
+        <citerefentry><refentrytitle>systemd-repart</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
+        For additional details, see the <option>EncryptedVolume=</option> description at
+        <citerefentry><refentrytitle>repart.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
+
         <xi:include href="version-info.xml" xpointer="v260"/>
         </listitem>
       </varlistentry>
@@ -1136,6 +1141,8 @@ external   /dev/sda3       keyfile:LABEL=keydev keyfile-timeout=10s,cipher=xchac
       <member><citerefentry><refentrytitle>systemd-cryptsetup@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
       <member><citerefentry><refentrytitle>systemd-cryptsetup-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
       <member><citerefentry><refentrytitle>systemd-cryptenroll</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
+      <member><citerefentry><refentrytitle>systemd-repart</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
+      <member><citerefentry><refentrytitle>repart.d</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
       <member><citerefentry project='man-pages'><refentrytitle>fstab</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>mkswap</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
index 5d325577456cdf48af15269e80d9a0ddbff9b9f7..5cdf2b0f34689a0d4f07c42c29fbe45a60eee4bf 100644 (file)
         <term><varname>EncryptedVolume=</varname></term>
 
         <listitem><para>Specifies how the encrypted partition should be set up. Takes at least one and at most
-        three fields separated with a colon (<literal>:</literal>). The first field specifies the encrypted
+        four fields separated with a colon (<literal>:</literal>). The first field specifies the encrypted
         volume name under <filename>/dev/mapper/</filename>. If not specified, <literal>luks-UUID</literal>
         will be used where <literal>UUID</literal> is the LUKS UUID. The second field specifies the keyfile
         to use following the same format as specified in crypttab. The third field specifies a
-        comma-delimited list of crypttab options. These fields correspond to the first, third and fourth
+        comma-delimited list of crypttab options. These three fields correspond to the first, third and fourth
         column of the
         <citerefentry><refentrytitle>crypttab</refentrytitle><manvolnum>5</manvolnum></citerefentry> format.
+        The fourth field contains extra options, which are not directly reflected to crypttab options. Currently,
+        the only supported extra option is <varname>fixate-volume-key</varname>. When specified, the volume key
+        hash will be taken upon LUKS volume formatting and the result will be added to the list of crypttab options,
+        i.e. <varname>fixate-volume-key=expected-hash</varname> will be added to the fourth column of the generated
+        crypttab.
         </para>
 
         <para>Note that this setting is only taken into account when <option>--generate-crypttab=</option>
index 7c19f1028775b4d9673197e7383b382fa2143657..48e05d3769ce71ae9e143060369ef6520249fc0c 100644 (file)
@@ -318,6 +318,7 @@ typedef struct PartitionEncryptedVolume {
         char *name;
         char *keyfile;
         char *options;
+        bool fixate_volume_key;
 } PartitionEncryptedVolume;
 
 static PartitionEncryptedVolume* partition_encrypted_volume_free(PartitionEncryptedVolume *c) {
@@ -2547,7 +2548,8 @@ static int config_parse_encrypted_volume(
                 void *data,
                 void *userdata) {
 
-        _cleanup_free_ char *volume = NULL, *keyfile = NULL, *options = NULL;
+        _cleanup_free_ char *volume = NULL, *keyfile = NULL, *options = NULL, *extra = NULL;
+        bool fixate_volume_key = false;
         Partition *p = ASSERT_PTR(data);
         int r;
 
@@ -2558,7 +2560,7 @@ static int config_parse_encrypted_volume(
 
         const char *q = rvalue;
         r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_DONT_COALESCE_SEPARATORS|EXTRACT_UNQUOTE,
-                               &volume, &keyfile, &options);
+                               &volume, &keyfile, &options, &extra);
         if (r == -ENOMEM)
                 return log_oom();
         if (r < 0) {
@@ -2589,10 +2591,29 @@ static int config_parse_encrypted_volume(
         if (!p->encrypted_volume)
                 return log_oom();
 
+        for (const char *e = extra;;) {
+                _cleanup_free_ char *word = NULL;
+
+                r = extract_first_word(&e, &word, ",", EXTRACT_DONT_COALESCE_SEPARATORS | EXTRACT_UNESCAPE_SEPARATORS);
+                if (r < 0) {
+                        log_syntax(unit, LOG_WARNING, filename, line, 0,
+                                   "Failed to parse extra options '%s', ignoring", word);
+                        break;
+                }
+                if (r == 0)
+                        break;
+
+                if (streq(word, "fixate-volume-key"))
+                        fixate_volume_key = true;
+                else
+                        log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown extra option '%s', ignoring", word);
+        }
+
         *p->encrypted_volume = (PartitionEncryptedVolume) {
                 .name = TAKE_PTR(volume),
                 .keyfile = TAKE_PTR(keyfile),
                 .options = TAKE_PTR(options),
+                .fixate_volume_key = fixate_volume_key,
         };
 
         return 0;
@@ -5174,6 +5195,44 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta
         if (r < 0)
                 return log_error_errno(r, "Failed to LUKS2 format future partition: %m");
 
+        if (p->encrypted_volume && p->encrypted_volume->fixate_volume_key) {
+                _cleanup_free_ char *key_id = NULL, *hash_option = NULL;
+
+                r = sym_crypt_get_volume_key_size(cd);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to determine volume key size: %m");
+                if (r == 0)
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Volume key has zero size and 'fixate-volume-key' is used");
+
+                _cleanup_(iovec_done) struct iovec vk = {
+                        .iov_base = malloc(r),
+                        .iov_len = r,
+                };
+
+                if (!vk.iov_base)
+                        return log_oom();
+
+                r = sym_crypt_volume_key_get(cd, CRYPT_ANY_SLOT, (char *) vk.iov_base, &vk.iov_len, NULL, 0);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to get volume key: %m");
+
+                r = cryptsetup_get_volume_key_id(
+                                cd,
+                                /* volume_name= */ p->encrypted_volume->name,
+                                /* volume_key= */ vk.iov_base,
+                                /* volume_key_size= */ vk.iov_len,
+                                /* ret= */ &key_id);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to get volume key hash: %m");
+
+                hash_option = strjoin("fixate-volume-key=", key_id);
+                if (!hash_option)
+                        return log_oom();
+
+                if (!strextend_with_separator(&p->encrypted_volume->options, ",", hash_option))
+                        return log_oom();
+        }
+
         if (IN_SET(p->encrypt, ENCRYPT_KEY_FILE, ENCRYPT_KEY_FILE_TPM2)) {
                 /* Use partition-specific key if available, otherwise fall back to global key */
                 struct iovec *iovec_key = arg_key.iov_base ? &arg_key : &p->key;