UEFI Secure Boot Stops Half the Nightly Reimage
A fleet of 240 build nodes is reimaged nightly from one PXE server in a four-hour window. The 180 older nodes use legacy BIOS and image without issue. Sixty new nodes - UEFI firmware, Secure Boot enabled, TPM 2.0, 25 GbE NICs - were added last month. They receive a DHCP lease, the TFTP transfer starts, and the firmware then returns to the boot menu with 'No bootable device'. The DHCP scope has a single filename "undionly.kpxe" and no per-architecture branching; both models sit in the same subnet. The kickstart file was written for the old hardware: it uses bootloader --location=mbr, one 500 MB /boot, and no EFI system partition. The new NIC driver is present in the initrd of the current installer image, so drivers are not the problem. Provisioning must stay unattended, and the security team will not disable Secure Boot fleet-wide. Nightly builds currently fail for 25% of the fleet.
1. Two independent faults in the boot chain explain why the new nodes fail while the old ones succeed. Identify both and state the minimal DHCP/TFTP change.
Read the worked answer
The failures are independent; fixing only one gets you no further than the next stage.
1. Wrong loader binary for the firmware. undionly.kpxe is an iPXE build that uses the BIOS UNDI stack. UEFI firmware cannot execute it, so the node leases an address, TFTP begins, and the firmware discards the image - exactly the 'No bootable device' symptom. Serve ipxe.efi (or snponly.efi) to EFI clients and keep undionly.kpxe for BIOS. Select by DHCP option 93 (Client System Architecture): 0x0000 = BIOS x86, 0x0007/0x0009 = EFI x64. Either split the scope by architecture or serve one iPXE loader and branch on ${platform} in the script.
2. Secure Boot rejects the unsigned EFI binary. An unsigned ipxe.efi is refused by firmware before any network boot happens. Chain through a Microsoft-signed shim (shimx64.efi) into a signed iPXE/GRUB, or enroll your own key with mokutil at factory time. Do not disable Secure Boot fleet-wide.
Verification: tcpdump -i eth0 port 67 or 68 on the PXE server to confirm the option 93 values actually sent, then the TFTP log to confirm which filename each model requested. Test one node per model before letting the full window run.
2. The new nodes now reach anaconda but abort at partitioning with an error about a missing BIOS boot or EFI partition. What must change in the kickstart, and how do you avoid maintaining two divergent files?
Read the worked answer
The kickstart is BIOS-shaped. bootloader --location=mbr on GPT with no EFI System Partition is invalid for UEFI; firmware needs a FAT ESP (typically 600 MB, --fstype=efi) and the bootloader installed to it.
Two workable approaches:
- Two kickstarts selected at boot. iPXE knows its platform, so pass
inst.ks=http://pxe/ks/${platform}.ks. Duplicate only the storage and bootloader stanzas, and%includea shared file for packages, users, services, and%postso the common parts cannot drift. - Detect in
%pre. Test[ -d /sys/firmware/efi ], write the appropriate partition snippet to/tmp/part.ks, and%includeit. It works with one file but is harder to review and to debug when%prefails.
Use GPT on both. Required lines: part /boot/efi --fstype=efi --size=600 --ondisk=sda for UEFI; part biosboot --fstype=biosboot --size=1 plus bootloader --location=mbr for BIOS; and no --location=mbr on the UEFI path. Validate on one node of each model beforehand, and confirm after install with efibootmgr -v and mokutil --sb-state. If the two paths share a template, add a CI check that renders both and diffs the storage sections.