Files
Vetting/internal/pxe/ipxe.go
T
josh a88e24bef4
CI / Lint + build + test (push) Successful in 1m23s
Release / release (push) Successful in 4m49s
live-image: real /init + verbose boot for first-boot diagnosis
Host boots past kernel init and then stalls silently. ACPI DSDT error
about TXHC.RHUB.SS01 is benign noise (Tiger Lake firmware bug) — the
actual problem is that nothing between kernel handoff and (maybe)
systemd is visible on the console.

Two changes:

1. Replace the /init → sbin/init symlink with a real shell script
   (live-image/mkosi.extra/init) that mounts /proc /sys /dev /dev/pts
   /dev/shm /run before execing systemd. Systemd has fallback mount
   code for these, but when it fails the failure is silent. Doing it
   explicitly in /init keeps failures visible and avoids the fragile
   symlink-resolution trick.

2. Drop 'quiet' from the kernel cmdline and add loglevel=7 plus
   systemd.log_target=kmsg + journald.forward_to_console=1 so every
   early-boot message reaches both tty0 and ttyS0. Will be dialed
   back once boot is stable.

Also: .gitattributes pins LF on live-image/, .gitea/, Makefile, and
*.sh so Windows checkouts don't break shell scripts and Makefile
recipes with CRLF. /init also gets chmod 0755 in repack-initrd as a
belt-and-braces against mode loss on non-Linux checkouts.
2026-04-18 14:31:40 -04:00

96 lines
3.4 KiB
Go

package pxe
import (
"fmt"
"io"
"strings"
"vetting/internal/model"
)
// IPXEParams is everything an iPXE boot script needs.
// For Phase 2 the boot target is always "linux" — Memtest chain-load
// is not required because we replaced Memtest86+ with stress-ng under
// Linux (see plan §3.2).
type IPXEParams struct {
OrchestratorURL string // e.g. http://10.0.0.5:8080
LiveKernelURL string // e.g. http://10.0.0.5:8080/live/vmlinuz
LiveInitrdURL string // e.g. http://10.0.0.5:8080/live/initrd.img
TLSCertFPR string // optional; empty = skip pin
RunID int64
MAC string
Token string // plaintext, hashed on server side
}
// BuildScript returns an iPXE script tailored for this run.
// iPXE scripts are plain text beginning with "#!ipxe".
func BuildScript(p IPXEParams) string {
cmdline := []string{
"initrd=initrd.img",
fmt.Sprintf("vetting.orchestrator=%s", p.OrchestratorURL),
fmt.Sprintf("vetting.run_id=%d", p.RunID),
fmt.Sprintf("vetting.mac=%s", p.MAC),
fmt.Sprintf("vetting.token=%s", p.Token),
}
if p.TLSCertFPR != "" {
cmdline = append(cmdline, fmt.Sprintf("vetting.cert_fpr=%s", p.TLSCertFPR))
}
// Verbose kernel + systemd logging on both the video console and the
// serial port so first-boot failures on unfamiliar hardware aren't
// invisible. Drop `quiet` entirely — once boot is stable we can
// re-add it. systemd.log_target=kmsg makes early systemd go through
// the same dmesg buffer as the kernel, so nothing is lost before
// journald comes up.
cmdline = append(cmdline,
"console=tty0",
"console=ttyS0,115200n8",
"ip=dhcp",
"loglevel=7",
"systemd.log_level=info",
"systemd.log_target=kmsg",
"systemd.journald.forward_to_console=1",
)
var b strings.Builder
fmt.Fprintln(&b, "#!ipxe")
fmt.Fprintf(&b, "echo Vetting run %d — booting live image for %s\n", p.RunID, p.MAC)
fmt.Fprintf(&b, "kernel %s %s\n", p.LiveKernelURL, strings.Join(cmdline, " "))
fmt.Fprintf(&b, "initrd %s\n", p.LiveInitrdURL)
fmt.Fprintln(&b, "boot")
return b.String()
}
// NotRegisteredScript is served for unknown MACs. The MAC allowlist
// at the dnsmasq level should prevent this from ever being reachable,
// but it exists as belt-and-braces.
func NotRegisteredScript(mac string) string {
return fmt.Sprintf("#!ipxe\necho MAC %s not registered for vetting — halting.\nshell\n", mac)
}
// NoActiveRunScript is served when a registered MAC PXE-boots but has
// no currently active run. The host is told to shut down rather than
// loop forever.
func NoActiveRunScript(mac string) string {
return fmt.Sprintf("#!ipxe\necho MAC %s has no active run — powering off in 10s.\nsleep 10\npoweroff\n", mac)
}
// Used by handlers to compose URLs; exposed for tests.
func BuildLiveURLs(base string) (kernel, initrd string) {
base = strings.TrimRight(base, "/")
return base + "/live/vmlinuz", base + "/live/initrd.img"
}
// WriteNotFound is a small convenience so handlers can return a shell
// script error directly to iPXE without cluttering handlers with a
// mime-type dance.
func WriteNotFound(w io.Writer, mac string) {
_, _ = w.Write([]byte(NotRegisteredScript(mac)))
}
// ScriptMarker is used by iPXE to detect that the response is a script.
const ScriptMarker = "#!ipxe"
// State returns the compact single-word status used for logging.
// Takes a Run's state because iPXE handler already looked it up.
func State(run model.Run) string { return string(run.State) }