Build a TPM2 Boot Policy with systemd-pcrlock
systemd-pcrlock writes a TPM2 access policy that can survive a firmware or kernel update, instead of a fragile seal to fixed PCR values. This guide inspects the measurements and component definitions it can use, tests a prediction, then walks through the guarded command that actually writes the policy. The examples target systemd 255, including Ubuntu's 255.4-1ubuntu8.17 package.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow 15 to 30 minutes for inspection. Creating a policy needs more care than the commands themselves: it changes TPM state and can affect access to secrets such as an encrypted disk key. You need a UEFI system with a working TPM2 device, current-boot event logs, root privileges for commands that write under /var, and a recovery plan for firmware or bootloader changes.
1. Confirm the executable and TPM inputs
The installed executable is normally addressed by its absolute path, /usr/lib/systemd/systemd-pcrlock. This version is experimental, so its interface and behaviour may change. Check the version and both event-log paths before doing anything that writes state:
/usr/lib/systemd/systemd-pcrlock --version
test -r /sys/kernel/security/tpm0/binary_bios_measurements && echo "UEFI event log: readable"
test -r /run/log/systemd/tpm2-measure.log && echo "userspace event log: readable"
On the reference installation, the first command reports systemd 255 with TPM2 support in its feature set. A missing event log or TPM device is a stopping point, not an invitation to guess.
Tip
Predictions are deliberately conservative. Unrecognised measurements, mismatches with current PCR values, or components absent from the event log simply keep those PCRs out of the result, rather than guessing.
Checkpoint
Continue only when the machine has a TPM2 device and the logs belong to the boot you are analysing.
2. Inspect the current evidence
Start with a read-only component listing. Definitions are .pcrlock files found in the default directories, including /etc/pcrlock.d/, /run/pcrlock.d/, /var/lib/pcrlock.d/, /usr/local/pcrlock.d/ and /usr/lib/pcrlock.d/.
/usr/lib/systemd/systemd-pcrlock list-components --no-pager
The default location range is 760-:940-, covering the normal userspace runtime window from the initrd-side components towards the main system runtime. Narrow the analysis to a particular phase when needed:
/usr/lib/systemd/systemd-pcrlock list-components \
--location=760-:940- --no-pager
Next, inspect the combined event log and its PCR matching. JSON is useful for saving output to review later; it does not create a policy:
/usr/lib/systemd/systemd-pcrlock log --json=pretty --no-pager
For a machine-readable event log in TCG Common Event Log Format, use cel instead. A successful command returns exit status 0. Treat a failure here as something to resolve before predicting.
3. Predict without changing TPM state
Run predict before make-policy, never the other way round. By default it considers PCRs 0 to 5, 7, and 11 to 15; select a narrower set explicitly when testing one measured boundary:
/usr/lib/systemd/systemd-pcrlock predict \
--pcr=7 --pcr=11 --json=pretty --no-pager
The result is a prediction for every combination of recognised component variants, which lets one policy cover several trusted kernel or bootloader versions at once. A PCR is omitted when the current evidence cannot support a complete prediction: a safety feature, since a policy claiming to cover an unknown measurement would be misleading.
Checkpoint
The prediction must include every PCR you intend to bind to a secret. If it does not, fix the event-log or component-definition problem first; adding --force does not make an incomplete prediction safe.
4. Add only the protections you can maintain
Protection commands generate or remove component definitions under /var/lib/pcrlock.d/. They are root operations, and they change what future predictions accept.
lock-firmware-coderecords firmware and extension-card measurements in PCRs 0 and 2. It becomes stale after a firmware update.lock-firmware-configrecords PCRs 1 and 3. Use this only on hardware whose firmware measurements are stable; volatile values such as temperatures can make it unreliable.lock-secureboot-policyrecords the Secure Boot variables that contribute to PCR 7.lock-secureboot-authorityrecords the authorities actually used to validate the boot path.lock-gptrecords the partition table;lock-ukiorlock-perecords selected boot binaries.lock-machine-idandlock-file-systemcover identity measurements in PCR 15;lock-kernel-cmdlineandlock-kernel-initrdcover kernel measurements in PCR 9.
For example, this read-and-write pair is intentionally explicit:
sudo /usr/lib/systemd/systemd-pcrlock lock-machine-id
sudo /usr/lib/systemd/systemd-pcrlock lock-gpt
sudo /usr/lib/systemd/systemd-pcrlock list-components --no-pager
Warning
Do not lock firmware configuration casually. Before an approved firmware or Secure Boot change, remove the relevant generated definition with its matching unlock- command (each lock- command has a paired unlock- command), make the change, then regenerate it and run a fresh prediction. That command deletes files, so record it and review the component list before continuing.
5. Write the TPM2 policy
Warning
This is the state-changing step. make-policy allocates a TPM2 NV index on first use and updates it later. The metadata is written to /var/lib/systemd/pcrlock.json. The TPM must implement PolicyAuthorizeNV, specified by this systemd version as TPM 2.0 version 1.38 or newer.
sudo /usr/lib/systemd/systemd-pcrlock make-policy \
--recovery-pin=yes --json=pretty --no-pager
The recovery PIN is requested interactively and stored in encrypted form in the policy metadata; keep it in an approved password store. Without --recovery-pin=yes, systemd-pcrlock generates a PIN automatically and stores it encrypted too, but that is less convenient when you need to recover a policy after a boot change.
Verify the metadata and the resulting policy without forcing another write:
sudo test -s /var/lib/systemd/pcrlock.json
sudo /usr/lib/systemd/systemd-pcrlock predict --json=pretty --no-pager
Repeated make-policy calls stop quickly when the prediction is unchanged. --force overrides that optimisation and writes the policy again; reserve it for a specific operational reason.
6. Recover from an intentional change
If a policy is no longer usable, use the recovery PIN through a deliberate maintenance path, then refresh the component definitions and run make-policy again.
Recovery
If the policy should be abandoned entirely, the irreversible-looking command is:
sudo /usr/lib/systemd/systemd-pcrlock remove-policy
This deletes /var/lib/systemd/pcrlock.json and deallocates the NV index. Confirm no disk-encryption or other TPM-protected consumer still depends on it before running the command. A backup of the JSON file alone does not restore an NV index or undo changes made inside the TPM.
Done means
- Inputs available. The TPM2 device and both current-boot event logs are readable.
- Read-only steps succeed.
list-components,logandpredictall work. - Coverage confirmed. The prediction contains every PCR the intended consumer requires.
- Protections match maintenance capacity. Firmware, Secure Boot and identity locks match changes you can actually keep up with.
- Recovery agreed. The recovery PIN and policy metadata have a protected recovery path.
- Policy written once. The NV-backed policy was written, verified, and not forced without a reason.