From 98d3bf5be6d4aaba36a889645211fb8897cc1b4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20L=C3=B6ser?= Date: Tue, 24 Oct 2023 15:08:14 +0200 Subject: [PATCH] Fixes #36885 - Add Clevis/Tang disk encryption template For disk encryption Clevis/Tang is often used. This commit introduces partition templates for Kickstart and Autoinstall taking care of disk encryption and a snippet responsible for binding the LUKS device via Clevis to a given Tang server. The default partition template encrypts the disk with a passphrase which can be provided via `disk_enc_passphrase` host parameter. If no host parameter is provided, the default passphrase is 'linux'. If, in addition, `disk_enc_tang_servers` host parameter is provided (can be one address as string or multiple addresses as array), the LUKS device will be bind to these Tang servers using Clevis. In this case, the passphrase will be removed. This commit targets the Red Hat family and Ubuntu operating system. --- .../kickstart_default_encrypted.erb | 18 ++++ .../preseed_default_autoinstall_encrypted.erb | 17 ++++ .../provision/kickstart_default.erb | 1 + .../snippet/disk_enc_clevis_tang.erb | 89 +++++++++++++++++++ ...reseed_autoinstall_clevis_tang_wrapper.erb | 18 ++++ .../preseed_autoinstall_cloud_init.erb | 5 +- ...it_user_data.ubuntu_autoinst4dhcp.snap.txt | 2 + 7 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 app/views/unattended/partition_tables_templates/kickstart_default_encrypted.erb create mode 100644 app/views/unattended/partition_tables_templates/preseed_default_autoinstall_encrypted.erb create mode 100644 app/views/unattended/provisioning_templates/snippet/disk_enc_clevis_tang.erb create mode 100644 app/views/unattended/provisioning_templates/snippet/preseed_autoinstall_clevis_tang_wrapper.erb diff --git a/app/views/unattended/partition_tables_templates/kickstart_default_encrypted.erb b/app/views/unattended/partition_tables_templates/kickstart_default_encrypted.erb new file mode 100644 index 00000000000..522d848125b --- /dev/null +++ b/app/views/unattended/partition_tables_templates/kickstart_default_encrypted.erb @@ -0,0 +1,18 @@ +<%# +kind: ptable +name: Kickstart default encrypted +model: Ptable +oses: +- AlmaLinux +- RedHat +- Rocky +-%> +<% if host_param('driverdisk_source') -%> +driverdisk --source=<%= host_param('driverdisk_source') %> +<% end -%> +<% if host_param('ignoredisk_options') -%> +ignoredisk <%= host_param('ignoredisk_options') %> +<% end -%> +zerombr +clearpart --all --initlabel +autopart --encrypted --passphrase="<%= host_param('disk_enc_passphrase', 'linux') %>" <%= host_param('autopart_options') %> diff --git a/app/views/unattended/partition_tables_templates/preseed_default_autoinstall_encrypted.erb b/app/views/unattended/partition_tables_templates/preseed_default_autoinstall_encrypted.erb new file mode 100644 index 00000000000..b75ccafbe71 --- /dev/null +++ b/app/views/unattended/partition_tables_templates/preseed_default_autoinstall_encrypted.erb @@ -0,0 +1,17 @@ +<%# +kind: ptable +name: Preseed default autoinstall encrypted +model: Ptable +description: | + Preseed Autoinstall default storage snippet configures drives automatically + with LVM and disk encryption. + Requires Ubuntu >= 22.04.3. + The snippet is automatically indented by 2 spaces. For reference: + https://ubuntu.com/server/docs/install/autoinstall-reference +oses: +- Ubuntu +%> +storage: + layout: + name: lvm + password: <%= host_param('disk_enc_passphrase', 'linux') %> diff --git a/app/views/unattended/provisioning_templates/provision/kickstart_default.erb b/app/views/unattended/provisioning_templates/provision/kickstart_default.erb index faee2687fc1..bddb63ea2fd 100644 --- a/app/views/unattended/provisioning_templates/provision/kickstart_default.erb +++ b/app/views/unattended/provisioning_templates/provision/kickstart_default.erb @@ -362,6 +362,7 @@ sed -e 's/DEFAULTKERNEL=kernel-uek/DEFAULTKERNEL=kernel/g' -i /etc/sysconfig/ker <%= snippet_if_exists(template_name + " custom post") %> <%= snippet 'insights' if host_param_true?('host_registration_insights') && os_major < 9 -%> +<%= snippet 'disk_enc_clevis_tang' if host_param('disk_enc_tang_servers') -%> touch /tmp/foreman_built 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 new file mode 100644 index 00000000000..c881b7790c3 --- /dev/null +++ b/app/views/unattended/provisioning_templates/snippet/disk_enc_clevis_tang.erb @@ -0,0 +1,89 @@ +<%# +kind: snippet +name: disk_enc_clevis_tang +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. + The temporary passphrase will be removed afterwards. Currently, only Red Hat + family and Ubuntu operating systems are supported. +-%> +<% + passphrase = host_param('disk_enc_passphrase', 'linux') + tang_server_list = [] + packages_redhat = "clevis clevis-luks clevis-systemd clevis-dracut" + packages_ubuntu = "clevis clevis-luks clevis-systemd clevis-initramfs" + + unless host_param('disk_enc_tang_servers').blank? + if host_param('disk_enc_tang_servers').is_a?(String) + tang_server_list = [host_param('disk_enc_tang_servers')] + else + tang_server_list = host_param('disk_enc_tang_servers') + end + end +-%> + +<% if (@host.operatingsystem.family == 'Redhat' || @host.operatingsystem.name == 'Ubuntu') && unless tang_server_list.blank? -%> + +cat > /tmp/rootdir-luks-device.sh << "EOF" +#!/bin/sh +# +# Author Jan Löser +# Published under the GNU Public Licence 3 +# +# This scripts tries to find the 1st LUKS device for / (root directory). +# +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 + set -e + slave=$(find /sys/class/block/$(basename $slavedev)/slaves -type l | head -n1) + slavedev=$(find /dev -name "$(basename $slave)" | head -n1) + set +e +done + +exit 1 +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" + +<% if @host.operatingsystem.family == 'Redhat' -%> + $PKG_MANAGER_INSTALL <%= packages_redhat %> +<% elsif @host.operatingsystem.name == 'Ubuntu' -%> + $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 + systemctl enable clevis-luks-askpass.path + systemctl enable remote-cryptsetup.target + +<% if @host.operatingsystem.family == 'Redhat' -%> + dracut --verbose --force --hostonly-cmdline --regenerate-all +<% elsif @host.operatingsystem.name == 'Ubuntu' -%> + update-initramfs -u -k 'all' +<% end -%> + +else + echo "No LUKS device found!" +fi + +<% end -%> 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 new file mode 100644 index 00000000000..a3ef18da895 --- /dev/null +++ b/app/views/unattended/provisioning_templates/snippet/preseed_autoinstall_clevis_tang_wrapper.erb @@ -0,0 +1,18 @@ +<%# +kind: snippet +name: preseed_autoinstall_clevis_tang_wrapper +model: ProvisioningTemplate +snippet: true +description: | + Wrapper snippet to set up Clevis/Tang disk encryption. + Requires Ubuntu >= 22.04.3. + The snippet is automatically indented by 2 spaces. For reference: + https://ubuntu.com/server/docs/install/autoinstall-reference +%> +- | + cat > /target/tmp/disk_enc_clevis_tang.sh <<"WRAPPER" + #!/bin/sh +<%= indent(2) { snippet 'disk_enc_clevis_tang' } %> + WRAPPER +- curtin in-target -- bash /tmp/disk_enc_clevis_tang.sh +- curtin in-target -- rm /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 40a55d54dca..8e82c737fe3 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 @@ -24,6 +24,8 @@ username_to_create = host_param('username_to_create', 'root') 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: @@ -67,9 +69,10 @@ autoinstall: allow-pw: true install-server: true updates: security -<%= indent(2) { @host.diskLayout } -%> +<%= 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 } %> - 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 diff --git a/test/unit/foreman/renderer/snapshots/ProvisioningTemplate/user_data/Preseed_Autoinstall_cloud-init_user_data.ubuntu_autoinst4dhcp.snap.txt b/test/unit/foreman/renderer/snapshots/ProvisioningTemplate/user_data/Preseed_Autoinstall_cloud-init_user_data.ubuntu_autoinst4dhcp.snap.txt index 0b1c9572c02..7425507a9be 100644 --- a/test/unit/foreman/renderer/snapshots/ProvisioningTemplate/user_data/Preseed_Autoinstall_cloud-init_user_data.ubuntu_autoinst4dhcp.snap.txt +++ b/test/unit/foreman/renderer/snapshots/ProvisioningTemplate/user_data/Preseed_Autoinstall_cloud-init_user_data.ubuntu_autoinst4dhcp.snap.txt @@ -31,7 +31,9 @@ autoinstall: storage: layout: name: lvm + late-commands: + - wget -Y off http://foreman.example.com/unattended/finish -O /target/tmp/finish.sh - curtin in-target -- chmod +x /tmp/finish.sh - curtin in-target -- /tmp/finish.sh