From 1b86750b0d2929d68c9845966be9e9b823779c5d Mon Sep 17 00:00:00 2001 From: Julian Cuni Date: Tue, 30 Jun 2026 15:21:32 +0200 Subject: [PATCH] docs(wiki): firmware/dbx-vs-TPM hardening + create disk-os-hardening page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Real-world park-buzi episode: a UEFI dbx update (delivered via fwupd/LVFS, NOT apt) revoked a stale GRUB -> panic, and moved PCR 7 -> broke TPM-sealed LUKS auto-unlock -> passphrase prompt. Recovered by re-sealing PCR 7. - appliance-provisioning.md: extend the §4 re-seal runbook to name dbx; new §4a (fwupd-not-apt, GRUB-panic ordering, PCR-7 re-seal, operator lockdown: mask fwupd + remove firmware-updater snap + BIOS-password + passphrase escrow) incl. the --test-passphrase-silently-passes-via-TPM trap (--disable-external-tokens); gotchas #12/#13. - disk-os-hardening.md: NEW — resolves a long-dangling wikilink referenced from ~18 pages. The *why* of host hardening (5 controls + firmware lockdown); commands stay in appliance-provisioning; reconciliation remains the primary anti-fraud control. - index.md: expand the disk-os-hardening catalog line. - log.md: two note entries. Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V --- wiki/decisions/appliance-provisioning.md | 76 ++++++++++++++++++-- wiki/decisions/disk-os-hardening.md | 92 ++++++++++++++++++++++++ wiki/index.md | 2 +- wiki/log.md | 35 +++++++++ 4 files changed, 200 insertions(+), 5 deletions(-) create mode 100644 wiki/decisions/disk-os-hardening.md diff --git a/wiki/decisions/appliance-provisioning.md b/wiki/decisions/appliance-provisioning.md index 6904140..91e7ee3 100644 --- a/wiki/decisions/appliance-provisioning.md +++ b/wiki/decisions/appliance-provisioning.md @@ -2,7 +2,7 @@ type: reference tags: [parking, deployment, appliance, hardening, runbook, offline-first] sources: [] -updated: 2026-06-27 +updated: 2026-06-30 status: settled --- @@ -103,9 +103,67 @@ sudo reboot - Still prompts = PCR mismatch; type the passphrase (NOT locked out), then retry with `--tpm2-pcrs=0`. The `password` slot + `crypttab.bak` make this fully reversible. -> **Re-seal runbook:** a BIOS update / Secure Boot change alters PCR 7 → the TPM refuses → boot -> falls back to the passphrase prompt (not a brick). After such a change, re-run step 4's -> `systemd-cryptenroll --wipe-slot=tpm2 --tpm2-device=auto --tpm2-pcrs=7 /dev/sda3` to re-bind. +> **Re-seal runbook:** a BIOS update / Secure Boot change / **UEFI dbx (revocation list) update** +> alters PCR 7 → the TPM refuses → boot falls back to the passphrase prompt (not a brick). After +> such a change, re-run step 4's +> `systemd-cryptenroll --wipe-slot=tpm2 --tpm2-device=auto --tpm2-pcrs=7 /dev/sda3` to re-bind, then +> reboot to confirm unattended unlock returned. + +### 4a. Firmware / UEFI dbx updates break PCR 7 — and are an OPERATOR threat (VERIFIED 2026-06-30) + +The PCR-7 re-seal hazard above is **not** a rare event — the most common trigger is a **UEFI `dbx` +(Secure Boot revocation database) update**, and it bit the real `park-buzi` booth on 2026-06-28: + +- **What `dbx` is:** the Secure Boot blocklist of known-vulnerable bootloader/shim hashes + (vendor = Microsoft). It is delivered by **`fwupd`/LVFS — a channel SEPARATE from APT** (the GNOME + "Firmware Updater", which on Ubuntu is the **`firmware-updater` snap**, surfaces it). `apt list + --upgradable` being clean does NOT mean a firmware/dbx update isn't pending. +- **The GRUB panic (root cause):** applying a *new* dbx against a *stale* GRUB/shim revokes the + installed bootloader → Secure Boot refuses to load it → **unbootable / GRUB "panic"**. The fix is + ordering: `apt full-upgrade` (current `grub-efi`/`shim-signed`) FIRST, *then* dbx. A fresh reinstall + ships a current GRUB, so reinstalling recovers it. +- **It moves PCR 7:** even with a current GRUB, applying dbx changes the Secure-Boot-policy + measurement → the TPM (slot 1) refuses to release the key → next boot **drops to the slot-0 + passphrase prompt**. Recover with the re-seal runbook above. VERIFIED: on `park-buzi` the dbx + update went through, the box rebooted to a passphrase prompt, the slot-0 passphrase unlocked it, + and `systemd-cryptenroll --wipe-slot=tpm2 … --tpm2-pcrs=7` restored silent auto-unlock. + +**Threat-model consequence ([[threat-model]]: the operator is the adversary).** A firmware/dbx update +on a TPM-sealed booth → the booth won't boot unattended and needs the slot-0 passphrase. So the +operator must be unable to *trigger* a firmware update, and must never hold the passphrase. Lock it +down (DONE on `park-buzi` 2026-06-30): + +```bash +# 1. Kill the firmware-update DAEMON (the GUI "Update" button then fails with no daemon): +sudo systemctl mask fwupd.service fwupd-refresh.timer +systemctl is-enabled fwupd.service fwupd-refresh.timer # → masked / masked (persists across reboot) + +# 2. Remove the operator-facing GUI so the screen is never even presented (Ubuntu = a snap): +sudo snap remove firmware-updater +snap list | grep -i firmware # → no output (re-check: seeded snaps can re-install) +``` + +Plus: the **BIOS admin password** (§1) must gate *entering setup / changing settings* (a +supervisor/admin password, not just a boot password) so the operator can't disable Secure Boot or +change boot order — either of which also breaks the seal. And the **slot-0 passphrase stays +off-machine / escrowed** (same custody as `EVENT_SIGNING_KEY` / `BACKUP_KEY`); it is an admin-only +recovery secret, used on-site during a maintenance window, never known to operators. + +> **Net:** firmware/dbx updates become an **admin-only, on-site, deliberate** action. The booth is +> unattended-bootable only while the firmware/Secure-Boot state is frozen — that is the security +> property, not a bug. Legitimate firmware maintenance now costs: physical presence + the slot-0 +> passphrase + a PCR-7 re-enroll. + +> **⚠ Gotcha — `cryptsetup … --test-passphrase` SILENTLY passes via the TPM.** Before any +> firmware/dbx change, you must *prove a typed passphrase still unlocks the disk* (the TPM-independent +> safety net). But `sudo cryptsetup open --test-passphrase /dev/sda3` with a TPM2 token enrolled will +> succeed **without prompting** — the TPM auto-answers (it unlocks the tpm2 *slot*, e.g. slot 1), a +> FALSE positive that proves nothing about a human-typeable key. Force a real test with +> `--disable-external-tokens` (→ `No usable token is available.` then it prompts; success on slot 0 = +> the passphrase genuinely works): +> ```bash +> sudo cryptsetup open --test-passphrase /dev/sda3 --disable-external-tokens --verbose +> ``` ## 5. GRUB password — EDIT-ONLY (VERIFIED 2026-06-23) @@ -317,6 +375,16 @@ works; the desktop app is a separate workstream. 6. GRUB password MUST be **edit-only** (`--unrestricted` on entries) or it prompts on EVERY boot → breaks unattended reboot. Verify `grep -c unrestricted /boot/grub/grub.cfg` ≥1 before rebooting. +### Firmware / dbx gotchas (2026-06-30, §4a) + +12. **UEFI dbx ships via `fwupd`/LVFS, NOT APT.** `apt list --upgradable` clean ≠ no firmware update + pending. A new dbx vs a stale GRUB → revoked bootloader → **unbootable / GRUB panic** (`apt + full-upgrade` first, then dbx). And dbx **moves PCR 7** → breaks TPM auto-unlock → passphrase + prompt → re-seal (§4 runbook). Mask `fwupd` + remove the `firmware-updater` snap so the operator + can't trigger it. +13. `cryptsetup … --test-passphrase` **silently passes via the TPM token** (false safety signal). Use + `--disable-external-tokens` to actually force a typed-passphrase test before any firmware change. + ### Komodo deploy gotchas (2026-06-27) 7. Periphery `core_address` is **Core's reverse-proxy URL** (`https://komodo.infra.msai.al`), NOT diff --git a/wiki/decisions/disk-os-hardening.md b/wiki/decisions/disk-os-hardening.md new file mode 100644 index 0000000..6307784 --- /dev/null +++ b/wiki/decisions/disk-os-hardening.md @@ -0,0 +1,92 @@ +--- +type: decision +tags: [parking, hardening, threat-model, luks, tpm, secure-boot, grub, firmware, offline-first] +sources: [parking-system-architecture] +updated: 2026-06-30 +status: settled +--- + +# Disk & OS hardening (booth appliance) + +The host-level defences that raise the cost of **offline, physical tamper** of a booth PC: full-disk +encryption, TPM-sealed auto-unlock, Secure Boot, a GRUB edit-lock, an unprivileged operator account, +and locking firmware updates away from the operator. This page is the **rationale (the *why*)**; the +step-by-step verified commands live in the [[appliance-provisioning]] runbook (§3–5c, §4a). Settled +across the first real provisioning (2026-06-23) and the firmware-update episode (2026-06-30). + +> ⚠ **This is the secondary control, not the main event.** The load-bearing anti-fraud mechanism is +> [[reconciliation]] over the [[append-only-event-chain|signed event chain]]. Disk/OS hardening +> defends the [[threat-model|outsider-with-the-box]] and raises the cost of offline tamper — it does +> **not** replace reconciliation, and it cannot stop a *legitimate, logged-in* operator from +> committing fraud through the app (that's what the signed ledger + reconciliation are for). + +## What it defends against + +The appliance sits on-site, physically reachable by the [[threat-model|booth operator (the primary +adversary)]] and by an outsider who can open the case. Without host hardening, either can: + +- **Pull the SSD** and read/alter the SQLite ledger offline → FDE (LUKS) defeats this. +- **Boot a live USB** to mount and edit the disk → Secure Boot + TPM-sealing (PCR 7) defeats booting + a tampered/unsigned kernel; FDE keeps the data unreadable. +- **Edit the GRUB cmdline** (`init=/bin/bash`) for a no-login root shell on the *decrypted* disk → + the GRUB edit-lock defeats this (the TPM seal does NOT — see below). +- **Escalate from the operator login** (sudo, `docker`/`lxd` groups) → the unprivileged-operator + model defeats this. + +## The five controls and why each is shaped the way it is + +| Control | Choice | Why this shape (the load-bearing nuance) | +| --- | --- | --- | +| **FDE** | LUKS, **passphrase** at install (not the installer's "hardware-backed" option) | The 26.04 installer's automated FDE profiler fails on this firmware (`PCR_UNUSABLE`/`dbt`). Passphrase LUKS + a *manual* TPM seal sidesteps it and lets us pick PCRs. The passphrase slot is the **permanent recovery key**. | +| **TPM auto-unlock** | `systemd-cryptenroll`, **PCR 7 only** | Unattended reboot is a hard requirement (no operator types a passphrase). PCR 7 = Secure-Boot policy: catches the attack that matters (disabling Secure Boot) **without** churning on kernel/GRUB updates (PCRs 4/8/9 → would drop to passphrase every boot). **Keep BOTH slots** — slot 0 password (recovery), slot 1 tpm2 (auto-unlock); the TPM is never the only key. | +| **Secure Boot** | Enabled, **Deployed Mode**, stock MS keys | Ubuntu's signed shim needs stock `db`. Reaching the installer with Secure Boot ON is itself proof the MS third-party UEFI CA is trusted. | +| **GRUB edit-lock** | password, **edit-only** (`--unrestricted`) | Closes the `init=/bin/bash` root-shell hole. **The PCR-7 TPM seal does NOT cover this** — editing the cmdline doesn't change PCR 7, so the TPM still releases the key and the attacker lands on the decrypted disk. Edit-only so the box still boots **unattended** (password required only to *edit* entries). | +| **Operator account** | unprivileged, auto-login; separate **admin**+sudo | The operator is the adversary; their OS identity must not be able to escalate. Strip `sudo`, and the latent-escalation groups `lxd`/`docker` (both root-equivalent) + `lpadmin`. Admin is a distinct, no-auto-login identity. | + +See [[tpm]] for the TPM-2.0 analysis (why PCR-only sealing, bus-sniff limits, TPM-vs-[[atecc608|ATECC608]]). + +## Firmware / UEFI dbx updates — a hardening surface AND an operator threat + +Settled 2026-06-30 after a real incident on `park-buzi`. This is the non-obvious one, because it +turns a routine "security update" into a booth-availability risk: + +- **UEFI `dbx`** (the Secure Boot revocation database) and BIOS firmware are delivered by + **`fwupd`/LVFS — a channel SEPARATE from APT** (Ubuntu's GNOME "Firmware Updater" = the + `firmware-updater` snap). A clean `apt list --upgradable` does NOT mean no firmware update is pending. +- **It can brick boot:** a new dbx against a *stale* GRUB/shim **revokes the installed bootloader** → + Secure Boot refuses it → unbootable / GRUB panic. Correct order: `apt full-upgrade` (current + `grub-efi`/`shim-signed`) **first**, then dbx. +- **It breaks auto-unlock:** even with a current GRUB, applying dbx **moves PCR 7** → the TPM refuses + the LUKS key → next boot falls back to the slot-0 passphrase prompt (not a brick). Recover with the + PCR-7 re-seal runbook ([[appliance-provisioning]] §4/§4a). +- **Threat-model consequence:** a firmware/dbx update makes the booth need a passphrase to boot + unattended — so the **operator must be unable to trigger one, and must never hold the passphrase.** + Lock it down: `systemctl mask fwupd.service fwupd-refresh.timer`, `snap remove firmware-updater`, + a **BIOS admin password** that gates *entering setup* (so the operator can't disable Secure Boot / + change boot order), and the **slot-0 passphrase escrowed off-machine** (same custody as + `EVENT_SIGNING_KEY` / `BACKUP_KEY`). Firmware maintenance becomes **admin-only, on-site, deliberate**. + +> The booth is unattended-bootable **only while the firmware / Secure-Boot state is frozen** — that is +> the security property, not a bug. The cost is that legitimate firmware maintenance now needs physical +> presence + the slot-0 passphrase + a PCR-7 re-enroll. + +> **⚠ Verification trap:** `cryptsetup … --test-passphrase` **silently passes via the TPM token** (a +> false safety signal). Before any firmware change, prove a *typed* passphrase still unlocks the disk +> with `--disable-external-tokens` — see [[appliance-provisioning]] §4a. + +## Where the commands live + +This page is the rationale. The **verified, run-on-real-hardware commands** are in +[[appliance-provisioning]]: §1 BIOS, §2 Secure-Boot live-USB check, §3 encrypted install (the `dbt` +workaround), §4 TPM seal (PCR 7) + re-seal runbook, **§4a firmware/dbx lockdown**, §5 GRUB edit-lock, +§5c admin-vs-operator accounts. Komodo Periphery is folded into the same hardened surface as a +root-capable remote agent — see [[fleet-deployment-komodo]] (bind to the NetBird interface only). + +## Relates + +- [[appliance-provisioning]] — the runbook (commands); this page is its *why*. +- [[tpm]] — TPM 2.0 analysis (sealing, PCR choice, limits, vs ATECC608). +- [[threat-model]] — the operator-adversary framing this hardening serves. +- [[reconciliation]] / [[append-only-event-chain]] — the **primary** anti-fraud control this + complements, never replaces. +- [[fleet-deployment-komodo]] — Periphery as part of the trusted computing base. diff --git a/wiki/index.md b/wiki/index.md index 83664ad..1a06386 100644 --- a/wiki/index.md +++ b/wiki/index.md @@ -55,7 +55,7 @@ Counts: 4 sources · 19 entities · 46 concepts · 7 decision records. ## Concepts — integrity & anti-fraud - [[append-only-event-chain]] — append-only + hash chain + ATECC608 signing = unforgeable log. - [[reconciliation]] — the real anti-fraud control; what remote sync actually is. -- [[disk-os-hardening]] — LUKS/GRUB/Secure Boot; worthwhile but not the main event. +- [[disk-os-hardening]] — the *why* of host hardening: LUKS FDE + TPM-sealed auto-unlock (PCR 7) + Secure Boot + GRUB edit-lock + unprivileged operator + firmware/dbx lockdown; secondary control (reconciliation is the main event). Commands → [[appliance-provisioning]]. - [[backup-recovery]] — admin-driven encrypted full-DB backup (local/SMB/SFTP) + DR; signing key escrowed & decoupled from TPM so the ledger survives total hardware loss; restore is admin-only. ## Concepts — device architecture & safety diff --git a/wiki/log.md b/wiki/log.md index c97a8a1..6faa7ed 100644 --- a/wiki/log.md +++ b/wiki/log.md @@ -1993,3 +1993,38 @@ the in-container path as the UI target. Acknowledged limitation: backups are NOT plug-a-USB-and-go); adding a destination is an admin host+compose change. Partly a feature vs the operator-adversary threat model (operator can't redirect backups to a removable stick). USB-automount-to- container flow deferred/not built. + +## [2026-06-30] note | UEFI dbx / firmware update vs TPM-sealed LUKS — GRUB panic + PCR-7 re-seal + operator lockdown (park-buzi) + +Real-world on park-buzi. The GNOME "Firmware Updater" (Ubuntu = the `firmware-updater` snap) surfaced a +pending UEFI dbx (Secure Boot revocation DB) update, vendor Microsoft, delivered by fwupd/LVFS — a channel +SEPARATE from APT (apt list --upgradable was clean except 2 cups packages). 2026-06-28 a dbx update against a +stale GRUB revoked the bootloader → GRUB panic / unbootable → user reinstalled Ubuntu 26.04 LTS (resolute) to +recover (fresh install ships a current GRUB). Correct order is `apt full-upgrade` (current grub-efi/shim-signed) +FIRST, then dbx. + +Even with a current GRUB, applying dbx moves PCR 7 (Secure-Boot-policy measurement) → the TPM (slot 1) refuses +to release the LUKS key → next boot drops to the slot-0 passphrase prompt. VERIFIED end-to-end: proved the +slot-0 typed passphrase first, applied dbx, rebooted to a passphrase prompt, unlocked with slot 0, re-enrolled +`systemd-cryptenroll --wipe-slot=tpm2 --tpm2-device=auto --tpm2-pcrs=7 /dev/sda3` → silent auto-unlock restored. + +Gotcha: `cryptsetup open --test-passphrase /dev/sda3` SILENTLY passes via the TPM token (auto-unlocks the tpm2 +slot without prompting) — a false safety signal. Force a real typed-passphrase test with +`--disable-external-tokens` (→ "No usable token is available." then prompts; success on slot 0 proves it). + +Threat-model lockdown (operator is the adversary): a firmware/dbx update makes the booth need the passphrase to +boot unattended, so operators must not be able to trigger one and must never hold the passphrase. Applied on +park-buzi: `systemctl mask fwupd.service fwupd-refresh.timer` (→ masked/masked, persists), `snap remove +firmware-updater` (remove the GUI; re-check, seeded snaps can re-install), BIOS admin password gates setup +entry, slot-0 passphrase stays escrowed off-machine. Firmware updates are now admin-only/on-site/deliberate. +Recorded in appliance-provisioning.md §4 re-seal runbook + new §4a + gotchas #12/#13. + +## [2026-06-30] note | Created disk-os-hardening.md (resolved a long-standing orphan) + +`[[disk-os-hardening]]` was referenced from ~18 pages (overview, threat-model, tpm, fleet-deployment, +appliance-provisioning, backup-recovery, index, …) but never written — a dangling wikilink. Wrote it as +the *rationale* page (the why): the five host controls (LUKS FDE, TPM-sealed PCR-7 auto-unlock, Secure +Boot Deployed, GRUB edit-lock, unprivileged-operator) + the firmware/dbx lockdown (§4a cross-ref), each +with its load-bearing nuance, plus the standing caveat that this is the SECONDARY control — +reconciliation over the signed chain is the main anti-fraud event. Commands stay in appliance-provisioning +(the how); this page points there. Updated the index.md line accordingly.