Keystone SystemsKS Systems

Secure Boot

Keystone integrates UEFI Secure Boot through Lanzaboote, a tool that signs NixOS boot components with custom keys. Secure Boot ensures that only trusted code executes during the boot process, forming the foundation of the trust chain that TPM-based disk encryption relies on. This page covers how Secure Boot works in Keystone, the key enrollment process, verification procedures, and key management.

How Secure Boot works

UEFI Secure Boot is a firmware feature that verifies the digital signature of every executable loaded during boot. If a binary is not signed by a trusted key, the firmware refuses to execute it. This prevents bootkits, rootkits, and unauthorized operating systems from running.

Keystone uses custom Secure Boot keys rather than relying on the pre-installed Microsoft keys found on most consumer hardware. This means the system trusts only binaries signed by the owner's keys, providing full control over the boot chain.

Lanzaboote

Lanzaboote replaces systemd-boot as the bootloader when Secure Boot is enabled. It performs the same function (loading the Linux kernel and initrd) but additionally signs all boot components with the machine's custom Secure Boot keys.

When keystone.os.secureBoot.enable is true, the module:

  1. Enables Lanzaboote with the PKI bundle stored at /var/lib/sbctl.
  2. Disables systemd-boot (Lanzaboote provides its own bootloader).
  3. Installs sbctl for key management.
  4. Registers an activation script that provisions keys on first boot.
keystone.os.secureBoot = {
  enable = true;  # default: true
};

The Secure Boot module requires an x86_64-linux system and EFI variables access (boot.loader.efi.canTouchEfiVariables = true, which the storage module enables automatically).

UEFI Setup Mode and User Mode

UEFI firmware operates in one of two modes with respect to Secure Boot:

Setup Mode exists when no Platform Key (PK) is enrolled. In this mode:

  • Secure Boot firmware is present but not enforcing signature checks.
  • Unsigned code is allowed to execute, including the Keystone installer.
  • All key variables (PK, KEK, db, dbx) can be modified freely.
  • The system transitions to User Mode once a Platform Key is enrolled.

User Mode is active after the Platform Key has been enrolled. In this mode:

  • Secure Boot signature verification is enforced.
  • Only binaries signed by keys in the db (signature database) are allowed to execute.
  • Key variables can only be modified by entities holding the appropriate keys.

New hardware typically ships in Setup Mode or with Microsoft keys pre-enrolled. The Keystone installer expects Setup Mode so that it can enroll custom keys during the first boot.

Verifying the current mode

bootctl status

Setup Mode output:

System:
     Firmware: UEFI 2.70 (EDK II 1.00)
  Secure Boot: disabled (setup)
   Setup Mode: setup

User Mode output (after key enrollment):

System:
     Firmware: UEFI 2.70 (EDK II 1.00)
  Secure Boot: enabled (user)
   Setup Mode: user

Key enrollment

Automatic enrollment during first boot

Keystone provisions Secure Boot keys automatically during the first NixOS activation after deployment. The provisioning script (modules/os/scripts/provision.sh) performs the following steps:

  1. Detect Setup Mode. The script reads the SetupMode EFI variable. If the system is already in User Mode with valid keys, provisioning is skipped.
  2. Generate keys. If no keys exist at /var/lib/sbctl/keys, sbctl create-keys generates a complete set:
    • PK (Platform Key): The root of trust. Enrolling the PK transitions the firmware from Setup Mode to User Mode.
    • KEK (Key Exchange Key): Authorizes updates to the signature database.
    • db (Signature Database): Contains the keys used to verify boot binaries.
  3. Enroll keys. The script runs sbctl enroll-keys --yes-this-might-brick-my-machine to write the keys into the UEFI firmware. Microsoft certificates are not included by default, meaning only Keystone-signed binaries are trusted.
  4. Transition to User Mode. After enrollment, the firmware transitions to User Mode and Secure Boot enforcement begins on the next reboot.

Key storage

Secure Boot keys are stored at /var/lib/sbctl/keys with the following structure:

/var/lib/sbctl/keys/
├── PK/
│   ├── PK.key    # Platform Key private key
│   └── PK.pem    # Platform Key certificate
├── KEK/
│   ├── KEK.key   # Key Exchange Key private key
│   └── KEK.pem   # Key Exchange Key certificate
└── db/
    ├── db.key    # Signature Database private key
    └── db.pem    # Signature Database certificate

These keys are generated on the target machine and never leave it. They are stored on the encrypted root filesystem, so they are protected by the disk encryption layer.

Manual key management with sbctl

The sbctl tool is installed automatically when Secure Boot is enabled. Common operations include:

# Check Secure Boot status and signed files
sudo sbctl status

# List signed EFI binaries
sudo sbctl list-files

# Verify all signed files are correctly signed
sudo sbctl verify

# Sign a new EFI binary
sudo sbctl sign /path/to/binary.efi

# Re-sign all enrolled binaries (after key rotation)
sudo sbctl sign-all

Verification

Verifying Secure Boot status

After enrollment and reboot, confirm that Secure Boot is active:

bootctl status | grep "Secure Boot"
# Expected: Secure Boot: enabled (user)

Verifying signed binaries

Confirm that all boot components are properly signed:

sudo sbctl verify

This command checks every enrolled binary against the current Secure Boot keys. All entries should show as properly signed. If any binary is unsigned or signed with a different key, Lanzaboote will fail to boot it.

Inspecting EFI variables directly

For lower-level verification, the EFI variables can be read from sysfs:

# Check SetupMode (0 = User Mode, 1 = Setup Mode)
od --address-radix=n --format=u1 \
  /sys/firmware/efi/efivars/SetupMode-8be4df61-93ca-11d2-aa0d-00e098032b8c

# Check SecureBoot (0 = not enforcing, 1 = enforcing)
od --address-radix=n --format=u1 \
  /sys/firmware/efi/efivars/SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c

# List all Secure Boot related variables
ls /sys/firmware/efi/efivars/ | grep -i secure

Resetting to Setup Mode

In some situations, it may be necessary to return the firmware to Setup Mode. This is required when:

  • Re-testing the key enrollment process.
  • Recovering from a bricked Secure Boot configuration.
  • Rotating Secure Boot keys.

On physical hardware

Most UEFI firmware provides an option in the BIOS settings to clear Secure Boot keys or reset to Setup Mode. The exact location varies by manufacturer but is typically found under "Security" or "Boot" settings.

On virtual machines

The bin/virtual-machine script provides a dedicated command:

# Shut down the VM
virsh shutdown keystone-test-vm

# Reset NVRAM to Setup Mode
./bin/virtual-machine --reset-setup-mode keystone-test-vm

# Start the VM
virsh start keystone-test-vm

Alternatively, delete and recreate the VM to start with a fresh NVRAM:

./bin/virtual-machine --reset keystone-test-vm
./bin/virtual-machine --name keystone-test-vm --start

Relationship to TPM enrollment

Secure Boot and TPM enrollment are tightly coupled in Keystone. The TPM module requires Secure Boot to be enabled (keystone.os.tpm.enable asserts keystone.os.secureBoot.enable).

The connection works through PCR 7 (Secure Boot certificates). When TPM enrollment binds the disk encryption key to PCR 7, it creates a dependency: the disk will only unlock automatically when the same Secure Boot keys are active. If Secure Boot is disabled or the keys change, the PCR 7 value changes, and the TPM refuses to unseal the disk encryption key.

This means:

  • Secure Boot protects the boot chain. Only signed code executes.
  • TPM protects the disk encryption key. The key is only released when the boot chain is verified.
  • Together they ensure that an attacker cannot boot an unauthorized OS to extract data from the encrypted disk.

After re-enrolling Secure Boot keys (which changes PCR 7), TPM enrollment must be redone. See Disk encryption for the re-enrollment procedure.

Troubleshooting

"Access Denied" on boot

This error occurs when Secure Boot is in User Mode but the boot binary is not signed by a trusted key. Common causes:

  • NixOS was rebuilt but Lanzaboote did not sign the new kernel. Run sudo sbctl sign-all and reboot.
  • The PKI bundle at /var/lib/sbctl is corrupted or missing. Regenerate keys in Setup Mode.

Firmware does not transition to User Mode

If bootctl status still shows Setup Mode after enrollment:

  • Verify that the enrollment command completed without errors.
  • Some firmware requires a reboot before the mode change takes effect.
  • Check that the firmware supports custom key enrollment (some locked-down firmware only accepts Microsoft keys).

Keys exist but Secure Boot shows "disabled"

This can occur if:

  • Secure Boot was disabled in the BIOS settings. Re-enable it.
  • The firmware was reset or updated, clearing the enrolled keys. Re-enter Setup Mode and re-run the provisioning by removing /var/lib/sbctl/keys and rebuilding.

VM testing

Keystone provides tooling for testing Secure Boot in virtual machines with full UEFI firmware emulation. The bin/virtual-machine script creates libvirt VMs with OVMF firmware in Setup Mode, including TPM 2.0 emulation. This allows testing the complete enrollment flow without physical hardware. See Testing for detailed VM testing procedures.

See also

  • Disk encryption for how TPM enrollment depends on the Secure Boot state
  • Testing for Secure Boot VM testing workflows
  • Getting started for Secure Boot key enrollment during initial installation