Conversation
jetm
force-pushed
the
imx93-var-posture-reporting
branch
2 times, most recently
from
August 20, 2026 15:18
a467c98 to
b66bddb
Compare
jetm
marked this pull request as draft
August 20, 2026 22:05
mobileoverlord
force-pushed
the
imx93-secure-boot-wrynose
branch
from
August 24, 2026 20:34
9308e29 to
5b7f642
Compare
jetm
force-pushed
the
imx93-secure-boot-wrynose
branch
from
August 24, 2026 23:35
61c163c to
a2efa39
Compare
jetm
force-pushed
the
imx93-var-posture-reporting
branch
from
August 25, 2026 16:20
ac87d71 to
09bb9a0
Compare
Both the TPM2 enroll and the TPM2 open in cryptsetup-var.sh fail open on purpose, because a legitimate PCR 7 change must not lock a device out of its own /var. The cost of that choice is silence: a device whose PCR 7 moved keeps booting on the Argon2id recovery slot indefinitely, and from outside it is indistinguishable from a correctly sealed one. At fleet scale that is a security control that can lapse across a whole deployment with no signal anywhere. Record which keyslot actually opened the volume, because that is the one fact nothing can reconstruct later - a luksDump afterwards shows that a TPM2 token exists, not that it was what worked. Pair it with the token's presence and whether /var really came up through the mapper, the latter re-checked in userspace rather than trusted from the initrd because opening the container says nothing about what fstab then mounted; that exact mismatch was a real defect on this board when AVOCADO_VAR_PART_DEV was unset. The initramfs cannot write the KV store directly - neither fw_setenv nor the fw_env.config it needs exists there, and the generator for that config runs in the real root. So the initrd drops the facts in /run, the tmpfs the key file already depends on and which systemd carries across the switch-root, and a rootfs-only unit publishes from there into the U-Boot environment peridiod already reads. Publishing on change only respects that store's documented expectation of infrequent writes, and makes the write itself the interesting event rather than steady per-boot flash wear. Verified by stubbing fw_printenv/fw_setenv/findmnt over six cases: first boot publishes all three keys, an identical second boot writes nothing, a tpm2 -> argon2id regression flips only that key and emits a warning naming PCR 7, a /var mounted off the raw partition reports unencrypted, and an absent posture file or absent libubootenv both exit 0 with a diagnostic. Separately confirmed write_posture cannot abort the unlock: it survives a failing token probe under set -e (the reason it uses `if` rather than `&&`) and an unwritable target. Not yet built or run on hardware - the build hosts were in use. Signed-off-by: Javier Tia <javier@peridio.com>
cryptsetup-var-posture was defined, packaged and set to auto-enable its unit, but nothing in the tree ever pulled it into an image. The RPM built and stopped there: a qemuarm64 encrypted-var rootfs came out with cryptsetup-var-udev in its manifest and no posture package, so avocado-posture-publish.service never reached a device and the posture this branch exists to report was never published anywhere. Add it beside cryptsetup-var-udev in the same encrypted-var branch, which is the condition that already decides whether any of this content is wanted - the posture reporter has nothing to describe on a machine whose /var is not a dm-crypt mapping. The auto-enable was what hid this. A package with SYSTEMD_AUTO_ENABLE looks wired up on inspection, and the unit is genuinely correct; the gap was one level up, in whether anything installed it. Nothing failed, nothing warned, and the built RPM in deploy/rpm made it look present. Signed-off-by: Javier Tia <javier@peridio.com>
The posture keys are written from Linux with fw_setenv and read back with fw_printenv, so nothing in this script's own path suggests U-Boot has an opinion about them. On a machine built with CONFIG_ENV_WRITEABLE_LIST it does, and the failure is silent: env_flags_validate rejects an H_EXTERNAL variable without the 'w' flag so it never reaches env_htab, env_export serialises the hashtable, and the A/B flow calls saveenv on every slot switch. A key absent from CFG_ENV_FLAGS_LIST_STATIC therefore survives until the first OTA and is then gone, with nothing logged and a fleet view that keeps reporting whatever it last saw. Neither this change nor the one adding that permit list is wrong on its own, which is why no review of either would surface it. Recording it against the keys themselves puts the constraint where the next person adds a fourth key, rather than in a commit message nobody reads at that moment. The debt marker rather than the fix because the permit list has not merged yet, so there is no list here to add the entries to. It names the trigger so this cannot leave draft without them. Also note the consequence for consumers: a permitted key is writable from the saved environment by construction, so posture is forgeable by anyone who can write the boot medium. That is acceptable for an observation, and is exactly why it must never be presented as tamper-evident. Signed-off-by: Javier Tia <javier@peridio.com>
jetm
force-pushed
the
imx93-var-posture-reporting
branch
from
August 26, 2026 19:10
09bb9a0 to
7bfba54
Compare
Contributor
|
Merged into wrynose via #316 (rebased, commits preserved). Closing as included. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
cryptsetup-var.shfails open in two places, both deliberately.ensure_tpm2_enroll()keeps the volume encrypted under Argon2id if the TPM2 enroll fails, andopen_var()falls back to the Argon2id key file if the TPM2 unseal fails. That is the right call — a legitimate PCR 7 change (a Secure Boot policy update, a key rotation) must not lock a device out of its own/var.The cost of that choice is silence. A device whose PCR 7 moved keeps booting on the recovery slot indefinitely, and from outside it is indistinguishable from a correctly sealed one: a later
luksDumpshows that a TPM2 token exists, not that it was the thing that worked. At fleet scale that means a storage-security control can lapse across a whole deployment with no signal anywhere.Solution
Record the one fact nothing can reconstruct afterwards — which keyslot actually opened the volume — and publish it, plus the token's presence and whether
/varreally came up through the mapper, into the U-Boot KV store that peridiod already reads.The
/var-through-the-mapper check is re-done in userspace rather than trusted from the initrd on purpose: opening the container says nothing about what fstab then mounted. That exact mismatch was a real defect on this board whenAVOCADO_VAR_PART_DEVwas unset — container opened in the initrd,/varmounted from the raw partition underneath it.Key changes
cryptsetup-var.shgainswrite_posture(), which drops three facts in/run/avocado-var-posture. Best-effort by construction: posture must never be the reason/varfails to open.avocado-posture-publish.sh+.service(rootfs-only) read that file, re-check the mount, and publishavocado_var_unlock,avocado_var_tpm2_token,avocado_var_encrypted.${PN}-posturepackage so the publisher stays out of the initrd.Two design points worth calling out for review:
Why
/runand not the KV store directly from the initrd. Neitherfw_setenvnor thefw_env.configit needs exists in this initramfs (see the recipe's own RDEPENDS note), andavocado-uboot-env.servicegenerates that config in the real root./runis the same early tmpfs the key file already depends on, and systemd carries it across the switch-root.Why publish on change only. The KV backend is a U-Boot environment block on the boot medium and its own docs expect infrequent writes. Re-writing three keys every boot would be steady flash wear for no new information, and a change is the interesting event — the first enroll, or the day a device silently drops to the recovery slot.
The unit orders
After=avocado-uboot-env.servicewithout requiring it: that unit lives inmeta-avocado-nxpwhile this ships in the shared layer, so it has to stay valid on a machine with no such generator. systemd ignoresAfter=on a non-existent unit, and the script exits 0 when the tooling is absent.Verification
Not built and not run on hardware — the build hosts were in use. What was verified, by stubbing
fw_printenv/fw_setenv/findmnt:tpm2->argon2idregression/varon the raw partitionavocado_var_encrypted=noSeparately confirmed
write_posturecannot abort the unlock: it survives a failing token probe underset -e(which is why it usesifrather than&&— the&&form is the footgun here) and survives an unwritable target.shellcheck --shell=shis clean on both scripts.Reviewer notes
Three things I could not verify without a build, listed so they get eyes rather than assumed:
PACKAGESorder.FILES:${PN}globs the whole${libexecdir}/cryptsetup-var/directory and so also matches the publisher;PACKAGES =+prepends, so${PN}-postureclaims it first and it stays out of the initrd. I documented that in the recipe, but documenting is not testing — worth confirming the publisher is genuinely absent from the initramfs after a build.libubootenvandutil-linux-findmntresolving as RDEPENDS.avocado_*keys to Peridio cloud, or that anything cloud-side surfaces them. So this makes posture durable and queryable on-device; the fleet-visible half is a further step.Scope stayed at LUKS/TPM deliberately. No AHAB lifecycle or fuse-state reporting here — I have no verified userspace path to read the ELE lifecycle and would have been guessing at one.