fix(docs): correct the MOK removal steps, drop two dead pins, fix the LSM audit trail #34
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "fix/policy-and-doc-accuracy"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Five factual errors plus one process gap, ranked by user impact. No shipped-config change — both removed pins are absent from every published
final.config, and an off-pin for a symbol kconfig doesn't know is a no-op throughmerge_config+olddefconfig.1.
uninstall.mdtold users to delete the wrong certificate ⚠️That path is the machine's own shim/DKMS MOK — the key Debian's
dkmsgenerates and signs local modules with. Following the section verbatim queues deletion of the user's key (breaking every DKMS module: NVIDIA, ZFS, VirtualBox) while leaving the project certificate enrolled — the exact inverse of the section's stated purpose. And the preceding line, "identify the project key", primes the reader to believe the listed path is ours, so the "adjust path per setup" hedge doesn't save it.Rewritten around
unredacted-hardened.derwith a fingerprint cross-check before deleting, an explicit warning, ordering guidance (remove packages first — once the key is gone, modules signed by it won't load), and a verification command. Cross-referenced fromsecure-boot.md(which documented import but never removal) anddkms.md.2. Two intent pins targeted symbols that no longer exist upstream
MODULE_SIG_KEY_TYPE_ECDSAandGCC_PLUGIN_STRUCTLEAKare absent from everyfinal.configinreleases/, across both the 7.0 and 7.1 lines. The fidelity assertion implements "off" as noSYM=line present, so absence satisfies the pin — both have been silent no-ops for the entire published history and could never fail.The POLICY headings had to change too, not just the prose: a stale backticked assertion in POLICY keeps passing forever for the same absence-satisfies reason, so those headings were load-bearing. The ECDSA rationale is kept as history — the upstream choice now offers RSA plus the post-quantum ML-DSA variants, and only the selected member needs pinning because kconfig
choiceis exclusive.3. POLICY.md claimed LoadPin and SafeSetID were active LSMs
CONFIG_LSM="landlock,lockdown,yama,integrity,bpf"— neitherloadpinnorsafesetidis in it, and per the kernel's own help text any LSM left off that list is ignored. SoLOADPIN_ENFORCE=yreads as "kernel file loads are pinned to one verified filesystem" while nothing of the kind is in effect.integrityis inert too: the subsystem is built, butCONFIG_IMAandCONFIG_EVMare both off.POLICY now separates active from compiled-in-but-inactive and says why the pins stay (so the symbols can't silently vanish; so activation is later a one-line edit).
Deliberately not resolved by editing
CONFIG_LSM. Enabling an enforcing LSM on users' machines to fix a documentation inconsistency inverts the risk: withMODULE_SIG_FORCE=ythere's no soft-fail path, and on hardware we don't control the failure mode ranges from a module refusing to load to an unbootable system. Recorded as an open decision with boot-matrix preconditions inconfig-architecture.md— notDECISIONS.md, which is untracked.4. Two doc examples would rewrite the current line's seed to an old series
tools/bump-seed.sh 7.0.10.hardened1-1(inconfig-architecture.mdandfirst-build.md) defaults to theupstream-seed.tomlsymlink — nowseeds/7.1.toml— so following either verbatim rewrites the 7.1 pin to a 7.0 config.bump-seed.shwarns on a series boundary but still performs the rewrite. Both now pass--pin-fileexplicitly and explain why.5. Prose describing constraints that no longer exist
config-architecture.mdsaidUSERFAULTFDis "kept on with sysctl restriction". It's compiled out;intent.configand the shipped config agree.intent.configandPOLICY.mdboth said a compressed module's signature can't be stripped — "a known caveat".strip-signatures.shdecompresses.ko.xz/.gz/.zstand strips the inner stream. No caveat remains.intent.config,configure-kernel.sh's header, andconfig-architecture.md.tools/reconcile-vs-anthraxx.shinstead. The seed is fetched at build time and isn't in this tree, so such claims are unverifiable from here and go stale silently — replacing one unverifiable claim with another would just recreate the defect.6. The release un-draft step appeared in no runbook
build-kernel.ymlcreates the release as a draft by design, andpublish.ymlreads draft assets via the token — so the registry publish succeeds while it's still a draft, which is precisely why the step is easy to miss. Nothing un-drafts it.No user-facing breakage today: I checked the live server — releases show as
releasedand an anonymous GET ofunredacted-hardened.derreturns HTTP 200, so this is being done by hand. The defect is that a required manual step was undocumented. Added asfirst-build.mdstep 10a, resolving the id from the LIST endpoint (the by-tag lookup filters drafts on some Forgejo versions — same reasonbuild-kernel.ymlalready avoids it) plus a credential-free reachability check before announcing.Verification
intent-matches-policy(extracted and run): passes. It caught a mistake mid-edit — I'd written a backtickedCONFIG_INTEGRITY=yin prose, which Direction 1 reads as an assertion requiring anintent.configpin. Reworded; a sweep for any other backtickedCONFIG_X=valuein POLICY lacking an intent pin now comes back empty.final.config:OK (90 intent lines, all landed).intent.configoff-pins vs the newestfinal.config: clean (was 2).shellcheck,py_compile,yamllint -d relaxedall clean.Note for review
tools/check-hardening-drift.sh:10's "~59 symbols" is left alone here because #30 already rewrites that line (to 92). After both land it should read 90 — one-word follow-up, flagged rather than creating a conflict between the two branches.🤖 Generated with Claude Code
Five factual errors, ranked by user impact. 1. docs/users/uninstall.md told users to delete the WRONG certificate. sudo mokutil --list-enrolled # identify the project key sudo mokutil --delete /var/lib/shim-signed/mok/MOK.der # adjust path per setup That path is the machine's OWN shim/DKMS MOK — the key Debian's dkms generates and signs local modules with. Following the section verbatim queues deletion of the user's key (breaking every DKMS module: NVIDIA, ZFS, VirtualBox) while LEAVING the project certificate enrolled: the exact inverse of the section's purpose. The line above it ("identify the project key") primes the reader to believe the listed path IS ours, so the "adjust path per setup" hedge does not help. Rewritten around unredacted-hardened.der with a fingerprint cross-check before deleting, an explicit warning, ordering guidance (remove packages first), and a verification command. Cross-referenced from secure-boot.md (which documented import but never removal) and dkms.md. 2. Two intent pins targeted symbols that no longer exist upstream. MODULE_SIG_KEY_TYPE_ECDSA and GCC_PLUGIN_STRUCTLEAK are absent from every final.config in releases/, across both the 7.0 and 7.1 lines. The fidelity assertion implements "off" as "no SYM= line present", so absence SATISFIES the pin: both have been silent no-ops for the entire published history and could never fail. Removed, with the POLICY headings rewritten too — a stale backticked assertion in POLICY keeps passing forever for the same reason, so the headings were load-bearing, not cosmetic. The ECDSA rationale is kept as history: the upstream choice now offers RSA plus the post-quantum ML-DSA variants, and only the selected member needs pinning because kconfig `choice` is exclusive. 3. POLICY.md claimed LoadPin and SafeSetID were active LSMs. They are not. CONFIG_LSM="landlock,lockdown,yama,integrity,bpf" — neither loadpin nor safesetid is in it, and the kernel ignores any LSM left off that list. So LOADPIN_ENFORCE=y reads as "kernel file loads are pinned to one verified filesystem" while nothing of the kind is in effect. `integrity` is inert too (subsystem built, IMA and EVM both off). POLICY now separates active from compiled-in-but-inactive and says why the pins stay. Deliberately NOT resolved by editing CONFIG_LSM: enabling an enforcing LSM on users' machines to fix a documentation inconsistency inverts the risk. With MODULE_SIG_FORCE=y there is no soft-fail path, and the failure mode on hardware we don't control ranges from a module refusing to load to an unbootable system. Recorded as an open decision with boot-matrix preconditions in config-architecture.md (not DECISIONS.md, which is untracked). 4. Two doc examples would rewrite the CURRENT line's seed to an OLD series. `tools/bump-seed.sh 7.0.10.hardened1-1` in config-architecture.md and first-build.md defaults to the upstream-seed.toml symlink — now seeds/7.1.toml — so following either verbatim rewrites the 7.1 pin to a 7.0 config. bump-seed.sh warns on a series boundary but still performs the rewrite. Both now pass --pin-file explicitly and say why. 5. Stale prose describing constraints that no longer exist. - config-architecture.md said USERFAULTFD is "kept on with sysctl restriction"; it is compiled out (intent.config and the shipped config agree). - intent.config and POLICY.md both said a compressed module's signature cannot be stripped, "a known caveat". strip-signatures.sh decompresses .ko.xz/.gz/.zst and strips the inner stream — no caveat remains. - Counts: 92 -> 90 setting lines after the two removals (17 deltas + 73 insurance), in intent.config, configure-kernel.sh's header, and config-architecture.md. - Claims about what anthraxx selects "today" are replaced with pointers to tools/reconcile-vs-anthraxx.sh: the seed is fetched at build time and is not in this tree, so such claims are unverifiable from here and go stale silently. Replacing one unverifiable claim with another would just recreate the defect. 6. The release un-draft step appears in no runbook. build-kernel.yml creates the release as a draft by design; publish.yml reads draft assets via the token, so the registry publish succeeds while it is still a draft — which is why the step is easy to miss. Nothing un-drafts it. Live releases DO show as published and the .der returns HTTP 200, so there is no user-facing breakage today; the gap is that a required manual step was undocumented. Added as first-build.md step 10a, with the id resolved from the LIST endpoint (the by-tag lookup filters drafts on some Forgejo versions) and a credential-free reachability check before announcing. No shipped-config change: both removed pins are absent from every published final.config, and an off-pin for a symbol kconfig does not know is a no-op through merge_config + olddefconfig. Note for review: tools/check-hardening-drift.sh:10's "~59 symbols" is left alone here because #30 already rewrites that line (to 92). After both land it should read 90. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>