Refactor dnsmasq to build as a container. Add signed iPXE image build

This commit is contained in:
Audiobuzz
2026-06-21 09:11:23 +00:00
parent 535793228e
commit 3226a44d5a
13 changed files with 410 additions and 35 deletions
+22 -9
View File
@@ -202,10 +202,19 @@ Caveats:
## PXE-only hosts: iPXE chainload and Secure Boot
Firmware without UEFI HTTP boot (e.g. HP EliteBook 820 G4) falls back to PXE; the
serving stack hands those hosts **iPXE** over TFTP (`serving/tftp/ipxe.efi`),
which then chainloads the UKI over HTTP. dnsmasq selects per client automatically
serving stack hands those hosts **iPXE** over TFTP (`snponly.efi`), which then
chainloads the UKI over HTTP. dnsmasq selects per client automatically
(HTTP-capable → UKI direct; PXE-only → iPXE → UKI), so no per-host config.
**Packaging:** the signed `snponly.efi` is built + signed + pushed by CI as a
small FROM-scratch **`fleet-ipxe` image** (`.gitea/workflows/ipxe-image.yaml` →
`scripts/build-ipxe-oci.sh`), mounted into the dnsmasq container at `/ipxe`
(serving/docker-compose.yml). It is deliberately a **separate, per-Secure-Boot-
environment image** (its signature is `db`-key-specific) so the dnsmasq image
stays generic/key-agnostic — same reasoning as invariant #9 keeping serving
software out of the release-artifact image. For offline/lab use, drop an
`snponly.efi` in `serving/tftp/` and the entrypoint falls back to that bind mount.
Secure Boot across that chain needs **only the `db` key — the one already
enrolled for the UKI. No extra keys, no Microsoft CA, no shim** (the fleet owns
its keys):
@@ -215,9 +224,10 @@ firmware --(verify snponly.efi vs db)--> iPXE --(chain -> firmware LoadImage
verifies UKI vs db)--> UKI
```
- The firmware verifies the TFTP-downloaded NBP (`ipxe.efi`) against `db` →
**the iPXE binary must be signed by `db`** (`sbsign`, same key/step as the UKI).
Stock/unsigned iPXE is rejected under Secure Boot.
- The firmware verifies the TFTP-downloaded NBP (`snponly.efi`) against `db` →
**the iPXE binary must be signed by `db`** (`sbsign`; CI does this in
`build-ipxe-oci.sh` when the `SB_KEY`/`SB_CERT` secrets are set, same key as the
UKI). Stock/unsigned iPXE is rejected under Secure Boot.
- iPXE must load the UKI as an **EFI image** (`imgfetch` + `boot`, or `chain`) so
the firmware's `LoadImage()` re-verifies it against `db` — do **not** use iPXE's
`kernel`/`boot` on a raw vmlinuz, which bypasses SB.
@@ -232,10 +242,13 @@ firmware --(verify snponly.efi vs db)--> iPXE --(chain -> firmware LoadImage
Boot ON is immune** — the stub ignores any loader-supplied cmdline — so this is
purely a lab/SB-off concern.
**Lab posture (now): iPXE signing OFF → run the iPXE path with Secure Boot OFF.**
`serving/tftp/ipxe.efi` is unsigned. **Production upgrade:** sign it with `db`
and Secure Boot stays enforced end-to-end (tracked in TODO #8). The HTTP-boot path
(firmware → UKI) is unaffected and remains SB-clean regardless.
**Posture:** the `fleet-ipxe` image is signed with `db` **whenever CI has the
`SB_KEY`/`SB_CERT` secrets** (same condition as the UKI), so the PXE path can stay
Secure-Boot-enforced end-to-end. An **unsigned** `fleet-ipxe` build (no secrets)
keeps its loud warning and requires Secure Boot OFF on the PXE target. The
HTTP-boot path (firmware → UKI) is unaffected and SB-clean regardless. Still
**BENCH-VERIFY** the signed PXE chain on the 820 (the VM uses HTTP boot, so the
signed iPXE stage is unverified on real hardware — TODO #8).
## Key hygiene & lifecycle