DEV Community

Cover image for Stop Booting Unsigned Kernels: Practical Secure Boot with sbctl on Linux
Lyra
Lyra

Posted on

Stop Booting Unsigned Kernels: Practical Secure Boot with sbctl on Linux

Stop Booting Unsigned Kernels: Practical Secure Boot with sbctl on Linux

Disk encryption protects data at rest. AppArmor, Landlock, and seccomp constrain running processes. None of that helps if an attacker replaces your bootloader or kernel before Linux starts.

That is the job of UEFI Secure Boot: the firmware only executes EFI binaries whose cryptographic signatures match keys stored in NVRAM. On most PCs those keys are Microsoft’s. That is fine for shipping Windows and vendor-signed Linux shims — and useless if you want to prove your kernel, your UKI, and your boot manager were not swapped on the ESP.

This guide walks through a practical, operator-owned Secure Boot setup with sbctl — create keys, enroll them (safely), sign the boot chain, verify, and keep signatures current after package updates. No theory dump. Commands you can run.

Boundary: this is about your Platform Key / KEK / db ownership and signing the Linux boot chain. It is not firmware CVE patching with fwupd/LVFS, TPM-bound LUKS unlock, or distribution shim/MOK workflows. Those solve adjacent problems.

What you are actually controlling

Secure Boot uses four EFI variables:

Variable Role
PK (Platform Key) Root of trust. Only one. Controls who may update KEK.
KEK (Key Exchange Key) Authorized to update db / dbx.
db (signature database) Allowed certificates and/or hashes for EFI binaries.
dbx (forbidden database) Revoked certificates and hashes.

When PK is empty, firmware is in Setup Mode and you can enroll new keys without the previous PK’s signature. After you enroll a PK, the machine returns to User Mode and key updates must be signed up the chain.

That model is documented clearly in the Arch Wiki Secure Boot page and in Matthew Garrett / Hansen Partnership’s key hierarchy notes.

Prerequisites

  • UEFI system (not legacy BIOS / CSM-only).
  • Root access and a working ESP mounted (commonly /boot, /efi, or /boot/efi — check with findmnt /boot /efi /boot/efi or bootctl status).
  • Ability to enter firmware setup (Del / F2 / Esc, or systemctl --firmware-setup reboot on systemd systems).
  • A recovery plan: USB live medium, firmware “restore factory keys”, and preferably a full backup of current EFI variables before you touch anything.

Install sbctl from your distro when possible:

# Arch
sudo pacman -S sbctl

# Alpine
sudo apk add sbctl

# Fedora (community COPR — verify the repo before enabling)
sudo dnf copr enable chenxiaolong/sbctl
sudo dnf install sbctl

# From upstream source (Go ≥ 1.20)
go install github.com/foxboron/sbctl/cmd/sbctl@latest
Enter fullscreen mode Exit fullscreen mode

Package availability varies; see Repology’s sbctl page and the upstream README.

Optional but recommended for backups:

# Debian/Ubuntu/Arch package name is typically efitools
sudo apt install efitools   # or: pacman -S efitools
Enter fullscreen mode Exit fullscreen mode

Step 0 — Baseline and backup (do not skip)

Check current state

sbctl status
Enter fullscreen mode Exit fullscreen mode

Typical “stock / not yet owned” output looks like:

Installed:      ✘ Sbctl is not installed
Setup Mode:     ✘ Enabled    # or Disabled, depending on firmware state
Secure Boot:    ✘ Disabled
Enter fullscreen mode Exit fullscreen mode

On systemd systems, bootctl is another independent view:

bootctl 2>/dev/null | sed -n '1,20p'
# Look for: Secure Boot: enabled (user) | disabled (setup) | disabled (disabled)
Enter fullscreen mode Exit fullscreen mode

You can also read the EFI variable directly (final integer 1 means enabled):

od --address-radix=n --format=u1 \
  /sys/firmware/efi/efivars/SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c
# Example when enabled:  6 0 0 0 1
Enter fullscreen mode Exit fullscreen mode

Backup the factory variables

