diff --git a/bastion/Dockerfile.bastion b/bastion/Dockerfile.bastion index 6952325..4092255 100644 --- a/bastion/Dockerfile.bastion +++ b/bastion/Dockerfile.bastion @@ -35,7 +35,7 @@ FROM --platform=$BUILDPLATFORM fedora:43 AS ipxe-builder RUN dnf install -y git gcc make perl-interpreter xz-devel gcc-aarch64-linux-gnu && dnf clean all RUN git clone --depth=1 https://github.com/ipxe/ipxe.git /tmp/ipxe -RUN cd /tmp/ipxe/src && make bin-x86_64-efi/snp.efi && \ +RUN cd /tmp/ipxe/src && make bin-x86_64-efi/snp.efi bin-x86_64-efi/ipxe.efi && \ make CROSS_COMPILE=aarch64-linux-gnu- bin-arm64-efi/snp.efi # ── Stage 2: Production runtime (Fedora -- needs dnsmasq) ─────── @@ -58,6 +58,15 @@ RUN dnf install -y \ # boot from CD-ROM/USB -- it requires PXE chainloading) COPY --from=ipxe-builder /tmp/ipxe/src/bin-x86_64-efi/snp.efi /usr/share/ipxe/ipxe-snp-x86_64.efi COPY --from=ipxe-builder /tmp/ipxe/src/bin-arm64-efi/snp.efi /usr/share/ipxe/arm64-efi/ipxe-snp.efi +# Full iPXE build (native NIC drivers + snp + nii fallback), served as the +# default UEFI x86_64 PXE binary. snponly rides the firmware's NII/UNDI stack, +# and on some NICs that stack cannot RECEIVE UNICAST frames: DHCP (broadcast) +# works, then every ARP reply is lost and HTTP times out. Observed on +# thebeast's onboard NIC 2026-10-19 (tcpdump: labmaster answered every ARP +# request within microseconds, iPXE kept re-asking). A native driver bypasses +# the firmware stack entirely; NICs without one still fall back to snp/nii, +# so machines that worked before are unaffected. +COPY --from=ipxe-builder /tmp/ipxe/src/bin-x86_64-efi/ipxe.efi /usr/share/ipxe/ipxe-full-x86_64.efi # Install pnpm RUN npm install -g pnpm@9 diff --git a/bastion/src/bastion/src/main.ts b/bastion/src/bastion/src/main.ts index e6a7e00..3a922a1 100644 --- a/bastion/src/bastion/src/main.ts +++ b/bastion/src/bastion/src/main.ts @@ -16,16 +16,19 @@ import { BastionConnection } from "./services/labd-connection.js"; import { progressBus } from "./services/progress-events.js"; import { ensureBootIso } from "./routes/boot-iso.js"; -function copyIfMissing(src: string, dest: string, label: string): void { - if (existsSync(dest)) { - logger.info(` ${label} -- cached`); - return; - } +// For binaries that ship WITH the image: the data dir is a persistent volume, +// so a copy-if-missing would keep serving whatever build landed there first, +// forever, across image upgrades. Compare content and replace on change. +function copyIfChanged(src: string, dest: string, label: string): void { if (!existsSync(src)) { throw new Error(`${label}: source not found at ${src}`); } + if (existsSync(dest) && readFileSync(dest).equals(readFileSync(src))) { + logger.info(` ${label} -- up to date`); + return; + } copyFileSync(src, dest); - logger.info(` ${label} -- copied from ${src}`); + logger.info(` ${label} -- installed from ${src}`); } function download(url: string, dest: string, label: string): void { @@ -254,18 +257,21 @@ export async function startBastion(overrides: Partial = {}): Prom if (config.skipArtifacts !== true) { logger.info(`Preparing boot artifacts (Fedora ${config.fedoraVersion} ${config.arch})...`); - copyIfMissing( + copyIfChanged( "/usr/share/ipxe/undionly.kpxe", `${config.tftpDir}/undionly.kpxe`, "iPXE BIOS", ); - copyIfMissing( - "/usr/share/ipxe/ipxe-snponly-x86_64.efi", + // Full build (native drivers + snp/nii fallback), NOT snponly: the + // firmware NII stack snponly rides cannot receive unicast on some NICs + // (thebeast 2026-10-19: DHCP fine, every ARP reply lost, HTTP timeout). + copyIfChanged( + "/usr/share/ipxe/ipxe-full-x86_64.efi", `${config.tftpDir}/ipxe.efi`, "iPXE UEFI x86_64", ); try { - copyIfMissing( + copyIfChanged( "/usr/share/ipxe/arm64-efi/snponly.efi", `${config.tftpDir}/ipxe-arm64.efi`, "iPXE UEFI arm64",