Refactor dnsmasq to build as a container. Add signed iPXE image build
This commit is contained in:
+11
-4
@@ -639,12 +639,19 @@ smoke test.
|
||||
the MS cert even in Setup Mode — looked like a non-empty-db rejection but was
|
||||
the NUL bug all along; it failed identically on a clean `pre-enrolled-keys=0`
|
||||
disk.)
|
||||
- **iPXE signing WIRED (2026-06-21):** `snponly.efi` is now built + signed
|
||||
(`db`) + pushed by CI as a separate FROM-scratch **`fleet-ipxe`** image
|
||||
(`scripts/build-ipxe-oci.sh`, `.gitea/workflows/ipxe-image.yaml`), mounted into
|
||||
dnsmasq at `/ipxe`. dnsmasq itself moved to a generic CI-built/pulled image
|
||||
(`.gitea/workflows/dnsmasq-image.yaml`) — no longer built at deploy time, and
|
||||
carries no keys/binary. (Decision: separate `fleet-ipxe` image, not baked into
|
||||
dnsmasq, so the daemon image stays key-agnostic across SB environments.)
|
||||
- **REMAINING for production:** (a) **secure path** — re-verify signed boot with
|
||||
`disable_dot1x=false` (802.1X/MACsec), not just the test path; (b) **820 / PXE
|
||||
path** — sign `snponly.efi` with the same `db` key so the iPXE front-end also
|
||||
passes Secure Boot (the VM's HTTP path has no iPXE stage, so this is still
|
||||
unverified on real PXE hardware); (c) confirm signing is reproducible (verity
|
||||
hash stable across two signed builds).
|
||||
path** — BENCH-VERIFY the signed `fleet-ipxe` chain on the 820 (the VM's HTTP
|
||||
path has no iPXE stage, so the signed iPXE binary is still unverified on real
|
||||
PXE hardware); (c) confirm signing is reproducible (verity hash stable across
|
||||
two signed builds).
|
||||
- **PXE-only iPXE fallback — BENCH-VERIFIED on the EliteBook 820 G4 (2026-06-20).**
|
||||
Firmware without UEFI HTTP boot chainloads `serving/tftp/snponly.efi` → renders
|
||||
`autoexec.ipxe` (ifopen + DHCP-retry + `imgargs <real cmdline>`) → UKI over HTTP
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user