New wiki/decisions/appliance-provisioning.md: the hardware-verified step-by-step for provisioning a booth PC (Dell OptiPlex 7070, i5-8500, discrete Nuvoton TPM 2.0) from factory Windows to a hardened Ubuntu 26.04 LTS appliance. Every command was run on the first real unit (2026-06-23). Captures the firmware-specific gotchas: Ventoy → 0x1A under Secure Boot (flash ISO directly); the 7070 BIOS can't view db (verify via live USB); the installer's hardware-backed encryption fails with PCR_UNUSABLE/dbt (use passphrase LUKS + manual systemd-cryptenroll PCR-7 seal); GRUB password must be edit-only (--unrestricted) to keep unattended boot. OS hardening on unit 1 is COMPLETE + verified: LUKS FDE + TPM auto-unlock (PCR 7, unattended) + Secure Boot (Deployed) + GRUB edit-lock (closes the init=/bin/bash root-shell hole that PCR-7 sealing does not cover). Resolves the implementation half of open-questions #12 for unit 1. Cross-linked from disk-os-hardening; index + log updated. Still TODO on the box: Docker + run the stack. Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V
9.2 KiB
type, tags, sources, updated, status
| type | tags | sources | updated | status | ||||||
|---|---|---|---|---|---|---|---|---|---|---|
| reference |
|
2026-06-23 | settled |
Appliance provisioning runbook (booth PC)
Step-by-step to take a booth PC from factory Windows to a hardened, encrypted, container-running parking appliance. Written from the first real provisioning, 2026-06-23, on the actual hardware below — every command here was run and verified on that machine, including the firmware-specific workaround. Companion to disk-os-hardening (the why), tpm (TPM analysis), and container-deployment (the images this runs).
⚠ This box is the threat-model defence. The load-bearing anti-fraud control is still reconciliation over the append-only-event-chain — disk encryption + Secure Boot raise the cost of offline tamper, they don't replace reconciliation.
Reference hardware (first unit, 2026-06-23)
- Dell OptiPlex 7070, Intel Core i5-8500 (Coffee Lake), 238 GB SATA SSD (
/dev/sda). - TPM 2.0 — discrete Nuvoton (
Get-Tpm→ManufacturerIdTxt NTC, fw 7.2.1.0). NOT Intel PTT/fTPM. Discrete ⇒ an external LPC/SPI bus exists (bus-sniff is a theoretical physical attack on PCR-only sealing — accepted; see tpm). Used PC — previous owner irrelevant. - Shipped Windows 11; formatted to Ubuntu 26.04 LTS (the decided platform — desktop-shell-tauri).
1. BIOS (F2 at the Dell logo)
- TPM: leave On. Used PC → Clear the TPM once (Security → TPM → Clear) so the prior owner's keys are wiped before LUKS enrollment. (PPI "Bypass for Clear" was unchecked → it asks for physical confirmation at next boot; that's normal.)
- Secure Boot: Enabled, Deployed Mode (not Audit). "Enable Custom Mode" UNCHECKED = Standard Mode with stock Microsoft keys — this is what Ubuntu's signed shim needs. Do NOT touch PK/KEK/db/dbx. NB: the 7070's Expert Key Management is edit-only (Save/Replace/Append/Delete — no read-only "View Key"), so you cannot inspect db from BIOS; verify via the live USB instead (step 2).
- Boot: UEFI only (no CSM/Legacy — a Legacy install has no Secure Boot / TPM-seal path).
- Set a BIOS admin password.
2. Boot the Ubuntu 26.04 USB (Secure Boot ON)
- Flash the ISO DIRECTLY (Rufus GPT/UEFI, Etcher, or
dd). NOT Ventoy — Ventoy's own bootloader isn't indb, so Secure Boot rejects it withVerification failed: (0x1A) Security Violation(this is Secure Boot working correctly, not a fault). A directly-flashed Ubuntu USB boots the Microsoft-signed shim, which stockdbtrusts. - F12 at the Dell logo → pick the USB under UEFI BOOT.
- Reaching the installer with Secure Boot ON = positive proof the MS third-party UEFI CA is in
db(the verification the BIOS couldn't show us).
3. Encrypted install — the firmware workaround (IMPORTANT)
The 26.04 installer disk page offers: No Encryption / Encrypt with a passphrase / Use hardware-backed encryption (+ advanced LVM/ZFS, both ZFS experimental).
- "Use hardware-backed encryption" FAILS on this 7070 with:
PCR_UNUSABLE … error with secure boot policy (PCR7) measurements: generating secure boot profiles for systems with timestamp revocation (dbt) support is currently not supported.→ Ubuntu's automated FDE profiler can't model PCR7 on Dell firmware carrying adbt(UEFI timestamp revocation list). It is NOT a TPM or Secure-Boot fault — both are fine. - So: choose "Encrypt with a passphrase". Set a strong passphrase and SAVE IT OFF-MACHINE (phone / password manager). It is both the boot unlock (until TPM sealing) AND the permanent recovery slot. Finish the install.
- Result (verify with
lsblk):sda1vfat/boot/efi,sda2ext4/boot,sda3crypto_LUKS→dm_crypt-0(LVM2) →ubuntu--vg-ubuntu--lvext4/.
4. Seal LUKS to the TPM (manual — PCR 7 only)
Do this AFTER first boot. Manual enrollment sidesteps the installer's dbt profiler and lets us pick PCRs. Bind to PCR 7 only (Secure Boot state): it catches the attack that matters (disabling Secure Boot to boot a tampered kernel) WITHOUT breaking on routine kernel/GRUB updates (which churn PCRs 4/8/9 → would otherwise drop every boot to the passphrase). Firmware-only PCR 0 is the fallback if PCR 7 ever errors.
sudo apt update && sudo apt install -y tpm2-tools
sudo tpm2_pcrread sha256 # sanity: PCRs 0-10 populated, PCR 7 has a real value
# Enroll the TPM (prompts for the EXISTING install passphrase to authorize the new slot):
sudo systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=7 /dev/sda3
# Verify TWO slots — keep BOTH (slot 0 password = recovery, slot 1 tpm2 = auto-unlock):
sudo systemd-cryptenroll /dev/sda3
# SLOT TYPE
# 0 password
# 1 tpm2
Wire it into boot (back up first; the mapping is dm_crypt-0, the LUKS UUID is in /etc/crypttab):
sudo cp /etc/crypttab /etc/crypttab.bak
sudo sed -i 's/none luks$/none luks,tpm2-device=auto/' /etc/crypttab
cat /etc/crypttab # → dm_crypt-0 UUID=… none luks,tpm2-device=auto
sudo update-initramfs -u
sudo reboot
- Boots straight to login, no passphrase prompt = ✅ TPM auto-unlock works (unattended reboot achieved — VERIFIED on this unit 2026-06-23).
- Still prompts = PCR mismatch; type the passphrase (NOT locked out), then retry with
--tpm2-pcrs=0. Thepasswordslot +crypttab.bakmake 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/sda3to re-bind.
5. GRUB password — EDIT-ONLY (VERIFIED 2026-06-23)
Closes the init=/bin/bash / systemd.unit=rescue.target local-root hole: without it, anyone at
the keyboard presses e at the GRUB menu, edits the kernel cmdline, and boots to a root shell with
no login. The PCR-7 TPM seal does NOT cover this — editing the GRUB cmdline doesn't change
PCR 7 (Secure Boot policy), so the TPM still releases the key and the attacker lands on the decrypted
disk. This is the specific countermeasure for the threat-model. Use
edit-only mode (--unrestricted) so the box still boots UNATTENDED — the password is required
only to EDIT entries, never to boot.
grub-mkpasswd-pbkdf2 # enter a password (twice) → copy the grub.pbkdf2.sha512.* hash
Add the superuser (paste YOUR hash) to the end of /etc/grub.d/40_custom:
set superusers="admin"
password_pbkdf2 admin grub.pbkdf2.sha512.10000.<YOUR_HASH>
Make menu entries bootable WITHOUT the password (edit-only) — in /etc/grub.d/10_linux, set the
active CLASS= line to include --unrestricted:
CLASS="--class gnu-linux --class gnu --class os --unrestricted"
Regenerate + VERIFY BOTH HALVES landed in the real config BEFORE rebooting (a GRUB misconfig means a rescue-USB recovery):
sudo update-grub
sudo grep -c "password_pbkdf2" /boot/grub/grub.cfg # want ≥1 (password present)
sudo grep -c "unrestricted" /boot/grub/grub.cfg # want ≥1 (entries bootable w/o password)
sudo reboot
✅ VERIFIED on this unit: boots straight to login (no GRUB prompt, TPM still auto-unlocks) AND
pressing e at the menu prompts for admin + password. Store the GRUB password off-machine
(alongside the LUKS passphrase).
OS hardening on the first unit is now COMPLETE: LUKS FDE + TPM auto-unlock (PCR 7) + Secure Boot (Deployed) + GRUB edit-lock.
5b. Further hardening (TODO — not yet done)
- Key-based SSH only (disable password auth) if SSH is enabled at all.
- No/locked-down desktop — single-purpose; autostart the kiosk (desktop-shell-tauri).
- Consider moving the host event-signing key into the TPM (non-extractable) — tpm, open-questions #12.
sudo apt autoremovethe leftover old kernel (linux-*-7.0.0-14) once the new one is proven.
6. Runtime — Docker stack
Per container-deployment: install Docker Engine + compose, then run the parking-server +
parking-vision images via docker-compose.yml -f docker-compose.prod.yml. Provide a real
JWT_SECRET (openssl rand -hex 32) and COOKIE_SECURE=0 (plain-http booth LAN — see
disk-os-hardening deploy-time runbook). Images are published to the Gitea registry by
build-images.yml on push to dev/main.
Quick-reference: the gotchas, in order they bit us
- Ventoy USB →
0x1ASecurity Violation under Secure Boot → flash the ISO directly instead. - 7070 BIOS has no "View Key" → can't inspect db; the live-USB boot IS the verification.
- Installer "hardware-backed encryption" →
PCR_UNUSABLE/dbt → use passphrase LUKS + manual seal. - Bind TPM to PCR 7 only, not a multi-PCR set (kernel updates churn 4/8/9 → passphrase every boot).
- Always keep the password slot + an off-machine copy of the passphrase (TPM is never the only key).
- GRUB password MUST be edit-only (
--unrestrictedon entries) or it prompts on EVERY boot → breaks unattended reboot. Verifygrep -c unrestricted /boot/grub/grub.cfg≥1 before rebooting.