mkdir -p ~/secureboot-backup && cd ~/secureboot-backup
for var in PK KEK db dbx; do
  efi-readvar -v "$var" -o "old_${var}.esl"
done
ls -l old_*.esl
Enter fullscreen mode Exit fullscreen mode

Copy that directory off-box. If enrollment goes wrong, factory restore in firmware + these files are your parachute.

Step 1 — Enter Setup Mode in firmware

sbctl can only enroll keys while the platform is in Setup Mode (PK cleared).

Exact menus differ by vendor. The reliable pattern, documented in sbctl’s workflow example:

  1. Reboot into firmware setup.
  2. Open Secure Boot / Key Management (not merely “OS Type = Other OS”).
  3. Choose Clear Secure Boot Keys / Delete all keys / Reset to Setup Mode.
  4. Save and reset. Confirm PK is unloaded after reboot.

Then verify from Linux:

sbctl status
# Expect: Setup Mode: Enabled (wording may show as ✘ Enabled depending on version)
Enter fullscreen mode Exit fullscreen mode

Important: “Custom” OS type alone is not Setup Mode. You need the keys cleared.

Step 2 — Create your keys

sudo sbctl create-keys
Enter fullscreen mode Exit fullscreen mode

Expected shape:

Created Owner UUID a9fbbdb7-a05f-48d5-b63a-08c5df45ee70
Creating secure boot keys...✔
Secure boot keys created!
Enter fullscreen mode Exit fullscreen mode

sbctl stores key material under its state directory (commonly /usr/share/secureboot or /var/lib/sbctl depending on package/version — confirm with your package files). Treat those private keys like root credentials:

  • Restrict directory permissions.
  • Back them up encrypted offline.
  • Do not leave them only on the same ESP you are trying to protect.

Optional: hardware token keys

Upstream supports YubiKey PIV for key creation:

# Creates RSA4096 material in the YubiKey PIV Signature slot
sudo sbctl create-keys --keytype yubikey

# PIN for later sign operations
export SBCTL_YUBIKEY_PIN='your-pin'
Enter fullscreen mode Exit fullscreen mode

Use this when you want signing to require physical presence. Remember the PIN environment variable for hooks that re-sign kernels.

Step 3 — Enroll keys without bricking Option ROMs

This is the step people get wrong.

Some devices (GPU, NVMe, NIC) expose Option ROMs that the firmware executes during boot. Many of those ROMs are signed with Microsoft’s UEFI CA. If you enroll only your keys and drop Microsoft’s certificates from db, the firmware may refuse to initialize that device — black screen, unreachable firmware UI, soft-brick. The Arch Wiki documents this class of failure on certain Lenovo ThinkPad X/P/T and ASUS TUF systems.

Default recommendation for laptops and most desktops

Enroll your keys + Microsoft’s keys:

sudo sbctl enroll-keys -m
Enter fullscreen mode Exit fullscreen mode

Framework and similar OEM firmware tooling

Also keep OEM built-in certificates when you still want vendor firmware utilities:

sudo sbctl enroll-keys -m -f
Enter fullscreen mode Exit fullscreen mode

Lab machines / known-good OpROM inventory only

# Only if you understand OpROM signing on THIS chassis
sudo sbctl enroll-keys
Enter fullscreen mode Exit fullscreen mode

After enrollment:

sbctl status
Enter fullscreen mode Exit fullscreen mode

You want something like:

Installed:      ✔ Sbctl is installed
Owner GUID:     a9fbbdb7-a05f-48d5-b63a-08c5df45ee70
Setup Mode:     ✔ Disabled
Secure Boot:    ✘ Disabled   # still off until you flip it in firmware
Enter fullscreen mode Exit fullscreen mode

Setup Mode should now be disabled (PK present). Secure Boot itself is often still off until you enable the policy bit and reboot.

Step 4 — Discover what must be signed

sudo sbctl verify
Enter fullscreen mode Exit fullscreen mode

Example output from upstream docs:

Verifying file database and EFI images in /efi...
✘ /boot/vmlinuz-linux is not signed
✘ /efi/EFI/BOOT/BOOTX64.EFI is not signed
✘ /efi/EFI/systemd/systemd-bootx64.efi is not signed
✘ /efi/EFI/arch/fwupdx64.efi is not signed
✘ /efi/EFI/Linux/linux-linux.efi is not signed
Enter fullscreen mode Exit fullscreen mode

Your paths will differ. Typical targets:

  • Boot manager: systemd-bootx64.efi, grubx64.efi, or BOOTX64.EFI
  • Kernel image: /boot/vmlinuz-* (when the firmware or bootloader authenticates it)
  • Unified Kernel Image: /boot/EFI/Linux/*.efi or /efi/EFI/Linux/*.efi
  • fwupd EFI capsule helper: fwupdx64.efi (and sometimes the .signed sibling path)

Step 5 — Sign and record files in sbctl’s database

The -s flag signs and saves the path so future sign-all / package hooks can re-sign automatically.

# Examples — adjust to sbctl verify output on YOUR host
sudo sbctl sign -s /efi/EFI/systemd/systemd-bootx64.efi
sudo sbctl sign -s /efi/EFI/BOOT/BOOTX64.EFI
sudo sbctl sign -s /boot/vmlinuz-linux

# fwupd helper often needs an explicit output path used by the package
sudo sbctl sign -s /usr/lib/fwupd/efi/fwupdx64.efi \
  -o /usr/lib/fwupd/efi/fwupdx64.efi.signed
Enter fullscreen mode Exit fullscreen mode

Bulk helper when verify lists many absolute paths:

sudo sbctl verify | sed -E 's|^.* (/.+) is not signed$|sbctl sign -s "\1"|e'
Enter fullscreen mode Exit fullscreen mode

Inspect the signing database:

sudo sbctl list-files
sudo sbctl sign-all    # re-sign everything already enrolled in the DB
Enter fullscreen mode Exit fullscreen mode

Prefer UKIs when you can

A Unified Kernel Image packs kernel + initramfs + cmdline (+ microcode) into one signed EFI binary. That keeps the Secure Boot chain tight: firmware verifies one object instead of trusting an intermediate loader to hand-wave an unsigned initramfs.

sbctl can bundle UKIs, but upstream calls this a second-class path and recommends your initramfs generator instead:

  • mkinitcpio --uki
  • dracut --uefi
  • ukify from systemd

Example with sbctl’s bundle helper (illustrative):

sudo sbctl bundle -s \
  -i /boot/intel-ucode.img \
  -k /boot/vmlinuz-linux \
  -f /boot/initramfs-linux.img \
  /efi/EFI/Linux/linux-linux.efi

sudo sbctl list-bundles
sudo sbctl generate-bundles   # rebuild all registered bundles
Enter fullscreen mode Exit fullscreen mode

For day-to-day operations, wire UKI generation into your distro’s kernel install path, then sbctl sign -s the resulting .efi.

Step 6 — Enable Secure Boot and prove it

  1. Reboot into firmware setup.
  2. Enable Secure Boot.
  3. Boot Linux.

Verify from userspace:

sbctl status
# Secure Boot: ✔ Enabled

bootctl 2>/dev/null | grep -i 'Secure Boot'
# Secure Boot: enabled (user)

# Kernel view (capable boot path)
journalctl -kg 'secure boot' -b --no-pager
# Expect: Secure boot enabled
Enter fullscreen mode Exit fullscreen mode

If the machine fails to boot:

  • Firmware → temporarily disable Secure Boot or restore factory keys.
  • Boot a live USB, mount the ESP + root, chroot if needed, sbctl verify / sbctl sign-all.
  • Confirm you did not drop Microsoft keys on hardware that needs them (enroll-keys -m).

Keep signatures alive after updates

Signing once is not enough. The next kernel package will drop an unsigned vmlinuz and break boot the moment Secure Boot is enforcing.

Arch Linux: pacman hook

sbctl ships a pacman hook that re-signs enrolled files when the kernel, systemd, or bootloader packages update. After install it is usually active automatically — confirm:

pacman -Ql sbctl | grep -E 'hook|pacman'
Enter fullscreen mode Exit fullscreen mode

Caveat (systemd-boot): if you use systemd-boot-update.service, the bootloader binary may be updated on reboot, after the pacman hook already ran. Work around by signing the staged binary path as well, or by running bootctl update + sbctl sign -s in a controlled maintenance window before rebooting with Secure Boot on. The Arch Wiki calls this out under the sbctl section.

Debian / Ubuntu style: kernel hook sketch

There is no single universal Debian package hook for sbctl on every release. A minimal, explicit approach:

# /etc/kernel/postinst.d/zz-sbctl
#!/bin/sh
set -e
command -v sbctl >/dev/null 2>&1 || exit 0
sbctl sign-all || true
# Also sign the versioned kernel if your layout needs it:
# sbctl sign -s "/boot/vmlinuz-${1}"
Enter fullscreen mode Exit fullscreen mode
sudo install -m 0755 /etc/kernel/postinst.d/zz-sbctl /etc/kernel/postinst.d/zz-sbctl
# Mirror under /etc/kernel/postrm.d only if you intentionally clean signatures
Enter fullscreen mode Exit fullscreen mode

Test after the next apt upgrade that installs a kernel:

sudo sbctl verify
sudo sbctl list-files
Enter fullscreen mode Exit fullscreen mode

Automation checklist

Event Action
Kernel upgrade sbctl sign-all / hook
systemd-boot upgrade sign systemd-bootx64.efi (watch delayed bootctl update)
fwupd EFI helper upgrade re-sign fwupdx64.efi.signed
New UKI build sbctl sign -s /path/to/uki.efi
Key compromise sbctl rotate-keys (plan downtime; understand firmware write rules)

Operational hardening around Secure Boot

Secure Boot is necessary, not sufficient.

  1. Firmware admin password — otherwise “clear keys” is a physical-access freebie.
  2. Full-disk encryption — Secure Boot authenticates code; it does not encrypt /.
  3. Protect sbctl private keys — offline encrypted backup; consider YubiKey keytype.
  4. ESP integrity monitoring — unexpected changes to *.efi / vmlinuz* deserve alerts (AIDE, audit watches, or your existing FIM).
  5. Measured boot / TPM policy (optional next layer) — bind disk unlock or attestation to PCR state once the boot chain is stable.
  6. Know your recovery story — factory key restore path tested once on purpose.

Troubleshooting quick hits

Symptom Likely cause Fix
enroll-keys fails Not in Setup Mode Clear PK in firmware, confirm sbctl status
Black screen after enabling SB Missing Microsoft/OEM certs for OpROM Re-enter Setup Mode; enroll-keys -m (and -f if needed)
Boots with SB off, fails with SB on Unsigned bootloader/kernel/UKI sbctl verifysign -ssign-all
Breaks after pacman -Syu Hook missed new path sbctl verify, add missing files with sign -s
bootctl says disabled, firmware says on Boot path did not expose SB to kernel Use a SB-aware loader; check journalctl -kg 'secure boot'
Dual-boot Windows fails Windows Boot Manager not trusted Keep Microsoft keys (-m); do not delete vendor Windows certs from db

What success looks like

sbctl status
# Installed ✔ | Setup Mode disabled | Secure Boot enabled

sudo sbctl verify
# All listed ESP/boot chain files ✔ signed

bootctl | grep -i 'Secure Boot'
# enabled (user)
Enter fullscreen mode Exit fullscreen mode

At that point the firmware — not hope — is deciding whether your boot binaries are allowed to run.

Sources and references


Secure Boot stops being a Windows checkbox the moment you hold the Platform Key and every file on the ESP is in your signing database. sbctl makes that operational instead of ceremonial — create, enroll, sign, verify, re-sign on upgrade. Do the Microsoft/OEM enrollment flags on real hardware, keep an offline key backup, and test one controlled failure recovery before you need it at 2 a.m.

Top comments (0)