Skip to content

Instantly share code, notes, and snippets.

@jfroy
Last active February 2, 2025 02:07
Show Gist options
  • Save jfroy/2ae5af2aa42b39a0b2686dd844e14f82 to your computer and use it in GitHub Desktop.
Save jfroy/2ae5af2aa42b39a0b2686dd844e14f82 to your computer and use it in GitHub Desktop.
Debian 12 "modern booting"
#!/bin/sh
set -eu
COMMAND="$1"
KERNEL_VERSION="$2"
INITRD="${KERNEL_INSTALL_STAGING_AREA}/initrd.img-${KERNEL_VERSION}"
[ "$COMMAND" = add ] || exit 0
if [ "$#" -ge 5 ]; then
# Explicit initrd paths were passed, do not generate.
exit 0
fi
[ "$KERNEL_INSTALL_INITRD_GENERATOR" = dracut ] || exit 0
echo "Running dracut: ${INITRD}"
dracut -q --force "${INITRD}" "${KERNEL_VERSION}"

Debian 12 "modern booting"

These rough steps will setup a Debian 12 system to:

  • Secure boot with a custom platform key (PK) with Microsoft's certificates in the trust db for OpROMs.
  • Enroll the secure boot material automatically at boot.
  • Boot a signed UKI.
  • Have an encryped root partition that unlocks automatically with a TPM key released only if the secure boot parameters are known (PCR 7 hash) and the UKI is signed by a trusted key (PCR 11 sig).
  • Install new kernels with kernel-install which will generate an initramfs with dracut and create a signed UKI with ukify which will be installed in the EFI system partition for systemd-boot.

Before booting the installer

  • Put system in secure boot setup mode.
    • This usually means enabling securte boot and erasing all keys from the UEFI menu.

Installer

  • Create simple partition scheme with an EFI system partition and a root partition.
    • The EFI system partition size should be generous, at least 1 GB.
    • You may be forced to create a boot partition when enabling encryption for the root partition, since at that point the bootloader will not be able to read the kernel image from root. If/when the installer allows installing the kernel and initrd to the EFI system partition, or to install a UKI, then this boot partition won't be needed. It is not needed once the system uses a UKI.
    • You can throw in a swap partition if your system needs it.
  • Encrypt root partiton with LUKS2.
    • Password should simple, it will be removed after setup.
  • Reboot into the system (but see next step first).

Discoverable partitions

If you can get a shell at the end of the install, you could this before rebooting to the new system.

Secure boot with a signed UKI

Some steps have a $BOOT path placehoder. This matches the description in https://uapi-group.org/specifications/specs/boot_loader_specification/#the-boot-partition-placeholder. Some steps have a $ESP placeholder for the EFI system parition. Check your system's specific configuration and mounts.

  • Remove grub and initramfs tools.
    • apt purge --allow-remove-essential grub* grub2* os-prober shim* cryptsetup-initramfs initramfs*
  • Install dracut, tpm tools, sbsign, and other dependencies.
    • apt install curl dracut keyutils libtss2-esys-3.0.2-0 libtss2-mu0 libtss2-rc0 policykit-1 python3-cryptography python3-pefile sbsigntool tpm2-tools
  • Hold grub so it never gets reinstalled.
    • apt-mark hold grub* grub2*
  • Install systemd-254 or later from backport.
    • apt install -t bookworm-backports systemd systemd-boot systemd-coredump systemd-networkd systemd-resolved systemd-timesyncd
  • Generate secure boot keys.
    • ./gen-secure-boot-keys.sh
  • Install secure boot keys.
    • cp db.key /etc/kernel/secure-boot.key.pem
    • cp db.pem /etc/kernel/secure-boot.cert.pem
    • cp {db,KEK,PK}.auth $ESP/loader/keys/auto/
      • NOTES: Keys have to be stored on the ESP!
  • Sign systemd-boot's EFI binary.
    • sbsign --key db.key --cert db.pem --output /usr/lib/systemd/boot/efi/systemd-bootx64.efi.signed /usr/lib/systemd/boot/efi/systemd-bootx64.efi
    • NOTE: There is nothing here to automatically sign new versions of systemd-boot, and bootctl install will prefer the signed file, which means systemd-boot updates will not be installed to the EFI. This is good as the unsigned version would be rejected by the system, but it does mean you may need to manually sign and update systemd-boot from time to time.
  • Generate PCR keys.
    • Must use ukify-255 or later, which at the time of writing is not in backports.
    • Since it's a Python program, you can download it from upstream and run it directly to generate these keys. There is no need to overwrite the system's copy.
    • ./ukify genkey --pcr-private-key=/etc/kernel/pcr-initrd.key.pem --pcr-public-key=/etc/kernel/pcr-initrd.pub.pem
    • ./ukify genkey --pcr-private-key=/etc/kernel/pcr-system.key.pem --pcr-public-key=/etc/kernel/pcr-system.pub.pem
  • Copy the PCR initrd public key for systemd-cryptenroll.
    • cp /etc/kernel/pcr-initrd.pub.pem /etc/systemd/tpm2-pcr-public-key.pem
  • Install the material in kernel to /etc/kernel/.
    • install.d/55-dracut.install is a custom kernel-install plugin to generate an initramfs that will be picked up by 60-ukify.install.
  • Install 60-ukify.install from systemd-255 or later to /etc/kernel/install.d.
    • Must use systemd-255 or later, which at the time of writing is not in backports.
    • systemd-254's version does not discover initrds in the UKI staging area.
  • Install the material in dracut.conf.d to /etc/dracut.conf.d/.
  • Install the material in dkms to /etc/dkms/.
    • All modules have to be signed to be loaded by the kernel.
  • Re-install the kernel with kernel-install to generate the signed UKI.
    • kernel-install add $(uname -r) /boot/vmlinuz-$(uname -r)
  • Install systemd-boot in the EFI system partition.
    • bootctl install
  • Reboot.

At this point, secure boot is setup, but we need one more phase to enroll the TPM key to decrypt the root partition. When the system reboots next, systemd-boot will enroll the secure boot material, and you will need to enter the password to decrypt root.

TPM crypt enroll

Now that secure boot is enabled, with the system in a trusted state, we can enroll a decryption key for the root partition bound to the TPM. We also replace the easy install password with a recovery key.

WARNING: Do not lose the recovery key! You cannot modify the LUKS partition, including adding or removing other keys, with just a TPM entry.

  • Find the device node for the root LUKS partition using lsblk or similar tool.
  • Enroll the new LUKS keys
    • systemd-cryptenroll /dev/$ROOTDEV --wipe-slot=tpm2 --tpm2-device=auto
    • systemd-cryptenroll /dev/$ROOTDEV --recovery-key
  • Delete the easy installer password
    • systemd-cryptenroll /dev/$ROOTDEV --wipe-slot=password
  • Reboot.
rd.auto rd.luks=1
add_dracutmodules+=" crypt systemd-pcrphase tpm2-tss "
#!/bin/bash
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is part of systemd.
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
#
# systemd is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with systemd; If not, see <https://www.gnu.org/licenses/>.
#
# Copied from https://man.archlinux.org/man/loader.conf.5 on 2024-05-05.
# Modifications:
# - Tuned the certificate CN to include the FQDN.
# - Removed encryption on the pem files.
# - Increased the certificate validity to 10 years.
uuid=$(systemd-id128 new --uuid)
for key in PK KEK db; do
openssl req -new -x509 -subj "/CN=$(hostname --fqdn) ${key}/" -keyout "${key}.key" -out "${key}.pem" -nodes -days 3652
openssl x509 -outform DER -in "${key}.pem" -out "${key}.der" -nodes
sbsiglist --owner "${uuid}" --type x509 --output "${key}.esl" "${key}.der"
done
# See also: Windows Secure Boot Key Creation and Management Guidance[4]
curl --location \
"https://go.microsoft.com/fwlink/p/?linkid=321192" -o ms-db-2011.der \
"https://go.microsoft.com/fwlink/p/?linkid=321185" -o ms-kek-2011.der \
"https://go.microsoft.com/fwlink/p/?linkid=321194" -o ms-uefi-db-2011.der \
"https://go.microsoft.com/fwlink/p/?linkid=2239776" -o ms-db-2023.der \
"https://go.microsoft.com/fwlink/p/?linkid=2239775" -o ms-kek-2023.der \
"https://go.microsoft.com/fwlink/p/?linkid=2239872" -o ms-uefi-db-2023.der
sha1sum -c <<END
580a6f4cc4e4b669b9ebdc1b2b3e087b80d0678d ms-db-2011.der
31590bfd89c9d74ed087dfac66334b3931254b30 ms-kek-2011.der
46def63b5ce61cf8ba0de2e6639c1019d0ed14f3 ms-uefi-db-2011.der
45a0fa32604773c82433c3b7d59e7466b3ac0c67 ms-db-2023.der
459ab6fb5e284d272d5e3e6abc8ed663829d632b ms-kek-2023.der
b5eeb4a6706048073f0ed296e7f580a790b59eaa ms-uefi-db-2023.der
END
for key in ms-*.der; do
sbsiglist --owner 77fa9abd-0359-4d32-bd60-28f4e78f784b --type x509 --output "${key%der}esl" "${key}"
done
# Optionally add Microsoft Windows certificates (needed to boot into Windows).
cat ms-db-*.esl >>db.esl
# Optionally add Microsoft UEFI certificates for firmware drivers / option ROMs and third-party
# boot loaders (including shim). This is highly recommended on real hardware as not including this
# may soft-brick your device (see next paragraph).
cat ms-uefi-*.esl >>db.esl
# Optionally add Microsoft KEK certificates. Recommended if either of the Microsoft keys is used as
# the official UEFI revocation database is signed with this key. The revocation database can be
# updated with fwupdmgr(1).
cat ms-kek-*.esl >>KEK.esl
attr=NON_VOLATILE,RUNTIME_ACCESS,BOOTSERVICE_ACCESS,TIME_BASED_AUTHENTICATED_WRITE_ACCESS
sbvarsign --attr "${attr}" --key PK.key --cert PK.pem --output PK.auth PK PK.esl
sbvarsign --attr "${attr}" --key PK.key --cert PK.pem --output KEK.auth KEK KEK.esl
sbvarsign --attr "${attr}" --key KEK.key --cert KEK.pem --output db.auth db db.esl
layout=uki
uki_generator=ukify
initrd_generator=dracut
mok_signing_key=/etc/kernel/secure-boot.key.pem
mok_certificate=/etc/kernel/secure-boot.cert.pem
[UKI]
Cmdline=@/etc/kernel/cmdline
OSRelease=@/etc/os-release
SecureBootPrivateKey=/etc/kernel/secure-boot.key.pem
SecureBootCertificate=/etc/kernel/secure-boot.cert.pem
[PCRSignature:initrd]
Phases=enter-initrd
PCRPrivateKey=/etc/kernel/pcr-initrd.key.pem
PCRPublicKey=/etc/kernel/pcr-initrd.pub.pem
[PCRSignature:system]
Phases=enter-initrd:leave-initrd enter-initrd:leave-initrd:sysinit enter-initrd:leave-initrd:sysinit:ready
PCRPrivateKey=/etc/kernel/pcr-system.key.pem
PCRPublicKey=/etc/kernel/pcr-system.pub.pem
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment