From c494c7b226e9240508e44f583b8d3f7adeca64dd Mon Sep 17 00:00:00 2001 From: Ilker Manap Date: Fri, 30 Oct 2026 11:28:56 +0100 Subject: [PATCH] GRUB local boot chainloads the disk's signed loader; image sums On the Secure Boot lab VM, GRUB's "exit" back to the firmware did not reach the disk: OVMF went on to its other network boot options and stopped in its own boot menu. The local-disk entry now finds the installed OS's signed loader on the EFI partition (shimx64.efi under EFI/debian, ubuntu, rocky, almalinux, redhat, centos; shim.efi for SUSE; else EFI/BOOT/BOOTX64.EFI) and chainloads it, as MAAS does. build-image.sh writes SHA256SUMS next to the image (the old one was stale after a rebuild; bootstrap.sh --image-url checks it). Co-Authored-By: Claude Opus 5.5 --- agent/build-image.sh | 2 ++ cmd/rollcall/grub.go | 20 +++++++++++++++++++- cmd/rollcall/grub_test.go | 5 +++-- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/agent/build-image.sh b/agent/build-image.sh index fe3478b..ba8d10e 100644 --- a/agent/build-image.sh +++ b/agent/build-image.sh @@ -125,5 +125,7 @@ for f in vmlinuz initrd.img filesystem.squashfs; do [ -n "$src" ] || { echo "missing $f in binary/live" >&2; exit 1; } install -m 0644 "$src" "$OUT/$f" done +# checked by bootstrap.sh --image-url when another region copies this one +(cd "$OUT" && sha256sum vmlinuz initrd.img filesystem.squashfs > SHA256SUMS) ls -la "$OUT" echo "=== done $(date)" diff --git a/cmd/rollcall/grub.go b/cmd/rollcall/grub.go index 7184f51..e432222 100644 --- a/cmd/rollcall/grub.go +++ b/cmd/rollcall/grub.go @@ -97,7 +97,7 @@ func pxelinuxToGrub(body string) string { case e.poweroff: b.WriteString(" halt\n") case e.local: - b.WriteString(" exit 1\n") // back to the firmware: its next boot option is the disk + b.WriteString(grubLocalBoot) case e.kernel != "": fmt.Fprintf(&b, " linux %s", grubQuote("/"+strings.TrimPrefix(e.kernel, "/"))) for _, w := range strings.Fields(e.append) { @@ -115,6 +115,24 @@ func pxelinuxToGrub(body string) string { return b.String() } +// grubLocalBoot starts the installed OS by chainloading its signed loader +// from the disk's EFI partition (shim, which Secure Boot accepts). Handing +// back to the firmware with "exit" is not enough: firmware then tries its +// other network boot options and ends in its own menu (OVMF does). +const grubLocalBoot = ` insmod part_gpt + insmod fat + insmod chain + for f in /EFI/debian/shimx64.efi /EFI/ubuntu/shimx64.efi /EFI/rocky/shimx64.efi /EFI/almalinux/shimx64.efi /EFI/redhat/shimx64.efi /EFI/centos/shimx64.efi /EFI/opensuse/shim.efi /EFI/sles/shim.efi /EFI/BOOT/BOOTX64.EFI; do + if search --no-floppy --set=root --file $f; then + chainloader $f + boot + fi + done + echo "no EFI loader found on the disks" + sleep 10 + exit 1 +` + var grubPlain = regexp.MustCompile(`^[A-Za-z0-9_./:=,+@%-]+$`) // grubQuote makes a word safe in GRUB's script language (no $ expansion, no ;). diff --git a/cmd/rollcall/grub_test.go b/cmd/rollcall/grub_test.go index cd32028..2e7ed60 100644 --- a/cmd/rollcall/grub_test.go +++ b/cmd/rollcall/grub_test.go @@ -15,14 +15,15 @@ func TestPxelinuxToGrub(t *testing.T) { "menuentry 'Commissioned: power off (default in 15s)' {\n halt\n}", "menuentry 'Re-run commissioning' {\n linux /rollcall/vmlinuz boot=live components fetch=http://10.213.77.1/rollcall/filesystem.squashfs", " initrd /rollcall/initrd.img\n", - "menuentry 'Boot from local disk' {\n exit 1\n}", + "menuentry 'Boot from local disk' {\n insmod part_gpt\n", + "chainloader $f", } { if !strings.Contains(g, want) { t.Errorf("missing %q in\n%s", want, g) } } deployed := fmt.Sprintf(deployedTmpl, "now", "m-1", "debian-13", "1", "node01", "debian-13", "m-1", "debian-13", "boot=live") - if g := pxelinuxToGrub(deployed); !strings.Contains(g, "set default=0\n") || !strings.Contains(g, "exit 1") { + if g := pxelinuxToGrub(deployed); !strings.Contains(g, "set default=0\n") || !strings.Contains(g, "/EFI/debian/shimx64.efi") { t.Errorf("deployed:\n%s", g) } // nothing from a cmdline can break out of the menu entry