Boot Chain
cache22 boots through one of two chains, depending on the firmware mode detected at install time.
UEFI (default)
firmware (UEFI)
-> shim-stub or PK/KEK/db verification
-> /efi/EFI/systemd/systemd-bootx64.efi (signed by cache22 SB key)
-> /efi/EFI/Linux/cache22-<csum>.efi (signed by cache22 SB key)
-> kernel + initramfs + cmdline
-> ostree-prepare-root sets up /sysroot
-> systemd in real root
Legacy BIOS
firmware (BIOS)
-> MBR boot code (GRUB stage 1)
-> BIOS-boot partition (GPT type ef02): GRUB stage 1.5
-> dedicated /boot ext4 partition (cache22-boot)
-> /grub/grub.cfg sets $root to the /boot partition UUID
-> blscfg reads /loader/entries/
-> kernel + initramfs + cmdline (from ostree BLS entry)
-> ostree-prepare-root sets up /sysroot
-> systemd in real root
/boot lives on its own ext4 partition so GRUB never has to traverse btrfs subvolumes (or any filesystem-specific quirks) to find grub.cfg and the BLS entries. The 1 MiB BIOS-boot partition holds raw GRUB stage 1.5 only; everything else (modules, grub.cfg, kernels, initramfses, BLS entries) lives on the /boot partition.
cache22-bios-initramfs assembles each deploy’s full initramfs set (microcode + user override or base + extra segments, the same set the UEFI UKI carries) into one combined initrd per BLS entry at /cache22/<entry>/initrd.img on the /boot partition, and points the entry’s initrd= line at it. GRUB loads the single file and the kernel unpacks the concatenated cpio segments. See INITRAMFS.md.
There is no signature verification on BIOS (no firmware Secure Boot mechanism), no UKI (no UEFI to load the PE-format binary), and no TPM2 LUKS unlock (depends on PCR measurements made by sd-stub during the UEFI chain). The rest of this page describes the UEFI chain only.
Every PE binary loaded by firmware is signed by the per-machine cache22 SB key. The firmware enrolls cache22’s keys (PK + KEK + db) plus Microsoft’s DB keys on first boot when the firmware is in setup mode. After enrollment, Secure Boot enforcement engages.
Per-machine keys
Generated by /usr/libexec/cache22/sb-key-init at install time, which calls sbctl create-keys and openssl genpkey. Stored at:
/var/lib/cache22/sbkey/keys/PK/PK.{key,pem,der} # Platform Key
/var/lib/cache22/sbkey/keys/KEK/KEK.{key,pem,der} # Key Exchange Key
/var/lib/cache22/sbkey/keys/db/db.{key,pem,der} # SB signing key (db)
/var/lib/cache22/sbkey/tpm-pcr11.{key,pub} # TPM PCR-policy key
Mode 0700 on the directory and 0600 on the private keys. The directory lives on the root filesystem, which is LUKS-encrypted in the recommended install. Keys are at-rest encrypted.
The TPM PCR-policy key is separate from the SB signing key. Both are per-machine. The TPM key is used to sign predicted PCR 11 values that are embedded in each UKI’s .pcrsig section, so the TPM accepts UKI updates without LUKS re-enrollment.
Firmware DB enrollment
cache22 enrolls its own PK + KEK + db plus Microsoft DB keys into firmware. Microsoft keys are kept so dual-boot Windows, fwupd-signed firmware updates, and signed option ROMs all keep working.
Enrollment is automatic on first boot when sd-boot detects firmware in setup mode and secure-boot-enroll = force in /efi/loader/loader.conf.
For the first-boot setup procedure see First-Boot Secure Boot Setup.
UKI structure
A cache22 UKI is a single PE binary at /efi/EFI/Linux/cache22-<csum>.efi containing these PE sections:
| Section | Content |
|---|---|
.linux |
Linux kernel binary. |
.initrd |
Initramfs. |
.cmdline |
Kernel command line, including ostree=/ostree/boot.X/<state>/<csum>/0. |
.osrel |
os-release content with VERSION_ID= set so sd-boot’s auto-default picks the right deploy. |
.pcrsig |
Signed predictions for what PCR 11 will be after sd-stub measures this UKI. Signed by tpm-pcr11.key. |
.uname |
Kernel version string. |
The whole UKI is signed by the per-machine SB key (db.key). The signature covers all PE sections, so any modification to kernel, initramfs, or cmdline invalidates it.
sd-stub measurement
When sd-boot loads a cache22 UKI, sd-stub (the UEFI stub embedded in the UKI) runs first. It measures these into PCR 11 before transferring control to the kernel:
| Measurement | What | Required for |
|---|---|---|
| PCR 11.0 | .linux + .initrd + .osrel etc. |
TPM unseal under PCR 11 binding. |
| PCR 11.1 | .cmdline |
Same. |
| PCR 11.2 | .pcrsig external signing pubkey |
Same. |
PCR 11 ends up at a value the UKI’s own .pcrsig predicts. If LUKS has a TPM2 keyslot bound to a signed PCR 11 policy (cache22-encryption enroll), the TPM verifies the signature and releases the key. LUKS unlocks.
The signed-policy approach means LUKS does not need re-enrollment when the UKI is rebuilt. Each new UKI ships its own .pcrsig signed by the same per-machine key.
kargs split
Kernel command-line arguments come from two sources, both compiled into the UKI’s .cmdline section by resign-uki:
- Image-default kargs. Defined at
/usr/lib/bootc/kargs.d/*.tomlin the image. Applied to all installs. - Per-machine kargs. Defined at
/etc/cache22/extra-cmdline, written bycache22-install(root UUID, LUKS UUID, btrfs subvol) and editable bycache22-karg.
The two sources are concatenated into the UKI’s signed .cmdline. sd-stub ignores any external cmdline override under SB enforcement.
To add or modify per-machine kargs see Kernel Args.
Per-deploy UKI build
/usr/libexec/cache22/resign-uki runs:
- During the shutdown sequence right after
ostree-finalize-staged.servicewrites the BLS entry, via the50-cache22-uki.confdrop-in. This is the path for normal updates. - Explicitly by
cache22-reboot --kexecbefore the kexec is triggered. - When
/etc/cache22/extra-cmdlineis modified, via thecache22-resign-uki.pathwatcher. - On demand via
sudo systemctl start cache22-resign-uki.service.
For each live ostree deploy, resign-uki walks /sysroot/ostree/deploy/<state>/deploy/<csum>/usr/lib/modules/<kver>/, assembles the cmdline (image-default kargs + user kargs from extra-cmdline + the ostree=... path), and runs ukify build with --secureboot-private-key, --secureboot-certificate, --pcr-private-key, --pcr-public-key. The signed .efi is atomically written to /efi/EFI/Linux/cache22-<csum>.efi.
Stale UKIs (UKIs for deploys that no longer exist) are garbage-collected only after every desired UKI is confirmed present. This avoids leaving the ESP without a bootable UKI even on partial failures.
sd-boot configuration
/efi/loader/loader.conf is set up by cache22-install:
default *-cache22-*.efi
timeout 3
console-mode auto
editor no
secure-boot-enroll force
auto-firmware yes
editor no disables sd-boot’s command-line editor. Combined with sd-stub’s refusal to honor external cmdline overrides under SB, this means the kernel cmdline is fully fixed by the signed UKI.
auto-firmware yes shows the “Reboot Into Firmware Interface” entry on systems that support it (most modern UEFI). Useful for entering firmware setup without holding a key at power-on.
sd-boot updates
resign-uki also re-signs and re-installs sd-boot whenever the in-image binary at /usr/lib/systemd/boot/efi/systemd-bootx64.efi is newer than the on-ESP copy. systemd’s stock systemd-boot-update.service is masked because it would copy the unsigned upstream binary over our locally-signed copy.
See also
- cache22-secureboot for managing keys.
- TPM and LUKS for unlock policy options.
- Threat Model for what this configuration protects.
- Per-Deploy UKI Build for the build pipeline internals.