From 94c1683663525b76a50bfaebd8ee9336231d7c4f Mon Sep 17 00:00:00 2001 From: Jarrod Johnson Date: Thu, 6 Aug 2026 16:07:19 -0400 Subject: [PATCH] Add support for specifying tpm2 pcrs in the encryptboot attribute This allows a user to opt into pcrs if they understand what they are doing. Some PCRs are sensitive to firmware updates and some are sensitive to boot loader, kernel, boot config, or initramfs. All of these are an opportunity for an unsuspecting update to remove access to the boot volume. There are update processes that can be put into place to make this work, but it is up to the OS update process to address that, and OS update processes are likely not to address that at this time. --- .../el7/profiles/default/scripts/pre.sh | 2 +- .../el7/profiles/default/scripts/tpm_luks.sh | 14 +++++++++++++- .../el8/profiles/default/scripts/pre.sh | 3 ++- .../el8/profiles/default/scripts/tpm_luks.sh | 12 ++++++++++-- .../ubuntu22.04/profiles/default/scripts/post.sh | 11 ++++++++++- confluent_server/confluent/config/attributes.py | 2 +- confluent_server/confluent/messages.py | 15 ++++++++++----- 7 files changed, 47 insertions(+), 12 deletions(-) diff --git a/confluent_osdeploy/el7/profiles/default/scripts/pre.sh b/confluent_osdeploy/el7/profiles/default/scripts/pre.sh index 65b3da0e..2e28de23 100644 --- a/confluent_osdeploy/el7/profiles/default/scripts/pre.sh +++ b/confluent_osdeploy/el7/profiles/default/scripts/pre.sh @@ -60,7 +60,7 @@ done cryptboot=$(grep ^encryptboot: /etc/confluent/confluent.deploycfg | awk '{print $2}') LUKSPARTY='' touch /tmp/addonpackages -if [ "$cryptboot" == "tpm2" ]; then +if [ "$cryptboot" == "tpm2" ] || [ "${cryptboot#tpm2:}" != "$cryptboot" ]; then LUKSPARTY="--encrypted --passphrase=$(cat /etc/confluent/confluent.apikey)" echo $cryptboot >> /tmp/cryptboot fi diff --git a/confluent_osdeploy/el7/profiles/default/scripts/tpm_luks.sh b/confluent_osdeploy/el7/profiles/default/scripts/tpm_luks.sh index df9c857f..51d8d084 100644 --- a/confluent_osdeploy/el7/profiles/default/scripts/tpm_luks.sh +++ b/confluent_osdeploy/el7/profiles/default/scripts/tpm_luks.sh @@ -1,4 +1,16 @@ #!/bin/sh cryptdisk=$(blkid -t TYPE="crypto_LUKS"|sed -e s/:.*//) -clevis luks bind -f -d $cryptdisk -k - tpm2 '{}' < /etc/confluent/confluent.apikey + +pcrs="" +if [ -f /tmp/cryptboot ]; then + # syntax of /tmp/cryptboot is e.g. tpm2:pcrs=1,7 + pcrs=$(sed -e 's/^tpm2:pcrs=//' /tmp/cryptboot) +fi + +if [ -n "$pcrs" ]; then + clevispcrs=$(echo "$pcrs" | sed -e 's/,/, /g') + clevis luks bind -f -d $cryptdisk -k - tpm2 "{\"pcr_ids\":\"$clevispcrs\"}" < /etc/confluent/confluent.apikey +else + clevis luks bind -f -d $cryptdisk -k - tpm2 '{}' < /etc/confluent/confluent.apikey +fi cryptsetup luksRemoveKey $cryptdisk < /etc/confluent/confluent.apikey diff --git a/confluent_osdeploy/el8/profiles/default/scripts/pre.sh b/confluent_osdeploy/el8/profiles/default/scripts/pre.sh index 53d30208..ed656805 100644 --- a/confluent_osdeploy/el8/profiles/default/scripts/pre.sh +++ b/confluent_osdeploy/el8/profiles/default/scripts/pre.sh @@ -89,7 +89,8 @@ LUKSPARTY='' touch /tmp/cryptpkglist touch /tmp/pkglist touch /tmp/addonpackages -if [ "$cryptboot" == "tpm2" ]; then + +if [ "$cryptboot" == "tpm2" ] || [ "${cryptboot#tpm2:}" != "$cryptboot" ]; then lukspass=$(python3 /opt/confluent/bin/apiclient /confluent-api/self/profileprivate/pending/luks.key 2> /dev/null) if [ -z "$lukspass" ]; then lukspass=$(python3 -c 'import os;import base64;print(base64.b64encode(os.urandom(66)).decode())') diff --git a/confluent_osdeploy/el8/profiles/default/scripts/tpm_luks.sh b/confluent_osdeploy/el8/profiles/default/scripts/tpm_luks.sh index 19cf9630..a46304b5 100644 --- a/confluent_osdeploy/el8/profiles/default/scripts/tpm_luks.sh +++ b/confluent_osdeploy/el8/profiles/default/scripts/tpm_luks.sh @@ -1,11 +1,19 @@ #!/bin/sh cryptdisk=$(blkid -t TYPE="crypto_LUKS"|sed -e s/:.*//) + +pcrs=$(sed -n -e 's/.*tpm2:pcrs=\([0-9,]*\).*/\1/p' /tmp/cryptboot) + if [ -x /bin/systemd-cryptenroll ]; then - PASSWORD=$(cat /etc/confluent/luks.key) systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs="" $cryptdisk + PASSWORD=$(cat /etc/confluent/luks.key) systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs="$pcrs" $cryptdisk sed -e 's/ discard/ tpm2-device=auto,discard/' -i /etc/crypttab dracut -f else - clevis luks bind -f -d $cryptdisk -k - tpm2 '{}' < /etc/confluent/luks.key + if [ -n "$pcrs" ]; then + clevispcrs=$(echo "$pcrs" | sed -e 's/,/, /g') + clevis luks bind -f -d $cryptdisk -k - tpm2 "{\"pcr_ids\":\"$clevispcrs\"}" < /etc/confluent/luks.key + else + clevis luks bind -f -d $cryptdisk -k - tpm2 "{}" < /etc/confluent/luks.key + fi #cryptsetup luksRemoveKey $cryptdisk < /etc/confluent/confluent.apikey fi chmod 000 /etc/confluent/luks.key diff --git a/confluent_osdeploy/ubuntu22.04/profiles/default/scripts/post.sh b/confluent_osdeploy/ubuntu22.04/profiles/default/scripts/post.sh index 78e6b411..4b1d92f2 100755 --- a/confluent_osdeploy/ubuntu22.04/profiles/default/scripts/post.sh +++ b/confluent_osdeploy/ubuntu22.04/profiles/default/scripts/post.sh @@ -112,7 +112,16 @@ if [ -f /etc/confluent_lukspass ]; then chmod 000 /target/etc/confluent/luks.key lukspass=$(cat /etc/confluent_lukspass) chroot /target apt install libtss2-rc0 - PASSWORD=$lukspass chroot /target systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs="" $CRYPTTAB_SOURCE + + tpm2pcrs="" + encryptboot=$(grep ^encryptboot: /target/etc/confluent/confluent.deploycfg | sed -e 's/^encryptboot://' -e 's/ //g') + case "$encryptboot" in + *pcrs=*) + tpm2pcrs=$(echo "$encryptboot" | sed -e 's/.*pcrs=//' -e 's/:.*//') + ;; + esac + + PASSWORD=$lukspass chroot /target systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs="$tpm2pcrs" $CRYPTTAB_SOURCE fetch_remote systemdecrypt mv systemdecrypt /target/etc/initramfs-tools/scripts/local-top/systemdecrypt fetch_remote systemdecrypt-hook diff --git a/confluent_server/confluent/config/attributes.py b/confluent_server/confluent/config/attributes.py index 72ceaced..cb088302 100644 --- a/confluent_server/confluent/config/attributes.py +++ b/confluent_server/confluent/config/attributes.py @@ -122,7 +122,7 @@ node = { 'Trusted Platform Module is available to decrypt the ' 'volume. Note that versions earlier than 8.2 may malfunction ' 'at boot time if this feature is attempted, depending on configuration.'), - 'validvalues': ('tpm2', 'none', ''), + 'validvalues': ('tpm2', 'tpm2:*', 'none', ''), }, 'deployment.apiarmed': { 'description': ('Indicates whether the node authentication token interface ' diff --git a/confluent_server/confluent/messages.py b/confluent_server/confluent/messages.py index 9245d38a..88894e3e 100644 --- a/confluent_server/confluent/messages.py +++ b/confluent_server/confluent/messages.py @@ -19,6 +19,7 @@ # Things are defined here to 'encourage' developers to coordinate information # format. This is also how different data formats are supported import base64 +from fnmatch import fnmatch import os import confluent.exceptions as exc import confluent.config.configmanager as cfm @@ -874,11 +875,15 @@ class InputAttributes(ConfluentMessage): # use that as cue to put it into config as an expr nodeattr[attr] = {'expression': nodeattr[attr]} if validattrs and 'validvalues' in validattrs.get(attr, []): - if (nodeattr[attr] and - nodeattr[attr] not in validattrs[attr]['validvalues']): - raise exc.InvalidArgumentException( - 'Attribute {0} does not accept value {1} (valid values would be {2})'.format( - attr, nodeattr[attr], ','.join(validattrs[attr]['validvalues']))) + if nodeattr[attr]: + for validvalue in validattrs[attr]['validvalues']: + if nodeattr[attr] == validvalue or fnmatch( + nodeattr[attr], validvalue): + break + else: + raise exc.InvalidArgumentException( + 'Attribute {0} does not accept value {1} (valid values would be {2})'.format( + attr, nodeattr[attr], ','.join(validattrs[attr]['validvalues']))) elif validattrs and 'validlist' in validattrs.get(attr, []) and nodeattr[attr]: req = nodeattr[attr].split(',') for v in req: