<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>secureboot — Sebastian Wiesner</title>
    <link>https://swsnr.writeas.com/tag:secureboot</link>
    <description>System engineer for satellite mission planning. Gnome. Rust. Arch.  </description>
    <pubDate>Thu, 12 Nov 2026 13:20:26 +0000</pubDate>
    <image>
      <url>https://i.snap.as/9knB2j11.jpg</url>
      <title>secureboot — Sebastian Wiesner</title>
      <link>https://swsnr.writeas.com/tag:secureboot</link>
    </image>
    <item>
      <title>Install Arch with Secure boot, TPM2-based LUKS encryption, and systemd-homed</title>
      <link>https://swsnr.writeas.com/install-arch-with-secure-boot-tpm2-based-luks-encryption-and-systemd-homed?pk_campaign=rss-feed</link>
      <description>#archlinux #systemd #secureboot #dracut #sbctl&#xA;&#xA;Discuss...&#xA;&#xA;Update: I no longer use dracut, and the corresponding part of this blog&#xA;post no longer reflects my setup.&#xA;&#xA;This article describes my Arch Linux setup which combines Secure Boot with custom keys, TPM2-based full disk encryption and systemd-homed into a fully encrypted and authenticated, yet convenient Linux system.&#xA;&#xA;This setup draws inspiration from Authenticated Boot and Disk Encryption on Linux and Unlocking LUKS2 volumes with TPM2, FIDO2, PKCS#11 Security Hardware on systemd 248 by Lennart Poettering, and combines my previous posts on these topics.</description>
      <content:encoded><![CDATA[<p><a href="https://swsnr.writeas.com/tag:archlinux" class="hashtag" rel="nofollow ugc"><span>#</span><span class="p-category">archlinux</span></a> <a href="https://swsnr.writeas.com/tag:systemd" class="hashtag" rel="nofollow ugc"><span>#</span><span class="p-category">systemd</span></a> <a href="https://swsnr.writeas.com/tag:secureboot" class="hashtag" rel="nofollow ugc"><span>#</span><span class="p-category">secureboot</span></a> <a href="https://swsnr.writeas.com/tag:dracut" class="hashtag" rel="nofollow ugc"><span>#</span><span class="p-category">dracut</span></a> <a href="https://swsnr.writeas.com/tag:sbctl" class="hashtag" rel="nofollow ugc"><span>#</span><span class="p-category">sbctl</span></a></p>

<p><a href="https://remark.as/p/swsnr/install-arch-with-secure-boot-tpm2-based-luks-encryption-and-systemd-homed" rel="nofollow ugc">Discuss...</a></p>

<p><strong>Update:</strong> I no longer use <code>dracut</code>, and the corresponding part of this blog
post no longer reflects my setup.</p>

<p>This article describes my Arch Linux setup which combines Secure Boot with custom keys, TPM2-based full disk encryption and systemd-homed into a fully encrypted and authenticated, yet convenient Linux system.</p>

<p>This setup draws inspiration from <a href="https://0pointer.net/blog/authenticated-boot-and-disk-encryption-on-linux.html" rel="nofollow ugc">Authenticated Boot and Disk Encryption on Linux</a> and <a href="https://0pointer.net/blog/unlocking-luks2-volumes-with-tpm2-fido2-pkcs11-security-hardware-on-systemd-248.html" rel="nofollow ugc">Unlocking LUKS2 volumes with TPM2, FIDO2, PKCS#11 Security Hardware on systemd 248</a> by Lennart Poettering, and combines my previous posts on these topics.</p>



<h2 id="what-this-setup-does">What this setup does</h2>
<ul><li>Authenticate the boot loader, kernel, initramfs, microcode with Secure Boot, using my own custom keys.  Nothing can boot which wasn’t signed by my keys.</li>
<li>Everything is either authenticated with my keys (kernel, initramfs, microcode) or encrypted (system partition).</li>
<li>Encrypt the system partition, and unlock it automatically if the boot process was authenticated, by means of a TPM2 key bound to the secure boot state.</li>
<li>Give every user their own dedicated encrypted home directory, which gets unlocked at login and locked again at logout.</li></ul>

<h2 id="what-it-doesn-t">What it doesn’t</h2>
<ul><li>Show an ugly LUKS password prompt at boot (even with Plymouth it’s not really pretty).</li>
<li>Leave some parts unencrypted and unauthenticated (conventional installations often fail to consider the initramfs).</li>
<li>Ask me twice for my password, once at boot to unlock the disk and then again at login.</li>
<li>Encrypt data of all users with a shared key.</li></ul>

<h2 id="tools-used">Tools used</h2>
<ul><li><strong>Archlinux</strong></li>
<li><strong>systemd</strong> &gt;= 250</li>
<li><strong>systemd-boot</strong> as bootloader</li>
<li><strong>systemd</strong> in initramfs to automatically discover and mount the root filesystem</li>
<li><strong>dracut</strong> to generate the initramfs and build signed UEFI binaries</li>
<li><strong>sbctl</strong> to create and enroll Secure Boot keys, and sign binaries</li>
<li><strong>systemd-homed</strong> to manage user accounts with per-user encrypted home directories</li>
<li><strong>systemd-cryptenroll</strong> to add TPM2 and recovery keys tokens to a LUKS partition</li></ul>

<h2 id="the-setup">The setup</h2>

<h3 id="install-the-system">Install the system</h3>

<p>We follow the <a href="https://wiki.archlinux.org/title/installation_guide" rel="nofollow ugc">Installation Guide</a> up to and including section <a href="https://wiki.archlinux.org/title/installation_guide#Update_the_system_clock" rel="nofollow ugc">“Update the system clock”</a>. Then we partition the disk (<code>/dev/nvme0n1</code> in our case); we need an EFI system partition of about 500MB and a root partition spanning the rest of the disk. The EFI partition must be unencrypted and have a FAT filesystem; for the root file system we choose btrfs on top of an encrypted partition.</p>

<p>First we partition the disk and reload the partition table; we take care to specify proper partition types (-t option) so that systemd can automatically discover and mount our filesystems without further configuration in <code>/etc/crypttab</code> or <code>/etc/fstab</code> (see <a href="https://systemd.io/DISCOVERABLE_PARTITIONS/" rel="nofollow ugc">Discoverable Partitions Specification (DPS)</a>):</p>

<pre><code class="language-console">$ target_device=/dev/nvme0n1
$ sgdisk -Z &#34;$target_device&#34;
$ sgdisk -n1:0:+550M -t1:ef00 -c1:EFISYSTEM -N2 -t2:8304 -c2:linux &#34;$target_device&#34;
$ sleep 3
$ partprobe -s &#34;$target_device&#34;
$ sleep 3
</code></pre>

<p>Then we setup the encrypted partition for the root file system. We get asked for an encryption password where we pick a very simple encryption password (even “password” is good enough for now, really) to save some typing during installation, as we’ll later replace the password with TPM2 key and a random recovery key:</p>

<pre><code class="language-console">$ cryptsetup luksFormat --type luks2 /dev/disk/by-partlabel/linux
$ cryptsetup luksOpen /dev/disk/by-partlabel/linux root
$ root_device=/dev/mapper/root
</code></pre>

<p>Now we create the filesystems:</p>

<pre><code class="language-console">$ mkfs.fat -F32 -n EFISYSTEM /dev/disk/by-partlabel/EFISYSTEM
$ mkfs.btrfs -f -L linux &#34;$root_device&#34;
</code></pre>

<p>Now we can mount the filesystems and create some basic btrfs subvolumes:</p>

<pre><code class="language-console">$ mount &#34;$root_device&#34; /mnt
$ mkdir /mnt/efi
$ mount /dev/disk/by-partlabel/EFISYSTEM /mnt/efi
$ for subvol in var var/log var/cache var/tmp srv home; do btrfs subvolume create &#34;/mnt/$subvol&#34;; done
</code></pre>

<p>Now we’re ready to bootstrap Arch Linux: We generate a mirrorlist and install essential packages:</p>

<pre><code class="language-console">$ reflector --save /etc/pacman.d/mirrorlist --protocol https --latest 5 --sort age
$ pacstrap /mnt base linux linux-firmware intel-ucode btrfs-progs dracut neovim
</code></pre>

<p>This takes a while to download and installation all packages; afterwards we configure some essential settings. Choose locale settings and the <code>$new_hostname</code> according to your personal preferences.</p>

<pre><code class="language-console">$ ln -sf /usr/share/zoneinfo/Europe/Berlin /mnt/etc/localtime
$ sed -i -e &#39;/^#en_GB.UTF-8/s/^#//&#39; /mnt/etc/locale.gen
$ echo &#39;LANG=en_GB.UTF-8&#39; &gt;/mnt/etc/locale.conf
$ echo &#39;KEYMAP=us&#39; &gt;/mnt/etc/vconsole.conf
$ echo &#34;$new_hostname&#34; &gt;/mnt/etc/hostname
</code></pre>

<p>Now we enter the new system and finish configuration by generating locales, enabling a few essential services and setting a root password:</p>

<pre><code class="language-console">$ arch-chroot /mnt
$ locale-gen
$ systemctl enable systemd-homed
$ systemctl enable systemd-timesyncd
$ passwd root
</code></pre>

<p>Still in chroot we now build unified EFI kernel images (including initrd and kernel) for booting and install the systemd-boot boot loader:</p>

<pre><code class="language-console">$ pacman -S --noconfirm --asdeps binutils elfutils
$ dracut -f --uefi --regenerate-all
$ bootctl install
</code></pre>

<p>We do <em>not</em> need to create <code>/etc/fstab</code> or <code>/etc/crypttab</code>; as we assigned the appropriate types to each partition and installed systemd-boot a systemd-based initramfs can automatically determine the disk the system was booted from, and discover all relevant partitions.  It can then use superblock information to automatically open encrypted LUKS devices and mount file systems.</p>

<p>At this point we also need to take care to install everything we need for network configuration after reboot.  For desktop systems I prefer network manager because it integrates well into Gnome:</p>

<pre><code class="language-console">$ pacman -S networkmanager
</code></pre>

<p>We have finished the basic setup from the live disk now; let’s leave chroot and reboot:</p>

<pre><code class="language-console">$ exit
$ reboot
</code></pre>

<p>After reboot we can complete the system installation, by adding a desktop environment, applications, command line tools, etc.</p>

<p>I like to automate this, and have two bash scripts in my <a href="https://github.com/swsnr/dotfiles" rel="nofollow ugc">dotfiles</a>, one for boostrapping a new system from a live disk (<a href="https://github.com/swsnr/dotfiles/blob/6a25c4e0620068ecc6360fcbe1587eb14b622ac2/arch/bootstrap-from-iso.bash" rel="nofollow ugc"><code>arch/bootstrap-from-iso.bash</code></a>) and another one for installing everything after the initial bootstrapping (<a href="https://github.com/swsnr/dotfiles/blob/6a25c4e0620068ecc6360fcbe1587eb14b622ac2/arch/install.bash" rel="nofollow ugc"><code>arch/install.bash</code></a>).</p>

<h3 id="create-homed-user">Create homed user</h3>

<p>With the installation finished we create our user account with <code>homectl</code>; let’s name it <code>foo</code> for the purpose of this article. First we should disable copy on write for <code>/home</code>, because this file system feature doesn’t work well with large files frequently updated in place, such as disk images of virtual machines or loopback files as created by systemd-homed:</p>

<pre><code class="language-console">$ chattr +C /home/
</code></pre>

<p>We now create the <code>foo</code> user with an encrypted home directory backed by LUKS and btrfs:</p>

<pre><code class="language-console">$ homectl create foo --storage luks --fs-type btrfs
</code></pre>

<p>By default systemd assigns 85% of the available disk space to the user account, and will balance available space among all user accounts (based on a weight we can configure with <code>—rebalance-weight</code>). On a single user system we may prefer to set an explicit quota for the user account:</p>

<pre><code class="language-console">$ homectl resize foo 50G
</code></pre>

<p>We can also add some additional metadata to the user account:</p>

<pre><code class="language-console">homectl update foo --real-name &#39;Foo&#39; --email-address foo@example.org --language en_GB.UTF-8 --member-of wheel
</code></pre>

<p><code>man homectl</code> provides a complete list of flags; in particular it also offers support for various kinds of security tokens (e.g. FIDO2) for user authentication, provides plenty of means for resource accounting (e.g. memory consumption) for the user account, and supports different kinds of password policies.</p>

<p>Finally we may run into systemd issues with home areas on btrfs (see below); if login fails with a “Operation on home failed: Not enough disk space for home” message we need to enable LUKS discard:</p>

<pre><code class="language-console">homectl update foo --luks-discard=true
</code></pre>

<p>This flag is not safe (heed the warning in <code>man homectl</code>), but until systemd improves its behaviour on btrfs we have no choice unfortunately.</p>

<h3 id="setup-secure-boot">Setup secure boot</h3>

<p>First let’s check the secure boot state. We must be in Setup Mode in order to enroll our own keys:</p>

<pre><code class="language-console">$ sbctl status
Installed:	✓ sbctl is installed
Owner GUID:	REDACTED
Setup Mode:	✗ Enabled
Secure Boot:	✗ Disabled
</code></pre>

<p>To enable secure boot we need some keys which we generate with <code>sbctl</code>.  For historical reasons sbctl creates these keys in <code>/usr/share/secureboot</code> but plans exists to change this to a more appropriate place (see <a href="https://github.com/Foxboron/sbctl/issues/57" rel="nofollow ugc">Github issue 57</a>).</p>

<pre><code class="language-console">$ sbctl create-keys
</code></pre>

<p>Now we tell dracut how to sign the UEFI binaries it builds and rebuild our kernel images to get them signed:</p>

<pre><code class="language-console">$ cat &gt; /etc/dracut.conf.d/50-secure-boot.conf &lt;&lt;EOF
uefi_secureboot_cert=&#34;/usr/share/secureboot/keys/db/db.pem&#34;
uefi_secureboot_key=&#34;/usr/share/secureboot/keys/db/db.key&#34;
EOF
$ dracut -f --uefi --regenerate-all
</code></pre>

<p>Next we need to sign the bootloader.  With <code>-s</code> we ask <code>sbctl</code> to remember this file in its database which later lets us check signatures with <code>sbctl verify</code> and automatically update all signatures with <code>sbctl sign-all</code>. The <code>sbctl</code> package includes a pacman hook which automatically updates signatures when an EFI binary on <code>/efi</code> or in <code>/usr/lib</code> changed. Note that we do not sign the boot loader on <code>/efi</code> but instead place a signed copy in <code>/usr/lib</code>.  Starting with systemd 250 <code>bootctl</code> will pick up the signed copy when updating the boot loader.  Hence we reinstall the bootloader afterwards to put the signed copy on <code>/efi</code>.</p>

<pre><code class="language-console">$ sbctl sign -s -o /usr/lib/systemd/boot/efi/systemd-bootx64.efi.signed /usr/lib/systemd/boot/efi/systemd-bootx64.efi
$ bootctl install
</code></pre>

<p>We should also do the same for the firmware update to enable seamless firmware updates under secure boot.  Again we use <code>-s</code> to remember this file in the <code>sbtctl</code> database:</p>

<pre><code class="language-console">$ sbctl sign -s -o /usr/lib/fwupd/efi/fwupdx64.efi.signed /usr/lib/fwupd/efi/fwupdx64.efi
</code></pre>

<p>Now let’s verify that we have all signatures in place and enroll keys if everything’s properly signed:</p>

<pre><code class="language-console">$ sbctl verify
Verifying file database and EFI images in /efi...
✓ /usr/lib/fwupd/efi/fwupdx64.efi.signed is signed
✓ /usr/lib/systemd/boot/efi/systemd-bootx64.efi.signed is signed
✓ /efi/EFI/BOOT/BOOTX64.EFI is signed
✓ /efi/EFI/Linux/linux-5.15.12-arch1-1-19ea0ebee1ea4de086128ce1a8e2197b-rolling.efi is signed
✓ /efi/EFI/systemd/systemd-bootx64.efi is signed
$ sbctl enroll-keys
</code></pre>

<p>After a reboot we can check the secure boot state again; we’ll see that setup mode is now disabled, secure boot is on, and everything was properly enrolled:</p>

<pre><code class="language-console">$ reboot
$ sbctl status
Installed:	✓ sbctl is installed
Owner GUID:	REDACTED
Setup Mode:	✓ Disabled
Secure Boot:	✓ Enabled
</code></pre>

<h3 id="enroll-tpm2-keys">Enroll TPM2 keys</h3>

<p>With the boot process secured we can now configure automatic unlocking of the root filesystem, by binding a LUKS key to the TPM.</p>

<p>We enable the tpm2-tss module in the Dracut configuration, install the dependencies of this dracut module, and regenerate our UEFI kernel images (which will again be signed for secure boot):</p>

<pre><code class="language-console">$ cat &gt; /etc/dracut.conf.d/50-tpm2.conf &lt;&lt;EOF
add_dracutmodules+=&#34; tpm2-tss &#34;
EOF
$ pacman -S tpm2-tools
$ dracut -f --uefi --regenerate-all
</code></pre>

<p>Now we can enroll a TPM2 token (bound to the secure boot measurement in PCR 7) and a recovery key to our root filesystem. This prompts for an existing passphrase each time.  Store the recovery key at a safe place <em>outside</em> of this disk, to have it at hand if TPM2 unlocking ever breaks.</p>

<pre><code class="language-console">$ systemd-cryptenroll /dev/gpt-auto-root-luks --recovery-key
$ systemd-cryptenroll /dev/gpt-auto-root-luks --tpm2-device=auto
</code></pre>

<p>Now reboot and enjoy: The boot process goes straight all the way to the login manager and never shows a LUKS password prompt.  The root filesystem is still reasonably secure: The TPM2 key becomes invalid if the secure boot state changes (e.g. new keys are enrolled, or secure boot is disabled), and cannot be recovered if the disk is removed from the system. Consequently only a kernel signed and authenticated with your own secure boot keys can unlock the root disk automatically.</p>

<p>Finally we can wipe the password slots if you like (<strong>make sure to have a recovery key at this point</strong>):</p>

<pre><code class="language-console">$ systemd-cryptenroll /dev/gpt-auto-root-luks --wipe-slot=password
</code></pre>

<p>If you cannot use secure boot for some reason you can alternatively bind the TPM2 token to a combination of firmware state and configuration and the exact boot chain (up to and including the specific kernel that was started), by specifing the PCR registers 0-5:</p>

<pre><code class="language-console">$ systemd-cryptenroll /dev/gpt-auto-root-luks --tpm2-device=auto --tpm2-pcrs 0+1+2+3+4+5
</code></pre>

<p>This only permits the current kernel and its specific boot chain (e.g bootloader used) to unlock the root filesystem automatically.  However this means that we need to reboot and then wipe and re-enroll the TPM2 token after every rebuild of the kernel image… which happens quite often in fact: Dracut updates or configuration changes, kernel updates, systemd updates (due to the EFI shim provided by systemd), bootloader updates, bootloader configuration changes, etc.</p>

<p>Hence I generally recommend to use secure boot if possible in any way.</p>

<h2 id="issues-with-this-setup">Issues with this setup</h2>

<p>While I am happy with this setup it still has a few drawbacks and issues.</p>

<h3 id="double-encryption">Double encryption</h3>

<p>In this setup home directories get encrypted twice, once by homed and then again by the underlying LUKS device.  This wastes a bunch of CPU cycles and likely impacts performance a lot, though I haven’t measured the impact and it’s not so bad as to be noticeable in my day-to-day work.</p>

<p>We could optimize this by putting <code>/home/</code> on a separate partition backed by dm-integrity to authenticate the filesystem (omitting dm-integrity and using a plain file system leaves an attack vector, because linux cannot securely mount untrusted file systems).  This setup requires at least systemd 250 or newer, because earlier versions do not support dm-integrity well.  With systemd 250 we can setup a HMAC-based integrity device, put the HMAC key on the rootfs (e.g. <code>/etc/keys/home.key</code>) and register the home partition in <code>/etc/integritytab</code> with that key, and then mount it via <code>/etc/fstab</code>.</p>

<p>However, this has a few issues on its own, because dm-integrity has a few design issues and is and nowhere near LUKS/dm-crypt:</p>
<ul><li>There’s no key management like LUKS for dm-crypt, meaning we can’t use passphrases or TPM2 keys for dm-integrity devices; instead we need a key file somewhere on disk.</li>
<li>Unlike LUKS/dm-crypt devices dm-integrity devices aren’t self-describing, because the superblock for dm-integrity doesn’t even contain the algorithm used (see &lt;<a href="https://github.com/systemd/systemd/pull/20902" rel="nofollow ugc">https://github.com/systemd/systemd/pull/20902</a>). We cannot mount a dm-integrity device without some extra configuration, and worse, getting the configuration wrong can silently corrupt the device.</li>
<li>For these reasons, <a href="https://systemd.io/DISCOVERABLE_PARTITIONS/" rel="nofollow ugc">DPS</a> cannot and does not support dm-integrity partitions, so we need to configure the whole home partition mount, from dm-integrity up to <code>/etc/fstab</code>.</li></ul>

<h2 id="tooling-issues">Tooling issues</h2>

<p>There are also multiple issues with current tooling that require some more or less safe workarounds:</p>
<ul><li>At the time of writing systemd-home has issues with resizing LUKS home areas on btrfs filesystems, apparently due to <code>fallocate()</code> idiosyncrasies in btrfs. This issue prevents users from logging in, see systemd issues <a href="https://github.com/systemd/systemd/issues/19398" rel="nofollow ugc">19398</a> and <a href="https://github.com/systemd/systemd/issues/20960" rel="nofollow ugc">20960</a>, an <a href="https://bbs.archlinux.org/viewtopic.php?id=258382" rel="nofollow ugc">Arch forums post</a>, and a <a href="https://lists.freedesktop.org/archives/systemd-devel/2020-August/045092.html" rel="nofollow ugc">mail on the systemd-devel list</a>. A workaround is to enable online discard for the user, but this flag is unsafe because it allows overcommitting disk space, which results in IO errors that the kernel and application are not well prepared to handle (which comes down to potential data loss).  Keep frequent backups if you need this.</li></ul>

<p><a href="https://remark.as/p/swsnr/install-arch-with-secure-boot-tpm2-based-luks-encryption-and-systemd-homed" rel="nofollow ugc">Discuss...</a></p>
]]></content:encoded>
      <guid>https://swsnr.writeas.com/install-arch-with-secure-boot-tpm2-based-luks-encryption-and-systemd-homed</guid>
      <pubDate>Wed, 05 Jan 2022 23:00:00 +0000</pubDate>
    </item>
    <item>
      <title>Unlock LUKS rootfs with TPM2 key</title>
      <link>https://swsnr.writeas.com/unlock-luks-rootfs-with-tpm2-key?pk_campaign=rss-feed</link>
      <description>#dracut #luks #sbctl #archlinux #systemd #secureboot&#xA;&#xA;Discuss...&#xA;&#xA;Historically cryptsetup and LUKS only supported good old passwords; however recent systemd versions extend cryptsetup with additional key types such as FIDO tokens and TPM devices.&#xA;&#xA;I like the idea of encrypting the rootfs with a TPM2 key; it allows booting without ugly LUKS password prompts but still it keeps data encrypted at rest, and when combined with secure boot also still protects the running system against unauthorized access.&#xA;&#xA;Secure boot will prevent others from placing custom kernels on the unencrypted EFI system partition and booting these, or changing the kernel cmdline, in order to obtain root access to the unlocked rootfs.  LUKS encryption with a TPM-based key bound to secure boot state protects the data if someone removes the hard disk and attempts to access it offline, or tries to disable secure boot in order to boot a custom kernel.&#xA;&#xA;I’ve covered secure boot setup in a past article; this article talks about the TPM2-based encryption.</description>
      <content:encoded><![CDATA[<p><a href="https://swsnr.writeas.com/tag:dracut" class="hashtag" rel="nofollow ugc"><span>#</span><span class="p-category">dracut</span></a> <a href="https://swsnr.writeas.com/tag:luks" class="hashtag" rel="nofollow ugc"><span>#</span><span class="p-category">luks</span></a> <a href="https://swsnr.writeas.com/tag:sbctl" class="hashtag" rel="nofollow ugc"><span>#</span><span class="p-category">sbctl</span></a> <a href="https://swsnr.writeas.com/tag:archlinux" class="hashtag" rel="nofollow ugc"><span>#</span><span class="p-category">archlinux</span></a> <a href="https://swsnr.writeas.com/tag:systemd" class="hashtag" rel="nofollow ugc"><span>#</span><span class="p-category">systemd</span></a> <a href="https://swsnr.writeas.com/tag:secureboot" class="hashtag" rel="nofollow ugc"><span>#</span><span class="p-category">secureboot</span></a></p>

<p><a href="https://remark.as/p/swsnr/unlock-luks-rootfs-with-tpm2-key" rel="nofollow ugc">Discuss...</a></p>

<p>Historically cryptsetup and LUKS only supported good old passwords; however recent systemd versions extend cryptsetup with <a href="https://0pointer.net/blog/unlocking-luks2-volumes-with-tpm2-fido2-pkcs11-security-hardware-on-systemd-248.html" rel="nofollow ugc">additional key types</a> such as FIDO tokens and TPM devices.</p>

<p>I like the idea of encrypting the rootfs with a TPM2 key; it allows booting without ugly LUKS password prompts but still it keeps data encrypted at rest, and when combined with secure boot also still protects the running system against unauthorized access.</p>

<p>Secure boot will prevent others from placing custom kernels on the unencrypted EFI system partition and booting these, or changing the kernel cmdline, in order to obtain root access to the unlocked rootfs.  LUKS encryption with a TPM-based key bound to secure boot state protects the data if someone removes the hard disk and attempts to access it offline, or tries to disable secure boot in order to boot a custom kernel.</p>

<p>I’ve covered <a href="2021-04-01-secure-boot-on-arch-linux-with-sbctl-and-dracut.md" rel="nofollow ugc">secure boot setup in a past article</a>; this article talks about the TPM2-based encryption.</p>



<h2 id="enroll-tpm-key">Enroll TPM key</h2>

<p>Check that the system supports TPM2:</p>

<pre><code class="language-console">$ systemd-cryptenroll --tpm2-device=list
PATH        DEVICE      DRIVER 
/dev/tpmrm0 MSFT0101:00 tpm_crb
</code></pre>

<p>Then enroll a new TPM2 key (use the appropriate block device path of course):</p>

<pre><code class="language-console">$ systemd-cryptenroll --tpm2-device=auto /dev/disk/by-partlabel/linux 
New TPM2 token enrolled as key slot 1.
</code></pre>

<p>Then add recovery key:</p>

<pre><code class="language-console">$ systemd-cryptenroll --recovery-key  /dev/disk/by-partlabel/linux
🔐 Please enter current passphrase for disk /dev/disk/by-partlabel/linux: 
A secret recovery key has been generated for this volume:

    🔐 efcfbdlt-rhkdjjul-inbhbvhi-nfkvbbbv-didjbjel-butkrrig-ugbrivdd-evnkkkgn

Please save this secret recovery key at a secure location. It may be used to
regain access to the volume if the other configured access credentials have
been lost or forgotten. The recovery key may be entered in place of a password
whenever authentication is requested.
New recovery key enrolled as key slot 2.
</code></pre>

<p>As suggested save this key in a secure location outside of the system. If TPM2 unlocking fails systemd will prompt for a password where you can enter this key.  This lets us access the data even if the TPM2 key became invalid (e.g. when the secure boot configuration changes).</p>

<h2 id="configure-dracut">Configure dracut</h2>

<p>Force dracut to include the TPM2 software stack, by adding /etc/dracut.conf.d/tpm.conf:</p>

<pre><code class="language-console">add_dracutmodules+=&#34; tpm2-tss &#34;
# And add files dracut currently fails to add, see
# https://github.com/dracutdevs/dracut/issues/1676
install_items+=&#34; /usr/lib/cryptsetup/libcryptsetup-token-systemd-tpm2.so &#34;
</code></pre>

<p>The last line adds necessary cryptsetup plugins which dracut doesn’t yet add (see <a href="https://github.com/dracutdevs/dracut/issues/1676" rel="nofollow ugc">https://github.com/dracutdevs/dracut/issues/1676</a>).</p>

<p>Make sure to use dracut newer than 055-106-g813577e2; dracut 55 has a typo in the tpm2-tss module dependencies which breaks the module (see <a href="https://github.com/dracutdevs/dracut/pull/1526" rel="nofollow ugc">https://github.com/dracutdevs/dracut/pull/1526</a>), and another issue with systemd-sysusers which interferes with TPM2 support in early boot (see <a href="https://github.com/dracutdevs/dracut/pull/1658" rel="nofollow ugc">https://github.com/dracutdevs/dracut/pull/1658</a>).  As of Dec 2021 you’ll need to install <a href="https://aur.archlinux.org/packages/dracut-git" rel="nofollow ugc">dracut-git from AUR</a>.</p>

<h3 id="reboot-and-cleanup">Reboot and cleanup</h3>

<p>Reboot to verify that the system now boots without a LUKS prompt.  Afterwards remove the password key from the rootfs:</p>

<pre><code class="language-console">$ systemd-cryptenroll --wipe-slot=password /dev/disk/by-partlabel/linux
</code></pre>

<p>Now the system only unlocks with the TPM2 key or the recovery key.</p>

<h3 id="tighten-security">Tighten security</h3>

<p>Check that</p>
<ul><li>secure boot is enabled (<code>bootctl status</code> or <code>sbctl status</code>),</li>
<li>secure boot uses custom keys,</li>
<li>all kernels are signed (<code>sbctl verify</code>; also check the firmware updater and other relevant EFI binaries), and</li>
<li>no unsigned initramfs or microcode image is used (at best use bundled UEFI executables).</li></ul>

<p>For users consider to use systemd-homed which offers per-user encrypted home “areas”, backed by LUKS encrypted loopback files.</p>

<p><a href="https://remark.as/p/swsnr/unlock-luks-rootfs-with-tpm2-key" rel="nofollow ugc">Discuss...</a></p>
]]></content:encoded>
      <guid>https://swsnr.writeas.com/unlock-luks-rootfs-with-tpm2-key</guid>
      <pubDate>Sun, 26 Dec 2021 23:00:00 +0000</pubDate>
    </item>
    <item>
      <title> Secure boot on Arch Linux with sbctl and dracut</title>
      <link>https://swsnr.writeas.com/secure-boot-on-arch-linux-with-sbctl-and-dracut?pk_campaign=rss-feed</link>
      <description>#archlinux #secureboot #dracut #sbctl&#xA;&#xA;Discuss...&#xA;&#xA;I started playing around with secure boot, with the ultimately goal of setting it up on my laptop. I experimented in a libvirt/qemu VM and to my surprise a custom secure boot setup is rather easy (the Secure Boot page on the Arch Wiki suggests quite the contrary), thanks to dracut and a fairly recent tool named sbctl which just recently had it’s first release.</description>
      <content:encoded><![CDATA[<p><a href="https://swsnr.writeas.com/tag:archlinux" class="hashtag" rel="nofollow ugc"><span>#</span><span class="p-category">archlinux</span></a> <a href="https://swsnr.writeas.com/tag:secureboot" class="hashtag" rel="nofollow ugc"><span>#</span><span class="p-category">secureboot</span></a> <a href="https://swsnr.writeas.com/tag:dracut" class="hashtag" rel="nofollow ugc"><span>#</span><span class="p-category">dracut</span></a> <a href="https://swsnr.writeas.com/tag:sbctl" class="hashtag" rel="nofollow ugc"><span>#</span><span class="p-category">sbctl</span></a></p>

<p><a href="https://remark.as/p/swsnr/secure-boot-on-arch-linux-with-sbctl-and-dracut" rel="nofollow ugc">Discuss...</a></p>

<p>I started playing around with secure boot, with the ultimately goal of setting it up on my laptop. I experimented in a libvirt/qemu VM and to my surprise a custom secure boot setup is rather easy (the <a href="https://wiki.archlinux.org/index.php/Unified_Extensible_Firmware_Interface/Secure_Boot#Manual_process" rel="nofollow ugc">Secure Boot</a> page on the Arch Wiki suggests quite the contrary), thanks to dracut and a fairly recent tool named <code>sbctl</code> which just recently had it’s first release.</p>



<h2 id="vm-setup">VM Setup</h2>

<p>We start with a fresh libvirt VM (you’ll <code>qemu</code>, <code>libvirt</code>, and <code>edk2-ovmf</code>) of a recent Arch Linux ISO using the EFI secure boot firmware provided by the <code>edk2-ovmf</code> package at <code>/usr/share/edk2-ovmf/x64/OVMF_CODE.secboot.fd</code>; when using virt-manager be sure to edit the configuration before starting the installation and make sure to select this firmware (otherwise you’ll end up in a BIOS-based VM which doesn’t really get far in terms of secure boot). Go through the standard installation process (I have a <a href="https://github.com/lunaryorn/dotfiles/blob/61826f1240901368f9f2432c9a6d892f64bf0099/arch/bootstrap-from-iso.bash" rel="nofollow ugc">custom bootstrap script</a> for a quick fresh Arch install with some standard settings); do make sure to install <code>dracut</code> for initrd and kernel image generation and setup systemd-boot as the EFI bootloader. Install the <code>linux-lts</code> kernel in addition to the standard <code>linux</code> kernel; this allows to leave one kernel unsigned after enabling secure boot to verify that booting the unsigned kernel is really forbidden.</p>

<p>Also build unified images right away with <code>dracut --uefi --kver</code>; together with systemd partition auto-discovery this avoids the need for dedicated boot loader configuration and already prepares for secure boot (which requires unified EFI binaries).</p>

<h2 id="prerequisites">Prerequisites</h2>

<p>After rebooting check the secure boot state with <code>bootctl</code> first to make sure that the firmware supports secure boot:</p>

<pre><code class="language-console"># bootctl | head
System:
     Firmware: UEFI 2.70 (EDK II 1.00)
  Secure Boot: disabled
   Setup Mode: setup
 Boot into FW: supported

Current Boot Loader:
      Product: systemd-boot 247.4-2-arch
     Features: ✓ Boot counting
               ✓ Menu timeout control
</code></pre>

<p>Now install <code>sbctl</code> to generate and enroll secure boot keys and sign EFI binaries (<code>pacman -S sbctl</code>); then check how sbctl sees the secure boot state:</p>

<pre><code class="language-console"># sbctl  status
==&gt; WARNING: Setup Mode: Enabled
==&gt; WARNING: Secure Boot: Disabled
</code></pre>

<h2 id="key-generation">Key generation</h2>

<p>Now we can generate our set of secure boot keys with <code>sbctl</code>:</p>

<pre><code class="language-console"># sbctl create-keys
==&gt; Creating secure boot keys...
  -&gt; Created UUID bb88de62-623a-4450-b256-7c9ffc924f64...
==&gt; Create EFI signature list /usr/share/secureboot/keys/PK/PK.der.esl...
==&gt; Signing /usr/share/secureboot/keys/PK/PK.der.esl with /usr/share/secureboot/keys/PK/PK.key...
==&gt; Create EFI signature list /usr/share/secureboot/keys/KEK/KEK.der.esl...
==&gt; Signing /usr/share/secureboot/keys/KEK/KEK.der.esl with /usr/share/secureboot/keys/PK/PK.key...
==&gt; Create EFI signature list /usr/share/secureboot/keys/db/db.der.esl...
==&gt; Signing /usr/share/secureboot/keys/db/db.der.esl with /usr/share/secureboot/keys/KEK/KEK.key...
</code></pre>

<p>This command creates all required keys in <code>/usr/share/secureboot</code>; on a physical installation it’s probably very advisable to copy the entire contents of the directory to a secure off-site location for backup.</p>

<p><code>sbctl verify</code> now lists all EFI binaries as unsigned:</p>

<pre><code class="language-console"># sbctl verify
==&gt; Verifying file database and EFI images in /efi...
  -&gt; WARNING: /efi/EFI/BOOT/BOOTX64.EFI is not signed
  -&gt; WARNING: /efi/EFI/Linux/linux-5.10.27-1-lts-b2f521553ef8449289122ae8d4c2cffe-rolling.efi is not signed
  -&gt; WARNING: /efi/EFI/Linux/linux-5.11.11-arch1-1-b2f521553ef8449289122ae8d4c2cffe-rolling.efi is not signed
  -&gt; WARNING: /efi/EFI/systemd/systemd-bootx64.efi is not signed
</code></pre>

<h2 id="boot-loader-signatures">Boot loader signatures</h2>

<p>Sign the boot loader first:</p>

<pre><code class="language-console"># sbctl sign -s /efi/EFI/BOOT/BOOTX64.EFI
  -&gt; Signing /efi/EFI/BOOT/BOOTX64.EFI...
# sbctl sign -s /efi/EFI/systemd/systemd-bootx64.efi
  -&gt; Signing /efi/EFI/systemd/systemd-bootx64.efi...
</code></pre>

<p>Now the boot loader is properly signed:</p>

<pre><code class="language-console"># sbctl verify
==&gt; Verifying file database and EFI images in /efi...
  -&gt; /efi/EFI/systemd/systemd-bootx64.efi is signed
  -&gt; /efi/EFI/BOOT/BOOTX64.EFI is signed
  -&gt; WARNING: /efi/EFI/Linux/linux-5.10.27-1-lts-b2f521553ef8449289122ae8d4c2cffe-rolling.efi is not signed
  -&gt; WARNING: /efi/EFI/Linux/linux-5.11.11-arch1-1-b2f521553ef8449289122ae8d4c2cffe-rolling.efi is not signed
</code></pre>

<p>The <code>-s</code> flag stores these paths in an internal <code>sbctl</code> database which keeps track of files <code>sbsign</code> signed; <code>sbctl</code> uses this database in <code>sbctl sign-all</code> to refresh the signatures of all files it ever signed. This helps with boot loader updates:</p>

<pre><code class="language-console"># bootctl update
Copied &#34;/usr/lib/systemd/boot/efi/systemd-bootx64.efi&#34; to &#34;/efi/EFI/systemd/systemd-bootx64.efi&#34;.
Copied &#34;/usr/lib/systemd/boot/efi/systemd-bootx64.efi&#34; to &#34;/efi/EFI/BOOT/BOOTX64.EFI&#34;.
# sbctl verify
==&gt; Verifying file database and EFI images in /efi...
  -&gt; WARNING: /efi/EFI/BOOT/BOOTX64.EFI is not signed
  -&gt; WARNING: /efi/EFI/systemd/systemd-bootx64.efi is not signed
  -&gt; WARNING: /efi/EFI/Linux/linux-5.10.27-1-lts-b2f521553ef8449289122ae8d4c2cffe-rolling.efi is not signed
  -&gt; WARNING: /efi/EFI/Linux/linux-5.11.11-arch1-1-b2f521553ef8449289122ae8d4c2cffe-rolling.efi is not signed
# sbctl sign-all
  -&gt; Signing /efi/EFI/systemd/systemd-bootx64.efi...
  -&gt; Signing /efi/EFI/BOOT/BOOTX64.EFI...
# sbctl verify
==&gt; Verifying file database and EFI images in /efi...
  -&gt; /efi/EFI/BOOT/BOOTX64.EFI is signed
  -&gt; /efi/EFI/systemd/systemd-bootx64.efi is signed
  -&gt; WARNING: /efi/EFI/Linux/linux-5.10.27-1-lts-b2f521553ef8449289122ae8d4c2cffe-rolling.efi is not signed
  -&gt; WARNING: /efi/EFI/Linux/linux-5.11.11-arch1-1-b2f521553ef8449289122ae8d4c2cffe-rolling.efi is not signed
</code></pre>

<h2 id="signed-unified-kernel-images">Signed unified kernel images</h2>

<p>To sign the kernel tell dracut about the secure boot keys:</p>

<pre><code class="language-console"># cat &gt; /etc/dracut.conf.d/50-secure-boot.conf &lt;&lt;EOF
uefi_secureboot_cert=&#34;/usr/share/secureboot/keys/db/db.pem&#34;
uefi_secureboot_key=&#34;/usr/share/secureboot/keys/db/db.key&#34;
EOF
</code></pre>

<p>While at it also configure a few other non-essential but still very useful dracut options, to silence the boot process and reduce the size of the images:</p>

<pre><code class="language-console"># cat &gt; /etc/dracut.conf.d/40-options.conf &lt;&lt;EOF
kernel_cmdline=&#34;quiet&#34;
compress=&#34;zstd&#34;
hostonly=&#34;yes&#34;
EOF
</code></pre>

<p>With secure boot systemd-boot can no longer set a kernel command line: In this mode systemd-boot uses EFI interfaces to start binaries, to avoid bypassing the signature requirement; this interface does not support for kernel command line arguments. For this reason the desired command line as well as all required initrds must be embedded into a single signed EFI binary, and any command line flags like “quiet” must be set through dracut with the <code>kernel_cmdline</code> setting.</p>

<p>Now generate a unified UEFI binary for the LTS kernel:</p>

<pre><code class="language-console"># dracut --force --uefi --kver 5.10.27-1-lts
dracut: Executing: /usr/bin/dracut --force --uefi --kver 5.10.27-1-lts
…
dracut: *** Creating image file &#39;/efi/EFI/Linux/linux-5.10.27-1-lts-b2f521553ef8449289122ae8d4c2cffe-rolling.efi&#39; ***
dracut: Using UEFI kernel cmdline:
dracut: quiet
warning: data remaining[25025536 vs 25035138]: gaps between PE/COFF sections?
warning: data remaining[25025536 vs 25035144]: gaps between PE/COFF sections?
Signing Unsigned original image
dracut: *** Creating signed UEFI image file &#39;/efi/EFI/Linux/linux-5.10.27-1-lts-b2f521553ef8449289122ae8d4c2cffe-rolling.efi&#39; done ***
</code></pre>

<p>Note the last line: dracut bundled everything into a single EFI file <em>and</em> signed it. <code>sbctl verify</code> confirms the new signature:</p>

<pre><code class="language-console"># sbctl verify
==&gt; Verifying file database and EFI images in /efi...
  -&gt; /efi/EFI/BOOT/BOOTX64.EFI is signed
  -&gt; /efi/EFI/systemd/systemd-bootx64.efi is signed
  -&gt; /efi/EFI/Linux/linux-5.10.27-1-lts-b2f521553ef8449289122ae8d4c2cffe-rolling.efi is signed
</code></pre>

<h2 id="secure-boot-activation">Secure boot activation</h2>

<p>Now enroll the new secure boot keys into the EFI firmware, check that the firmware left secure boot setup mode:</p>

<pre><code class="language-console"># sbctl enroll-keys
==&gt; Syncing /usr/share/secureboot/keys to EFI variables...
==&gt; Synced keys!
# sbctl status
==&gt; Setup Mode: Disabled
==&gt; WARNING: Secure Boot: Disabled
</code></pre>

<p>Now enable the boot loader menu to be able to select a kernel at boot and reboot:</p>

<pre><code class="language-console"># echo &#39;timeout 10&#39; &gt;&gt; /efi/loader/loader.conf
# reboot
</code></pre>

<p>The firmware now prohibits booting the unsigned kernel, but allows the signed kernel:</p>

<p><img src="https://i.snap.as/N2JKKj36.webp" alt="secureboot with prohibited and permitted binary"/></p>

<h2 id="open-points">Open points</h2>

<p>Dracut automates signing kernel images (<a href="https://aur.archlinux.org/packages/dracut-hook-uefi" rel="nofollow ugc">dracut-hook-uefi</a> automatically invokes dracut when installing or updating kernel images through pacman), but ensuring proper signatures on the bootloader itself even across updates presents an open issue; ideally there should be some way to call <code>sbctl sign-all</code> automatically after <code>bootctl</code>.</p>

<p><code>sbctl</code> is also a rather new project, which published a first 0.1 only recently; it remains to be seen how sustainable the project is (I dearly hope it is since it provides a huge improvement over the state of the art secure-boot tooling).</p>

<p><a href="https://remark.as/p/swsnr/secure-boot-on-arch-linux-with-sbctl-and-dracut" rel="nofollow ugc">Discuss...</a></p>
]]></content:encoded>
      <guid>https://swsnr.writeas.com/secure-boot-on-arch-linux-with-sbctl-and-dracut</guid>
      <pubDate>Wed, 31 Mar 2021 22:00:00 +0000</pubDate>
    </item>
  </channel>
</rss>