scripts v1.18.0 — R-21 slice B: firmware loader option --loader shim|mkimage (F1)

Closes N100 F1 (HIGH): cheap AMI (AN3PLUS 0.01-class) UEFI firmware can't
relocate the ISO's stock signed GRUB from USB (relocation 0x0). The run's live
grub-mkimage workaround is now a first-class pipeline mode.

- build-felhom-iso.sh: --loader shim|mkimage (default shim, byte-for-byte
  unchanged; profile-settable FELHOM_LOADER; --loader wins). Loud banner +
  manifest loader:/grub-mkimage: fields + -mkimage filename suffix.
- mkimage-surgery.sh (new): post-prepare-iso, in the assistant container. Builds
  a monolithic grub-mkimage loader from the ISO's own GRUB (module set from its
  grub.cfg; embedded search --fs-uuid -> configfile the real menu). Swaps it into
  the ISO9660 tree (real lowercase path) + the efi.img ESP; xorriso re-master
  preserves BIOS-hybrid + UEFI + GPT-ESP, drops only Apple HFS+/APM. Recipe from
  the N100 run evidence, not re-derived.
- Dockerfile.assistant: grub-common + grub-efi-amd64-bin + mtools + dosfstools.
  profiles/n100.profile (new, mkimage + SB-off note).
- Validated on nested VM 311 (RUNBOOK-B legs): leg1 shim boots+installs under
  OVMF SB-enforcing + SeaBIOS; leg2 mkimage boots+installs under SB-off; leg3
  (red-proof) mkimage under SB-enforcing FAILS Access Denied (unsigned -> SB must
  be OFF); leg4 surgery byte-identical payload. bash -n + shellcheck clean.
  Physical N100 closure folds into the rehearsal (n100-safety match-nothing ISO
  built + sha-recorded, unbooted). PXE stays a deferred R-21 note.
