How to Define Resilient TPM2 Prediction Policies on Ubuntu Server Using systemd-pcrlock

Modern Linux server security relies heavily on the Trusted Platform Module (TPM2) to seal full-disk encryption keys and verify measured boot integrity. In conventional TPM2 sealing configurations, LUKS disk encryption keys are bound to specific Platform Configuration Registers (such as PCR 0 for firmware, PCR 2 for option ROMs, and PCR 7 for Secure Boot state). However, conventional PCR sealing suffers from operational fragility: minor UEFI firmware updates, kernel driver changes, or shim upgrades alter measured hash values, causing TPM unsealing to fail and stranding headless servers at emergency recovery prompts. With systemd version 255 and newer on Ubuntu Server, systemd-pcrlock resolves this dilemma by introducing declarative, resilient PCR prediction policies.

Instead of hardcoding single transient hash digests into the TPM policy, systemd-pcrlock compiles multi-variant event-log policies into cryptographic lock files signed by a local policy authority. This architecture allows system administrators to pre-authorise upcoming firmware versions, alternative kernels, and boot paths while maintaining rigorous cryptographic guarantees against bootkit tampering and unauthorised storage access.

Understanding PCR Prediction and the Event Log Architecture

During the Unified Extensible Firmware Interface (UEFI) boot process, firmware and bootloaders log every executed component into the TCG (Trusted Computing Group) event log while extending corresponding measurements into TPM2 PCR banks. A single PCR value represents a cumulative SHA-256 hash calculated recursively across all extended events. If an event sequence changes even by a single byte, the final PCR digest changes unpredictably.

The systemd-pcrlock framework separates measured events into discrete, predictable classes known as components. Components include firmware measurements (PCR 0), UEFI memory maps and configuration tables (PCR 1), PCI option ROMs (PCR 2), boot device boot managers (PCR 4), GPT partition tables (PCR 5), Secure Boot authorities and certificates (PCR 7), and UKI (Unified Kernel Image) executable hashes (PCR 11 and PCR 12). By parsing past event logs recorded in /sys/kernel/security/tpm0/binary_bios_measurements alongside declared component files, systemd-pcrlock calculates all legitimate mathematical permutations of valid PCR states.

Verifying System Prerequisites and TPM2 Status

Before implementing PCR lock policies on Ubuntu Server, verify that the hardware platform provides a functional TPM 2.0 cryptoprocessor and that systemd is operating at version 255 or later.

Query the TPM2 subsystem and inspect available PCR banks using standard system utilities:

# Verify TPM2 device accessibility
systemd-cryptenroll --tpm2-device=list

# Check systemd version and pcrlock binary availability
systemctl --version | head -n 1
which systemd-pcrlock

Inspect the current live PCR registers to confirm active SHA-256 measurement banks:

# Display live values for standard boot integrity registers
systemd-pcrlock log

The command parses the TCG event log and outputs a detailed tabular stream of every recorded boot phase, displaying measured files, driver loads, and the exact PCR register affected by each operation.

Initialising the Local Policy Authority Keypair

To securely bind PCR lock files to the system without requiring external signing infrastructure, systemd-pcrlock employs an internal asymmetric policy keypair. The private key signs compiled PCR policies, while the public key is enrolled directly into the TPM2 NVRAM or LUKS2 keyslot policy.

Generate the local policy keypair in the standard configuration directory:

# Create systemd pcrlock policy directory
sudo mkdir -p /var/lib/systemd/pcrlock.d
sudo chmod 700 /var/lib/systemd/pcrlock.d

# Generate dedicated RSA or ECC policy signing keys
sudo systemd-pcrlock make-policy

This command generates two critical cryptographic files:

  • /var/lib/systemd/pcrlock.d/pcrlock.pk.key: The private policy key used by administrators to sign future PCR states after system upgrades.
  • /var/lib/systemd/pcrlock.d/pcrlock.pub.key: The public key distributed to LUKS2 header policies to validate authorized boot states.

Ensure permissions on the private key file remain strictly restricted to root access:

sudo chmod 600 /var/lib/systemd/pcrlock.d/pcrlock.pk.key

Defining and Compiling PCR Component Policies

Components define the exact boot characteristics that systemd-pcrlock monitors. Component definition files reside in /var/lib/systemd/pcrlock.d/ and end with the .pcrlock or .pcrlock.d/ extension.

To capture current platform firmware, Secure Boot certificates, and bootloader configurations into a locked policy state, execute the lock generation command:

# Lock all standard boot registers based on current verified event logs
sudo systemd-pcrlock lock-raw --components=firmware,secureboot-policy,gpt

To review compiled component definitions and inspect which variants are recognised as valid, execute:

# List recognized components and policy variants
sudo systemd-pcrlock list-components

When an upcoming kernel or UKI update is staged, administrators can predict and append the measurement of the new kernel binary before rebooting, preventing boot failure:

# Pre-predict measurements for an updated Unified Kernel Image
sudo systemd-pcrlock lock-uki /boot/efi/EFI/Linux/ubuntu-6.8.0-45-generic.efi

Binding LUKS2 Encrypted Volumes to the PCRLock Policy

Once the policy is defined and signed, enroll the policy public key into the target encrypted volume using systemd-cryptenroll. This instructs the TPM to release the volume decryption key only when the presented boot state matches a signature generated by the trusted policy authority.

# Identify the target block device containing the LUKS2 container
lsblk -f

# Enroll TPM2 unlocking bound to pcrlock policy
sudo systemd-cryptenroll \
  --tpm2-device=auto \
  --tpm2-public-key=/var/lib/systemd/pcrlock.d/pcrlock.pub.key \
  --tpm2-pcrlock=/var/lib/systemd/pcrlock.d/ \
  /dev/nvme0n1p3

Verify that the keyslot metadata has successfully recorded the public key requirement:

# Inspect LUKS2 header token assignments
sudo cryptsetup luksDump /dev/nvme0n1p3

The output will show a systemd-tpm2 token containing the base64-encoded public key digest alongside the required PCR lock conditions.

Maintaining PCRLock Policies Across System Upgrades

To prevent operational disruptions when updating Ubuntu packages, integrate policy regeneration into system maintenance workflows.

After installing updated system packages, firmware packages, or bootloader components, recalculate and resign the active policy:

# Re-read live event logs and resign policies following authorized updates
sudo systemd-pcrlock make-policy --force

Ubuntu administrators can automate this process across distribution upgrades by linking policy updates into dpkg post-invoke triggers located in /etc/apt/apt.conf.d/99pcrlock, ensuring every verified kernel installation updates the signed prediction tables seamlessly.

Get the best tech tips delivered straight to your inbox.

Join thousands of readers mastering Apple, Google, Microsoft, and Linux.