Secure Boot ZFSBootMenu with Custom Keys and Redundant ESPs
If you run Arch Linux on a mirrored ZFS root pool, you likely have redundant EFI System Partitions (ESPs) mapped to multiple NVMe drives for resilience.
However, protecting this boot path with Secure Boot can be a challenge. With the use of Secure Boot key manager you can take ownership of your motherboard’s platform security, automat syncing across multiple drives, and verify the integrity of your native ZFS boot environment.
The Tools⌗
- ZFSBootMenu: ZFSBootMenu is a Linux bootloader that attempts to provide an experience similar to FreeBSD’s bootloader. It is built on an initramfs that can natively discover, import, and boot from ZFS pools. It has plenty of other nifty tricks too.
- sbctl: This tool intends to be a user-friendly secure boot key manager capable of setting up secure boot, offer key management capabilities, and keep track of files that needs to be signed in the boot chain.
Aligning the ZFSBootMenu Configuration⌗
ZFSBootMenu includes a tool, generate-zbm, that can be used to build and
bundle all necessary components for a initramfs into a single Unified Kernel
Image (UKI). It uses a special file, /etc/zfsbootmenu/config.yaml, to manage
this process. The docs for the config.yaml can be found
here
The key things to set in config.yaml:
ManageImages: true- without thisgenerate-zbmwill do nothing.Enabled: falseinsideComponents- make sure it makes a single UKI.Versions: falseinsideEFI- make sure it doesn’t version control the file name.Prefix: VMLINUZinsideKernel- make sure the final filenames match the exact capitalization expected (e.g.,VMLINUZ.EFIandVMLINUZ-BACKUP.EFI)
An example of my config.yaml file.
Global:
ManageImages: true
BootMountPoint: /boot/efi
DracutConfDir: /etc/zfsbootmenu/dracut.conf.d
PreHooksDir: /etc/zfsbootmenu/generate-zbm.pre.d
PostHooksDir: /etc/zfsbootmenu/generate-zbm.post.d
InitCPIOConfig: /etc/zfsbootmenu/mkinitcpio.conf
Components:
Enabled: false
EFI:
ImageDir: /boot/efi/EFI/ZBM
Versions: false
Enabled: true
SplashImage: /etc/zfsbootmenu/splash.bmp
Kernel:
CommandLine: ro quiet loglevel=0
Prefix: VMLINUZ
Synchronizing Redundant ESPs via Post-Hooks⌗
By default, generate-zbm only knows how to write to its primary mount point
(/boot/efi). If you maintain a secondary mirror drive mounted at
/boot/efi1, you can utilize ZFSBootMenu’s native hook framework to clone your
boot loader automatically upon creation.
Here is the post-generate script I am using to keep the directories synced.
sudo tee "/etc/zfsbootmenu/generate-zbm.post.d/sync-backup-esp" > /dev/null <<'EOF'
#!/bin/bash
SOURCE_DIR="/boot/efi/EFI/ZBM"
TARGET_DIR="/boot/efi1/EFI/ZBM"
if [ -d "$SOURCE_DIR" ] && [ -d "/boot/efi1/EFI" ]; then
echo "--- Syncing ZFSBootMenu to secondary ESP (/boot/efi1) ---"
mkdir -p "$TARGET_DIR"
rsync -av --delete "$SOURCE_DIR/" "$TARGET_DIR/"
else
echo "WARNING: Secondary ESP missing. Sync skipped."
fi
EOF
sudo chmod +x /etc/zfsbootmenu/generate-zbm.post.d/sync-backup-esp
NOTE: the post-generation script directory processing framework ignores files with shell extensions, so drop the
.shsuffix. Also, theechonever print. Or at least, I’ve never seen them print. I didn’t get that to work so I gave up.
Fixing the Hostid Mismatch⌗
Before cooking the new images, verify that your active system hostid matches what your ZFS module expects. A mismatch will cause ZFSBootMenu to drop to a recovery shell because it assumes the pool belongs to another physical computer.
If your generator throws a hostid warning, force-sync it:
sudo rm -f /etc/hostid
sudo zgenhostid 00bab10c
(Replace 00bab10c with the specific hostid requested by your pool).
Regenerate your images to bake the correct hostid into the initramfs:
sudo generate-zbm
Enrolling Keys and Securing the Boot Chain⌗
With your firmware in Setup Mode, install sbctl and initialize your
platform keys.
sudo pacman -S sbctl
sudo sbctl create-keys
Next, enroll your keys into the motherboard’s NVRAM. Use the -m flag to
include Microsoft’s standard OEM keys alongside yours. This ensures option ROMs
on internal components (like graphics cards) and dual-boot continue to work.
sudo sbctl enroll-keys -m
Verify the status:
sudo sbctl status
Signing Binaries Across Both Drives⌗
The motherboard will now instantly reject any untrusted binary. Instruct
sbctl to sign and track (-s) all four ZFSBootMenu images distributed across
your primary and secondary NVMe drives:
# Primary ESP files
sudo sbctl sign -s /boot/efi/EFI/ZBM/VMLINUZ.EFI
sudo sbctl sign -s /boot/efi/EFI/ZBM/VMLINUZ-BACKUP.EFI
# Secondary ESP files
sudo sbctl sign -s /boot/efi1/EFI/ZBM/VMLINUZ.EFI
sudo sbctl sign -s /boot/efi1/EFI/ZBM/VMLINUZ-BACKUP.EFI
Finally, verify the state of your boot paths:
$ sudo sbctl verify
✓ /boot/efi/EFI/ZBM/VMLINUZ.EFI is signed
✓ /boot/efi/EFI/ZBM/VMLINUZ-BACKUP.EFI is signed
✓ /boot/efi1/EFI/ZBM/VMLINUZ.EFI is signed
✓ /boot/efi1/EFI/ZBM/VMLINUZ-BACKUP.EFI is signed
Because sbctl integrates directly with pacman hooks, any future upstream
upgrades to your kernel or ZFSBootMenu system will automatically re-build,
mirror across your hardware array, and sign the resulting binaries out of the
box.