Overview
Secure Boot is a UEFI feature that ensures only signed bootloaders can execute. The trust chain works as follows:
- UEFI Firmware trusts Microsoft's UEFI CA
- Shim is signed by Microsoft, so firmware trusts it
- Shim contains the distro's key (e.g., Debian, Fedora) and trusts binaries signed by that key
- Bootloader (GRUB or systemd-boot) is signed by the distro, so shim trusts it
- Kernel is either signed or loaded via MOK (Machine Owner Key)
The Shim Bootloader
Shim is the critical component for Secure Boot. Key facts:
- Signed by Microsoft: UEFI firmware trusts it
- Contains distro key: Can verify distro-signed binaries
- Hardcoded to load
grubx64.efi: Looks for this file in the same directory - Only verifies signature: Doesn't care what grubx64.efi actually is
This last point is crucial: shim will happily load a signed systemd-boot binary as long as it's named grubx64.efi and signed by the distro key.
EFI Directory Layout
For systemd-boot (Debian/Ubuntu)
/boot/EFI/
├── BOOT/
│ ├── BOOTX64.EFI <- shimx64.efi.signed (957KB)
│ ├── grubx64.efi <- systemd-bootx64.efi.signed (125KB) - NOT GRUB!
│ └── mmx64.efi <- mmx64.efi.signed (850KB, optional)
└── systemd/
└── systemd-bootx64.efi <- copy for bootctl discoverability
For GRUB2 (Fedora/RHEL)
/boot/EFI/
├── BOOT/
│ ├── BOOTX64.EFI <- shimx64.efi
│ ├── grubx64.efi <- signed grubx64.efi from container
│ └── mmx64.efi <- MOK manager (optional)
└── fedora/ <- or centos, redhat
└── grub.cfg <- GRUB configuration
Source Locations for Signed Binaries
Debian/Ubuntu
| Binary | Location in Container |
|---|---|
| Shim | /usr/lib/shim/shimx64.efi.signed |
| MOK Manager | /usr/lib/shim/mmx64.efi.signed |
| systemd-boot | /usr/lib/systemd/boot/efi/systemd-bootx64.efi.signed |
| Fallback | /usr/lib/shim/fbx64.efi.signed (DO NOT USE) |
Fedora/RHEL/CentOS
| Binary | Location in Container |
|---|---|
| Shim | /boot/efi/EFI/{distro}/shimx64.efi |
| GRUB | /boot/efi/EFI/{distro}/grubx64.efi |
| MOK Manager | /boot/efi/EFI/{distro}/mmx64.efi |
What NOT to Do
Do NOT install fbx64.efi
The fallback bootloader (fbx64.efi) causes a "Restore Boot Option" blue screen because:
- fbx64.efi looks for
EFI/<distro>/BOOTX64.CSV - Our setup uses
EFI/BOOT/(removable media path) - When CSV is not found, fbx64.efi shows the blue screen
We use efibootmgr to register boot entries instead.
Do NOT use unsigned binaries
grub-installproduces unsigned GRUB EFI binaries- These will be rejected by Secure Boot
- Always use the signed binaries from the container image
Do NOT use signed systemd-boot directly as BOOTX64.EFI
- Distro signing keys are not in UEFI firmware's trust store
- Only Microsoft-signed binaries (like shim) are trusted directly
- Must go through shim chain
Do NOT assume EFI directory case
- Container extraction may create lowercase
efi - FAT32 is case-insensitive but case-preserving
- Use two-step rename:
efi->efi_tmp->EFI
Debugging Guide
"Restore Boot Option" Blue Screen
Cause: fbx64.efi is being executed
Solution: Remove fbx64.efi from EFI/BOOT/
# From rescue environment
mount /dev/sda1 /mnt
rm /mnt/EFI/BOOT/fbx64.efi
umount /mnt
reboot
"Access Denied" Error
Cause: Secure Boot rejected an unsigned or incorrectly-signed binary
Solutions:
- Ensure shim is used as BOOTX64.EFI (not systemd-boot directly)
- Verify grubx64.efi is the signed binary from the container
- Check that shim and bootloader are from the same distro
System Boots to Wrong Device
Cause: Boot order not set correctly
Solution:
# Check current order
efibootmgr -v
# Set your OS first (replace 0008 with your boot entry)
efibootmgr -o 0008,0002,0003
bootctl Shows "Secure Boot: disabled"
Possible causes:
- Secure Boot not enabled in firmware
- Booted directly, not through shim
Verification:
mokutil --sb-state # Should say "SecureBoot enabled"
Investigation History (December 2025)
This section documents the debugging process that led to the current implementation.
Initial Problem
After installing a Debian system with systemd-boot using nbc, Secure Boot failed with various symptoms including "Restore Boot Option" blue screen.
Failed Attempt 1: Using unsigned bootloaders
- Tried using
grub-installoutput directly - Result: Secure Boot rejected the unsigned binary
Failed Attempt 2: Signed systemd-boot as BOOTX64.EFI
- Assumption: Distro-signed binaries are in Microsoft's trust chain
- Reality: They're not - only shim is Microsoft-signed
- Result: "Access Denied"
Failed Attempt 3: Shim + systemd-boot + fbx64.efi
- Included fbx64.efi thinking it was needed for boot recovery
- Result: "Restore Boot Option" blue screen
- Root cause: fbx64.efi couldn't find BOOTX64.CSV
Failed Attempt 4: Incorrect assumption about systemd-boot path
- Assumed systemd-boot couldn't work as grubx64.efi because it "needs to know its path"
- This was wrong - systemd-boot finds loader.conf relative to ESP root
- Wasted time trying alternative approaches
Working Solution
- Shim as BOOTX64.EFI - Microsoft-signed, trusted by firmware
- Signed systemd-boot as grubx64.efi - Distro-signed, trusted by shim
- MOK manager - For key enrollment if needed
- NO fbx64.efi - Causes blue screen
Key Insight
Shim doesn't care what grubx64.efi actually is. It only:
- Looks for a file named
grubx64.efiin the same directory - Verifies the signature against the embedded distro key
- Loads and executes it if signature is valid
This means systemd-boot works perfectly as grubx64.efi because:
- It's signed by Debian's key
- Shim trusts Debian's key
- systemd-boot doesn't care what filename it's loaded as
Testing Secure Boot
Create a Secure Boot VM with Incus
# Create VM with Secure Boot enabled
incus launch images:debian/trixie myvm --vm \
-c security.secureboot=true
# Attach install ISO
incus config device add myvm iso disk \
source=/path/to/installer.iso boot.priority=10
# Start and connect
incus start myvm
incus console myvm
Verify Secure Boot Status
# Inside the running system
mokutil --sb-state
# Should output: SecureBoot enabled
bootctl status
# Should show: Secure Boot: enabled (user)
Verify Correct Binaries
# Check file sizes to identify binaries
ls -la /boot/EFI/BOOT/
# Expected:
# BOOTX64.EFI ~957KB (shim)
# grubx64.efi ~125KB (systemd-boot) or ~2MB (GRUB)
# mmx64.efi ~850KB (MOK manager)
Related Files
pkg/bootloader.go- Main bootloader installation codesetupSystemdBootSecureBootChain()- systemd-boot Secure Boot setupsetupSecureBootChain()- GRUB2 Secure Boot setupfindShimEFI(),findSignedSystemdBootEFI(), etc. - Binary locators