05 · sealing
Sealing
The sealed lane: a signed boot chain over a verity-enforced composefs root, with TPM2 disk unlock bound to the key that signed the boot measurement.
Signing proves who built an artifact. Sealing proves that the artifact which booted is the one that was signed, and binds disk unlock to the key behind that proof, so the binding survives every kernel update. The trust chain covers the keys and certificates; this page covers the stack they hold up.
The sealed image is what installs to disk. GRUB, shim and bootupd are absent from the image, so base’s unsealed tags are substrates an instance layers on (D27).
Everything hangs on the one signed binary: change the root by a byte and the digest on its cmdline stops matching, and only a kernel this estate signed can satisfy the policy that releases the disk key.
The seal recipe
swaps in systemd-boot, signs it, then runs bootc container ukify over the
rootfs and checks the result structurally without booting it.
The composefs digest it computes lands on the UKI’s measured .cmdline,
and a PCR11 policy signed with the estate’s policy key lands in the UKI’s
.pcrsig and .pcrpkey sections. That policy authorises the signer of an
expected boot measurement, not a raw register value, which is why a LUKS2 slot
bound to it survives a kernel update. At runtime the root is a composefs mounted
verity=require, and the kernel fs-verity-checks every object it reads.
The sealed boot suite
A sealed candidate earns :stable-sealed by booting.
sealed-boot
installs it to a disk and boots it under enforcing Secure Boot with a throwaway
platform key and no shim, enrolling the boot certificate lifted out of the UKI’s
own signature, so the gate never touches a private key. Its central assertion is
the install-time digest match: the composefs= digest signed into the UKI equals
the fs-verity digest the installer actually laid down,
checked three independent ways inside the running guest.
Each line is read out of the running guest, so the tag records a boot that happened rather than a property of the image on the shelf.
Two gates run beside the boot. pcr11-anchor-gate compares the sealed UKI’s
policy key against the anchor committed in the repo, and autoenroll boots the
candidate from firmware Setup Mode to confirm it enrolls its own Secure Boot
keys and comes up enforcing.
Both promotions
hard-need all three gates.
Unlock bound to the signer
A LUKS2 slot enrolled with systemd-cryptenroll --tpm2-public-key --tpm2-public-key-pcrs=11 opens for any boot state the paired key signed.
Enrolment needs no signature; unlock does, and sd-stub hands it over from the
UKI’s baked .pcrsig, so there is no /etc/crypttab entry for root and nothing
else to stage. The enrolled public key therefore has to be the exact half of the
key that signed that UKI’s policy. Enroll any other and the token looks valid,
the TPM refuses to release the volume, and the host falls back to its recovery
passphrase: degraded, not bricked.
tpm-rebind.sh
derives the key from the running UKI and fails closed when a supplied path
disagrees; the operator procedure it backs is
docs/tpm-rebind.md.
The PCR11 policy key is a raw public key with no certificate around it, and its published fingerprint is the anchor. Which posture a host settles into, and how the recovery passphrase gets in, is the install path.
What is proven, and what is not
Every promotion hangs off a real boot of the sealed stack,
which an estate host runs on metal. The
example lanes build on the unsealed base:stable and
carry no seal component; whether they can install to disk at all is open,
tracked at
example#2.
The install-and-rebind cycle on TPM2-LUKS is rehearsed end to end outside the promotion gates, on the pipeline’s own schedule.