#!/bin/sh # dnsmasq entrypoint: assemble the TFTP root, then exec dnsmasq with the compose # args ("$@"). # # iPXE + Secure Boot OFF cmdline problem: # When iPXE executes the UKI it sets the EFI LoadOptions to the image's command # line, or — if that's empty — to the image NAME. With Secure Boot OFF, # systemd-stub uses LoadOptions AS THE KERNEL CMDLINE, replacing the UKI's # EMBEDDED cmdline (root/roothash/rd.nvmf.discover/rd.dot1x=0/volatile). So # booting the UKI URL directly (or clearing the args) makes the kernel come up # with just "fleet-.efi" as its cmdline -> dracut emergency shell. # Fix: feed iPXE the REAL cmdline via `imgargs`. That cmdline lives only inside # the UKI on the serving host (not in the OCI image, not as a file), so we read # it out of the UKI's .cmdline PE section and bake it into boot.ipxe. # (Secure Boot ON ignores any loader cmdline and uses the embedded one, so this # whole dance is a lab/SB-OFF concern; harmless when SB is on.) # # Extraction: objdump -h gives the .cmdline section's size + file offset; we dd # those bytes out of the (read-only) UKI. We deliberately avoid `objcopy`: # --dump-section rewrites the input in place (fails on the read-only /artifacts # mount), and `-O binary --only-section` lays the section out at its huge VMA # (multi-GB sparse file). set -eu mkdir -p /srv/tftp # User-supplied iPXE binary (ipxe.efi) from the read-only source mount. cp -f /tftp-src/*.efi /srv/tftp/ 2>/dev/null || true extract_cmdline() { # $1 = UKI path; echoes the cleaned cmdline (empty on failure) uki="$1" command -v objdump >/dev/null 2>&1 || return 0 # objdump -h columns: Idx Name Size VMA LMA File-off Algn line=$(objdump -h "$uki" 2>/dev/null | awk '$2==".cmdline"{print $3" "$6; exit}') [ -n "$line" ] || return 0 sz=$((0x${line% *})); off=$((0x${line#* })) [ "$sz" -gt 0 ] || return 0 dd if="$uki" bs=1 skip="$off" count="$sz" 2>/dev/null \ | tr -d '\000' | tr '\n\t' ' ' | sed 's/ */ /g; s/^ //; s/ $//' } render_boot_ipxe() { uki="fleet-${IMAGE_VERSION}.efi" cmdline="" [ -f "/artifacts/${uki}" ] && cmdline=$(extract_cmdline "/artifacts/${uki}") if [ -z "$cmdline" ]; then echo "WARN: could not extract .cmdline from /artifacts/${uki} — iPXE boot will" >&2 echo " hit a dracut shell under Secure Boot OFF (no embedded cmdline)." >&2 echo " Check the /artifacts mount + binutils, or enable Secure Boot." >&2 fi # Render the SAME boot logic to two files: # autoexec.ipxe — iPXE fetches + runs this at STARTUP (before autoboot). The # leading `ifopen net0` brings the link UP on NICs whose NII/UNDI link iPXE # can't establish during autoboot — e.g. EliteBook 820 G4 / Intel I219-LM, # which sits at link:down ("No configurable methods") until ifopen. Without # this, autoboot fails before it can even DHCP for boot.ipxe. # boot.ipxe — the DHCP-bootfile fallback (tag:ipxe) for firmware that DOES # autoboot-DHCP fine; harmless to carry the same ifopen. # Both feed iPXE the UKI's REAL cmdline via imgargs (Secure Boot OFF; see top). for f in /srv/tftp/autoexec.ipxe /srv/tftp/boot.ipxe; do cat > "$f" < iPXE exits -> the # firmware restarts PXE), so EVERY step that can fail is guarded with '||'. # # The link is already up + forwarding here (the TFTP fetch of this script just # succeeded), so try DHCP FIRST without touching the PHY — on a PortFast/edge # port this succeeds immediately with no delay. dhcp net0 && goto have_ip || echo initial DHCP failed; will reset link and retry # Fallback: reset the link, then retry DHCP long enough to outlast the switch's # STP forwarding delay (~30s) that the ifopen bounce triggers. A single ifopen # (held open across the loop) means the PHY bounces ONCE; STP then converges and # a later DHCP gets through. PROPER fix is PortFast/edge on the switch port so # there's no delay at all — see serving/tftp/README.md. ifopen net0 || goto err set tries 0 :dhcp_retry dhcp net0 && goto have_ip || echo DHCP retry \${tries} inc tries || goto err iseq \${tries} 30 && goto no_ip || sleep 3 goto dhcp_retry :have_ip imgfetch http://${SERVER_IP}/${uki} || goto err imgargs ${uki} ${cmdline} boot ${uki} || goto err :no_ip echo No DHCP after ~90s on net0 - enable PortFast/edge on the switch port (STP delay). :err echo Fleet iPXE chain failed (\${errno}) - dropping to iPXE shell shell EOF done echo "rendered autoexec.ipxe + boot.ipxe -> ${uki} (cmdline: ${cmdline:-})" } if [ -n "${SERVER_IP:-}" ] && [ -n "${IMAGE_VERSION:-}" ]; then render_boot_ipxe else echo "WARN: SERVER_IP/IMAGE_VERSION unset; /srv/tftp/boot.ipxe NOT rendered" >&2 fi exec dnsmasq "$@"