This commit is contained in:
2026-07-17 14:28:10 +02:00
parent d86d2699c3
commit 3172df1927
11 changed files with 439 additions and 58 deletions
+42 -6
View File
@@ -1,4 +1,4 @@
# Felhom bare-metal ISO pipeline (R-21 slice A)
# Felhom bare-metal ISO pipeline (R-21 slice A + B)
Turns the official Proxmox VE ISO into a **Felhom auto-install ISO** whose first-boot chain,
unattended, fetches and runs `felhom-host-install.sh` until the host is enrolled and a guest is
@@ -18,7 +18,8 @@ channel).
| File | Role |
|---|---|
| `Dockerfile.assistant` | build host: Debian trixie + `proxmox-auto-install-assistant` (paired to the ISO's PVE generation by Debian codename) + `xorriso` + `shellcheck` |
| `build-felhom-iso.sh` | the pipeline: render answer, mint throwaway root hash, **validate-answer output-parse gate**, render stub, `prepare-iso`, emit ISO + sha256 + manifest |
| `build-felhom-iso.sh` | the pipeline: render answer, mint throwaway root hash, **validate-answer output-parse gate**, render stub, `prepare-iso`, **optional `--loader mkimage` surgery**, emit ISO + sha256 + manifest |
| `mkimage-surgery.sh` | slice-B loader surgery (runs in the container, post-`prepare-iso`): builds a monolithic `grub-mkimage` UEFI loader and swaps it into the ISO's EFI boot path (F1 firmware fix) |
| `answer.toml.tmpl` | Proxmox answer template (`__FQDN__` / `__ROOT_HASH__` / `__ROOT_SSH_KEYS__` / `__DISK_SETUP__` placeholders; `[first-boot]` from-iso/fully-up) |
| `profiles/*.profile` | disk-selection + fqdn fragments (sourceable, no secret) |
| `stub-first-boot.sh` | the ONE first-boot executable (skeleton; build injects the bootstrap script/unit/env as base64) |
@@ -66,6 +67,33 @@ glob. **A filter matching nothing, or a non-existent disk, fails-safe**: the ins
1, no disk touched) rather than installing on the wrong disk (spike S5c). Never commit a real key in
`FELHOM_ROOT_SSH_KEY` unless it is meant to ship in every ISO built from that profile.
A profile may also set `FELHOM_LOADER="shim"|"mkimage"` (see below); `--loader` on the command line
overrides it.
## Loader modes (`--loader shim|mkimage`, slice B)
The UEFI boot loader is selectable. Default **shim**; a profile can pin `FELHOM_LOADER`; `--loader`
wins over both.
- **`shim`** (default) — the stock output: the ISO's MS-signed shim → GRUB chain. **Secure Boot keeps
working** on compliant firmware (no MOK, no keypress — spike S2b). Use this everywhere it boots.
- **`mkimage`** — replaces the ISO's UEFI boot path with a **monolithic `grub-mkimage` loader** built
from the ISO's own GRUB modules. This is the fix for cheap AMI (`AN3PLUS 0.01`-class) mini-PC
firmware that **cannot UEFI-boot the ISO's stock GRUB from USB** (`relocation 0x0 is not implemented
yet`; SB-off and shim-bypass don't help) — the F1 finding of `tests/VALIDATION-n100-baremetal-2026-07-16.md`,
worked around live during that run and now first-class. The recipe (module set from the ISO's
`grub.cfg`, embedded `search --fs-uuid``configfile` the real menu) is reproduced from the run
evidence, not re-derived.
> **⚠ mkimage is UNSIGNED → the target board MUST have Secure Boot OFF.** With Secure Boot
> enforcing the firmware refuses the loader (`Access Denied`, no boot — proven, RUNBOOK-B leg 3).
> mkimage ISOs are named with a **`-mkimage`** suffix and the manifest's `loader:` line says so;
> never confuse them with a shim (SB-capable) build. shim mode's output is byte-for-byte the same as
> before this flag existed (the surgery is skipped entirely).
The surgery runs **after** `prepare-iso` and touches only the EFI boot path — the answer file and
the on-first-boot payload are provably byte-identical to the shim variant's (RUNBOOK-B leg 4).
## bootstrap-env (SECRET-BEARING)
The `--bootstrap-env` file becomes the in-ISO `/etc/felhom/bootstrap.env`:
@@ -104,17 +132,25 @@ producer steps re-run each pass).
## N100 profile (for RUNBOOK B)
The nested profile uses `disk-list=["sda"]`. **A real single-disk mini-PC should use a unique-serial
udev filter, not `disk-list`:**
Use the committed `profiles/n100.profile` template (cheap-board, `FELHOM_LOADER="mkimage"`). **A real
single-disk mini-PC uses a unique-serial udev filter, not `disk-list`:**
1. Boot once (or use a live env), harvest the target disk's udev id:
`udevadm info --query=property --name=/dev/sda | grep -E 'ID_SERIAL_SHORT|ID_MODEL|ID_WWN'`.
2. Write a profile: `filter.ID_SERIAL_SHORT = "<harvested>"` + `filter-match = "all"`. A
2. In the profile, set `filter.ID_SERIAL_SHORT = "<harvested>"` + `filter-match = "all"`. A
match-nothing filter fails-safe (won't wipe the wrong disk).
3. **Prior-LVM prerequisite:** the auto-installer does **not** force-clear a pre-existing LVM PV
signature — a disk with a previous Linux/Proxmox install aborts at "cannot init physical volume"
(spike S2b). Wipe first: `blkdiscard -f /dev/<disk>` (or `wipefs -a` + zero the PV region).
4. UEFI + Secure Boot: no caveat — installs and boots under SB enforcing, no MOK/keypress (spike S2b).
4. **Boot loader + Secure Boot (F1):** cheap AMI (`AN3PLUS`-class) firmware can't boot the stock GRUB
from USB, so `profiles/n100.profile` uses `--loader mkimage`. **That loader is unsigned → set
Secure Boot to OFF/Disabled in the BIOS** before booting the stick (with SB enforcing the board
refuses it: `Access Denied`, no boot). Firmware that boots the stock ISO fine needs no mkimage and
keeps Secure Boot working (default `shim`, spike S2b). *Optional pre-flight (operator, no risk):* a
`n100-safety` ISO — `--loader mkimage` + a **match-nothing** filter — boots the board, loads GRUB,
and the installer aborts touching nothing (S5c). It confirms the loader + SB-off setting before the
real install; its sha is recorded at build. Never boot the real install-armed ISO except in the
supervised rehearsal.
5. **BIOS "State After G3" → "Power On" (or "Last State")** before final sign-off — so the mini-PC
auto-powers-on after a mains loss (a managed appliance must come back without a physical press).
Set it in the BIOS power/ACPI menu; it is board-specific and cannot be scripted (operator step).