Update pxe boot flow
This commit is contained in:
@@ -2,10 +2,18 @@
|
||||
# the version is pinned and the entrypoint is predictable). Config is passed
|
||||
# entirely as --flags from docker-compose.yml.
|
||||
FROM harbor.bitasylum.org/dockerhub/library/alpine:3.20
|
||||
RUN apk add --no-cache dnsmasq
|
||||
# binutils (objcopy): the entrypoint extracts the UKI's .cmdline PE section so it
|
||||
# can feed the REAL cmdline to iPXE (Secure Boot OFF chainload) — see entrypoint.sh.
|
||||
RUN apk add --no-cache dnsmasq binutils
|
||||
# fleet-namer: --dhcp-script that assigns creative starship hostnames per MAC,
|
||||
# server-side (keeps the workstation image neutral). COPY'd in root-owned, 0755
|
||||
# so dnsmasq will execute it. Uses busybox sha256sum/cut/wc — all in the image.
|
||||
COPY fleet-namer /usr/local/bin/fleet-namer
|
||||
RUN chmod 0755 /usr/local/bin/fleet-namer
|
||||
ENTRYPOINT ["dnsmasq"]
|
||||
# entrypoint assembles the TFTP root (copies the user's ipxe.efi from the ro
|
||||
# source mount + renders boot.ipxe, which clears the iPXE load options so the
|
||||
# UKI's embedded cmdline survives the chainload — see entrypoint.sh) then execs
|
||||
# dnsmasq with the compose args.
|
||||
COPY entrypoint.sh /usr/local/bin/dnsmasq-entrypoint
|
||||
RUN chmod 0755 /usr/local/bin/dnsmasq-entrypoint
|
||||
ENTRYPOINT ["/usr/local/bin/dnsmasq-entrypoint"]
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
#!/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-<ver>.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" <<EOF
|
||||
#!ipxe
|
||||
# NOTE: in iPXE a bare failing command aborts the script (-> 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:-<MISSING>})"
|
||||
}
|
||||
|
||||
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 "$@"
|
||||
Reference in New Issue
Block a user