From d35a06d6a593ac8a3f01d1c0fdc18adfd604e163 Mon Sep 17 00:00:00 2001 From: Martin Spiessl <33026673+MartinSpiessl@users.noreply.github.com> Date: Thu, 8 Aug 2024 07:40:44 +0200 Subject: [PATCH] Fixes #37857 - Tang support multiple phys. volumes Often there is more than a single disk that gets encrypted during installation. When multiple disks are available and `autopart` is used in the kickstart config, all disks get encrypted and put into a volume group on which a logical volume for the root partition is created. Another example is when users add a separate disk for directories like /var to separate application data. This means we need to recursively resolve all underlying encrypted volumes, not just for the root partition. To identify these, we take all entries from /etc/crypttab, which should catch all block devices that were encrypted in the installation process. As this feature should also work for Ubuntu, we also address some bugs with the Ubuntu support: - disk_enc_clevis_tang.erb needs to work with dash (not just bash) - the check of the minor version in the autoinstall template is broken, recommendation is also to not rely on the minor version for this OS - PKG_MANAGER_INSTALL is missing in preseed_autoinstall_cloud_init.erb --- .../snippet/disk_enc_clevis_tang.erb | 72 +++++++++++-------- ...reseed_autoinstall_clevis_tang_wrapper.erb | 1 + .../preseed_autoinstall_cloud_init.erb | 3 +- 3 files changed, 45 insertions(+), 31 deletions(-) diff --git a/app/views/unattended/provisioning_templates/snippet/disk_enc_clevis_tang.erb b/app/views/unattended/provisioning_templates/snippet/disk_enc_clevis_tang.erb index 296dc9b7996..eeda1d4c8f0 100644 --- a/app/views/unattended/provisioning_templates/snippet/disk_enc_clevis_tang.erb +++ b/app/views/unattended/provisioning_templates/snippet/disk_enc_clevis_tang.erb @@ -5,7 +5,7 @@ model: ProvisioningTemplate snippet: true description: | Binds encrypted root directory ('/') utilizing Clevis to Tang server(s) for - decryption. The first parent device containing a LUKS container will be used. + decryption. All parent devices containing a LUKS container will be used. The temporary passphrase will be removed afterwards. Currently, only Red Hat family and Ubuntu operating systems are supported. -%> @@ -27,35 +27,47 @@ description: | <% if (@host.operatingsystem.family == 'Redhat' || @host.operatingsystem.name == 'Ubuntu') && tang_server_list.present? -%> cat > /tmp/rootdir-luks-device.sh << "EOF" -#!/bin/sh +#!/bin/bash # -# Author Jan Löser -# Published under the GNU Public Licence 3 +# SPDX-FileCopyrightText: 2024 Jan Löser +# SPDX-FileCopyrightText: 2024 Martin Spiessl +# SPDX-License-Identifier: GPL-3.0-or-later # -# This scripts tries to find the 1st LUKS device for / (root directory). +# This script tries to find all LUKS devices mounted in /etc/crypttab. # set -o pipefail -rootdev=$(df / --output=source | tail -n1) -targetdev=$(readlink -f $rootdev) -slavedev=$targetdev - -while : ; do - /sbin/cryptsetup luksDump $slavedev &>/dev/null && echo $slavedev && exit 0 +mapfile -t maybe_luks_devices < <( + awk '{print $1}' /etc/crypttab | \ + xargs -rn1 find /dev -name | \ + xargs -rn1 readlink -f +) +luks_devices=() +while [[ ${#maybe_luks_devices[@]} -gt 0 ]] ; do + # classify maybe_luks_devices into luks_devices and non_luks_devices + non_luks_devices=() + for dev in "${maybe_luks_devices[@]}" ; do + /sbin/cryptsetup luksDump "$dev" &>/dev/null && luks_devices+=("$dev") || non_luks_devices+=("$dev") + done + # resolve non_luks_devices to discover new maybe_luks_devices set -e - slave=$(find /sys/class/block/$(basename $slavedev)/slaves -type l | head -n1) - slavedev=$(find /dev -name "$(basename $slave)" | head -n1) + maybe_luks_devices=() + for dev in "${non_luks_devices[@]}" ; do + mapfile -t slaves < <(find "/sys/class/block/$(basename "$dev")/slaves" -type l) + for slave in "${slaves[@]}" ; do + slavedev=$(find /dev -name "$(basename "$slave")" | head -n1) + maybe_luks_devices+=("$slavedev") + done + done set +e done - -exit 1 +printf "%s\n" "${luks_devices[@]}" EOF # needs bash here because Ubuntu's sh (dash) doesn't support `-o pipefail` option -luksdev=$(bash /tmp/rootdir-luks-device.sh) - -if [[ -n "$luksdev" ]]; then - echo "LUKS device found for '/': $luksdev" +luksdevs=$(bash /tmp/rootdir-luks-device.sh) +if [ -n "$luksdevs" ]; then + echo "LUKS devices found for '/': $luksdevs" <% if @host.operatingsystem.family == 'Redhat' -%> $PKG_MANAGER_INSTALL <%= packages_redhat %> @@ -63,16 +75,18 @@ if [[ -n "$luksdev" ]]; then $PKG_MANAGER_INSTALL <%= packages_ubuntu %> <% end -%> -<% for tang_server in tang_server_list -%> - echo '<%= passphrase %>' | clevis luks bind -y -k - -d $luksdev tang '{"url": "<%= tang_server %>"}' - if [[ $? -ne 0 ]]; then - echo "---" - echo "There was an error during Clevis LUKS bind of '$luksdev' to Tang server '<%= tang_server %>'." - echo "System halted." - sleep infinity - fi -<% end -%> - echo '<%= passphrase %>' | cryptsetup luksRemoveKey $luksdev + for luksdev in $luksdevs; do + <% for tang_server in tang_server_list -%> + echo '<%= passphrase %>' | clevis luks bind -y -k - -d $luksdev tang '{"url": "<%= tang_server %>"}' + if [ $? -ne 0 ]; then + echo "---" + echo "There was an error during Clevis LUKS bind of '$luksdev' to Tang server '<%= tang_server %>'." + echo "System halted." + sleep infinity + fi + <% end -%> + echo '<%= passphrase %>' | cryptsetup luksRemoveKey $luksdev + done systemctl enable clevis-luks-askpass.path systemctl enable remote-cryptsetup.target diff --git a/app/views/unattended/provisioning_templates/snippet/preseed_autoinstall_clevis_tang_wrapper.erb b/app/views/unattended/provisioning_templates/snippet/preseed_autoinstall_clevis_tang_wrapper.erb index a3ef18da895..8e62c294905 100644 --- a/app/views/unattended/provisioning_templates/snippet/preseed_autoinstall_clevis_tang_wrapper.erb +++ b/app/views/unattended/provisioning_templates/snippet/preseed_autoinstall_clevis_tang_wrapper.erb @@ -12,6 +12,7 @@ description: | - | cat > /target/tmp/disk_enc_clevis_tang.sh <<"WRAPPER" #!/bin/sh +<%= indent(2) { snippet 'pkg_manager' } %> <%= indent(2) { snippet 'disk_enc_clevis_tang' } %> WRAPPER - curtin in-target -- bash /tmp/disk_enc_clevis_tang.sh diff --git a/app/views/unattended/provisioning_templates/user_data/preseed_autoinstall_cloud_init.erb b/app/views/unattended/provisioning_templates/user_data/preseed_autoinstall_cloud_init.erb index 8e82c737fe3..ca21f919b5d 100644 --- a/app/views/unattended/provisioning_templates/user_data/preseed_autoinstall_cloud_init.erb +++ b/app/views/unattended/provisioning_templates/user_data/preseed_autoinstall_cloud_init.erb @@ -25,7 +25,6 @@ realname_to_create = host_param('realname_to_create') || username_to_create password_to_create = host_param('password_to_create') || @host.root_pass enable_auto_update = (host_param_true?('package_upgrade') && !host_param('kt_activation_keys')) os_major = @host.operatingsystem.major.to_i -os_minor = @host.operatingsystem.minor.to_i -%> #cloud-config autoinstall: @@ -72,7 +71,7 @@ autoinstall: <%= indent(2) { @host.diskLayout } %> <%= indent(2) { snippet_if_exists(template_name + " custom root") } -%> late-commands: -<%= indent(2) { snippet 'preseed_autoinstall_clevis_tang_wrapper' if host_param('disk_enc_tang_servers') && os_major >= 22 && os_minor >= 3 } %> +<%= indent(2) { snippet 'preseed_autoinstall_clevis_tang_wrapper' if host_param('disk_enc_tang_servers') && os_major >= 22 } %> - wget -Y off <%= @static ? "'#{foreman_url('finish', static: 'true')}'" : foreman_url('finish') %> -O /target/tmp/finish.sh - curtin in-target -- chmod +x /tmp/finish.sh - curtin in-target -- /tmp/finish.sh