Refactor dnsmasq to build as a container. Add signed iPXE image build
This commit is contained in:
@@ -17,6 +17,18 @@ ARTIFACT_SRC=../artifacts/0.0.0-2eefd97
|
||||
SPDK_IMAGE=git.bitasylum.org/audiobuzz/spdk-nvmf
|
||||
SPDK_VERSION=v24.09
|
||||
|
||||
# dnsmasq image — prebuilt + pushed by CI (.gitea/workflows/dnsmasq-image.yaml);
|
||||
# GENERIC (no Secure Boot keys / no iPXE binary). Pulled, not built at deploy.
|
||||
DNSMASQ_IMAGE=git.bitasylum.org/audiobuzz/fleet-dnsmasq
|
||||
DNSMASQ_VERSION=latest
|
||||
|
||||
# fleet-ipxe image — the SIGNED iPXE bootloader (snponly.efi) for PXE-only hosts,
|
||||
# built + signed (fleet db key) + pushed by CI (.gitea/workflows/ipxe-image.yaml).
|
||||
# Mounted into dnsmasq at /ipxe. Per-environment (signature is db-key-specific);
|
||||
# pin by digest in production, e.g. fleet-ipxe@sha256:…
|
||||
IPXE_IMAGE=git.bitasylum.org/audiobuzz/fleet-ipxe
|
||||
IPXE_VERSION=latest
|
||||
|
||||
# --- This serving host on the boot LAN -------------------------------------
|
||||
BOOT_IFACE=ens19 # interface facing the booting client / test bridge
|
||||
SERVER_IP=192.168.50.10 # this host's address on BOOT_IFACE (DHCP/DNS/HTTP/NVMe)
|
||||
|
||||
@@ -23,8 +23,18 @@
|
||||
set -eu
|
||||
|
||||
mkdir -p /srv/tftp
|
||||
# User-supplied iPXE binary (snponly.efi) from the read-only source mount.
|
||||
cp -f /tftp-src/*.efi /srv/tftp/ 2>/dev/null || true
|
||||
# iPXE binary (snponly.efi) into the TFTP root. Prefer the signed binary from the
|
||||
# mounted fleet-ipxe image (/ipxe); fall back to the read-only bind mount
|
||||
# (/tftp-src) for offline/lab use. The dnsmasq image carries no .efi itself.
|
||||
if ls /ipxe/*.efi >/dev/null 2>&1; then
|
||||
cp -f /ipxe/*.efi /srv/tftp/
|
||||
echo "iPXE bootloader source: /ipxe (fleet-ipxe image)"
|
||||
elif ls /tftp-src/*.efi >/dev/null 2>&1; then
|
||||
cp -f /tftp-src/*.efi /srv/tftp/
|
||||
echo "iPXE bootloader source: /tftp-src (bind mount, lab fallback)"
|
||||
else
|
||||
echo "WARN: no iPXE binary in /ipxe or /tftp-src — PXE-only clients can't chainload" >&2
|
||||
fi
|
||||
# autoexec.ipxe was an STP-era workaround (ifopen+retry before autoboot); it's no
|
||||
# longer rendered — PortFast on the switch port is the real fix. Remove any stale
|
||||
# copy left in a long-lived container's writable layer.
|
||||
|
||||
+24
-11
@@ -37,7 +37,12 @@
|
||||
services:
|
||||
# --- DHCP + DNS ------------------------------------------------------------
|
||||
dnsmasq:
|
||||
build: ./dnsmasq
|
||||
# Prebuilt by CI (.gitea/workflows/dnsmasq-image.yaml) and pulled here, like
|
||||
# the SPDK image — the serving host no longer builds it at deploy time. The
|
||||
# image is GENERIC (no Secure Boot keys, no iPXE binary): the signed iPXE
|
||||
# bootloader rides the separate, per-environment fleet-ipxe image mounted at
|
||||
# /ipxe below. Local-dev alternative: replace this line with `build: ./dnsmasq`.
|
||||
image: ${DNSMASQ_IMAGE}:${DNSMASQ_VERSION}
|
||||
network_mode: host
|
||||
cap_add:
|
||||
- NET_ADMIN # DHCP raw sockets
|
||||
@@ -48,11 +53,19 @@ services:
|
||||
# + DNS A/PTR. Auto-populated by fleet-namer (creative names per MAC); add
|
||||
# manual lines to pin specific hosts. RW so fleet-namer can append.
|
||||
- ./dnsmasq/dhcp-hosts:/etc/dnsmasq-hosts
|
||||
# iPXE binary source (read-only) for the PXE-only chainload (firmware without
|
||||
# UEFI HTTP boot, e.g. EliteBook 820 G4). Drop ipxe.efi here — see
|
||||
# serving/tftp/README. The entrypoint copies it into /srv/tftp and renders
|
||||
# boot.ipxe alongside it (the actual TFTP root is container-internal).
|
||||
- ./tftp:/tftp-src:ro
|
||||
# Signed iPXE bootloader for the PXE-only chainload (firmware without UEFI
|
||||
# HTTP boot, e.g. EliteBook 820 G4). Mounted from the separate, signed,
|
||||
# digest-pinnable fleet-ipxe image (.gitea/workflows/ipxe-image.yaml); the
|
||||
# entrypoint copies /ipxe/snponly.efi into /srv/tftp. Kept OUT of the dnsmasq
|
||||
# image so the daemon image stays generic/key-agnostic — see serving/tftp/README.
|
||||
- type: image
|
||||
source: ${IPXE_IMAGE}:${IPXE_VERSION}
|
||||
target: /ipxe
|
||||
image:
|
||||
subpath: ipxe
|
||||
# Fallback (Docker < 28 / offline / lab): drop snponly.efi in ./tftp and bind
|
||||
# it instead (the entrypoint prefers /ipxe, then falls back to /tftp-src):
|
||||
# - ./tftp:/tftp-src:ro
|
||||
# The release artifacts (same image volume as the http service), read-only,
|
||||
# so the entrypoint can objcopy the UKI's .cmdline section into boot.ipxe
|
||||
# (Secure Boot OFF needs the real cmdline fed to iPXE — see entrypoint.sh).
|
||||
@@ -137,11 +150,11 @@ services:
|
||||
# snponly.efi = UEFI x64 iPXE that rides the FIRMWARE's NIC driver (UEFI SNP)
|
||||
# instead of iPXE's own — works on virtio VMs AND real hardware where the
|
||||
# native driver won't bind (e.g. EliteBook 820 G4 / Intel I219-LM, which
|
||||
# fails ipxe.efi with "No configurable methods"). Ships in Ubuntu's `ipxe`
|
||||
# package; see serving/tftp/README.md.
|
||||
# NOTE (lab): the iPXE binary is UNSIGNED, so this path needs Secure Boot OFF.
|
||||
# For production, sign it with the fleet db key (then SB stays enforced
|
||||
# end-to-end) — see docs/secure-boot-keys.md.
|
||||
# fails ipxe.efi with "No configurable methods"). Built + signed by CI into
|
||||
# the fleet-ipxe image (mounted at /ipxe); see serving/tftp/README.md.
|
||||
# The fleet-ipxe image is signed with the fleet db key when CI has the
|
||||
# secrets, so this path stays Secure-Boot-enforced end-to-end; an unsigned
|
||||
# (lab) fleet-ipxe build needs Secure Boot OFF — see docs/secure-boot-keys.md.
|
||||
- --enable-tftp
|
||||
- --tftp-root=/srv/tftp
|
||||
- --dhcp-vendorclass=set:pxe,PXEClient
|
||||
|
||||
+20
-6
@@ -1,11 +1,25 @@
|
||||
# TFTP root — iPXE chainload for PXE-only firmware
|
||||
|
||||
This directory is mounted read-only at `/srv/tftp` in the dnsmasq container and
|
||||
serves the iPXE network bootloader over TFTP to firmware that **lacks UEFI HTTP
|
||||
boot** (e.g. HP EliteBook 820 G4). HTTP-boot-capable firmware never touches this
|
||||
path — it gets the UKI straight over HTTP (see the `httpboot` tag in
|
||||
`docker-compose.yml`). The fallback is automatic, driven by what each client
|
||||
advertises (DHCP vendor-class / client-arch).
|
||||
The iPXE network bootloader (`snponly.efi`) is served over TFTP to firmware that
|
||||
**lacks UEFI HTTP boot** (e.g. HP EliteBook 820 G4). HTTP-boot-capable firmware
|
||||
never touches this path — it gets the UKI straight over HTTP (see the `httpboot`
|
||||
tag in `docker-compose.yml`). The fallback is automatic, driven by what each
|
||||
client advertises (DHCP vendor-class / client-arch).
|
||||
|
||||
> **Where `snponly.efi` comes from (production): the `fleet-ipxe` OCI image.**
|
||||
> CI builds it, **signs it with the fleet `db` key**, and pushes a tiny
|
||||
> FROM-scratch image (`.gitea/workflows/ipxe-image.yaml` →
|
||||
> `scripts/build-ipxe-oci.sh`). The dnsmasq container mounts that image at
|
||||
> `/ipxe` and the entrypoint copies `/ipxe/snponly.efi` into `/srv/tftp`. It's a
|
||||
> **separate, per-Secure-Boot-environment image** (its signature is `db`-specific)
|
||||
> so the dnsmasq image itself stays generic — see docs/secure-boot-keys.md and
|
||||
> CLAUDE.md invariant #9.
|
||||
>
|
||||
> **This directory is the offline/lab fallback only:** if you drop an
|
||||
> `snponly.efi` here and enable the commented `./tftp:/tftp-src:ro` bind in
|
||||
> `docker-compose.yml`, the entrypoint uses it when `/ipxe` is empty (it prefers
|
||||
> `/ipxe`, then falls back to `/tftp-src`). Handy on Docker < 28 or when iterating
|
||||
> without a registry round-trip.
|
||||
|
||||
Flow for a PXE-only host:
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user