Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Introduction

This documentation helps users set up UEFI Secure Boot for their NixOS system using a custom keychain. The audience are experienced NixOS users.

Secure Boot for NixOS is still in development and has some sharp edges. There may be cases where you end up with a system that does not boot.

We only recommend setting up Secure Boot to NixOS users that are comfortable using recovery tools to restore their system or have a backup ready.

At this point we have tested a few notebooks and are confident about Lenovo ThinkPads and Framework notebooks. However, Secure Boot support is known to be inconsistent even on notebooks from the same product line so we cannot give guarantees on the applicability of what we describe here.

Prerequisites

To be able to setup Secure Boot on your device, NixOS needs to be installed in UEFI mode and systemd-boot must be used as a boot loader. This means if you wish to install Lanzaboote on a new machine, you need to follow the install instruction for systemd-boot and then switch to Lanzaboote after the first boot.

These prerequisites can be checked via bootctl status:

$ bootctl status
System:
     Firmware: UEFI 2.70 (Lenovo 0.4720)
  Secure Boot: disabled (disabled)
 TPM2 Support: yes
 Boot into FW: supported

Current Boot Loader:
      Product: systemd-boot 251.7
...

In the bootctl output, the firmware needs to be UEFI and the current boot loader needs to be systemd-boot. If this is the case, you are all set to continue.

Getting Started

To setup Secure Boot on your machine, first prepare your system and then enable Secure Boot.

Prepare Your System

This guide walks you through setting up your own Secure Boot keys and configuring your machine to automatically sign your binaries with Lanzaboote.

Generate Keys

To create Secure Boot keys, we use sbctl, a great tool that makes the handling of Secure Boot keys easy and secure. sbctl is available in Nixpkgs as pkgs.sbctl.

Once you have installed sbctl (or entered a Nix shell) enter:

$ sudo sbctl create-keys
[sudo] password for julian:
Created Owner UUID 8ec4b2c3-dc7f-4362-b9a3-0cc17e5a34cd
Creating secure boot keys...✓
Secure boot keys created!

This takes a couple of seconds. When it is done, your Secure Boot keys are located in /var/lib/sbctl. sbctl sets the permissions of the secret key so that only root can read it.

[!TIP] If you have preexisting keys in /etc/secureboot you can migrate these to /var/lib/sbctl.

sbctl setup --migrate

Configure NixOS (with lon)

Add lanzaboote as a dependency via lon tracking a stable release tag (https://github.com/nix-community/lanzaboote/releases).

$ lon add github nix-community/lanzaboote -r v0.4.3 --frozen
Adding lanzaboote...
Locked revision: v0.4.3
Locked hash: sha256-If6vQ+KvtKs3ARBO9G3l+4wFSCYtRBrwX1z+I+B61wQ=

Add this fragment to your configuration.nix:

# file: configuration.nix
{ pkgs, lib, ... }:
let
  sources = import ./lon.nix;
  lanzaboote = import sources.lanzaboote;
in
{
  imports = [ lanzaboote.nixosModules.lanzaboote ];

  environment.systemPackages = [
    # For debugging and troubleshooting Secure Boot.
    pkgs.sbctl
  ];

  # Lanzaboote currently replaces the systemd-boot module.
  # This setting is usually set to true in configuration.nix
  # generated at installation time. So we force it to false
  # for now.
  boot.loader.systemd-boot.enable = lib.mkForce false;

  boot.lanzaboote = {
    enable = true;
    pkiBundle = "/var/lib/sbctl";
  };
}

If you're using Lanzaboote from main, you need to call it via

lanzaboote = import sources.lanzaboote { inherit pkgs; };

Configure NixOS (with Flakes)

Add this fragment to your flake.nix:

{
  description = "A SecureBoot-enabled NixOS configurations";

  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";

    lanzaboote = {
      url = "github:nix-community/lanzaboote/v0.4.3";

      # Optional but recommended to limit the size of your system closure.
      inputs.nixpkgs.follows = "nixpkgs";
    };
  };

  outputs = { self, nixpkgs, lanzaboote, ...}: {
    nixosConfigurations = {
      yourHost = nixpkgs.lib.nixosSystem {
        system = "x86_64-linux";

        modules = [
          # This is not a complete NixOS configuration and you need to reference
          # your normal configuration here.

          lanzaboote.nixosModules.lanzaboote

          ({ pkgs, lib, ... }: {

            environment.systemPackages = [
              # For debugging and troubleshooting Secure Boot.
              pkgs.sbctl
            ];

            # Lanzaboote currently replaces the systemd-boot module.
            # This setting is usually set to true in configuration.nix
            # generated at installation time. So we force it to false
            # for now.
            boot.loader.systemd-boot.enable = lib.mkForce false;

            boot.lanzaboote = {
              enable = true;
              pkiBundle = "/var/lib/sbctl";
            };
          })
        ];
      };
    };
  };
}

Now, rebuild your system with nixos-rebuild switch.

Verify Your Machine is Ready

After you rebuild your system, check sbctl verify output:

$ sudo sbctl verify
Verifying file database and EFI images in /boot...
✓ /boot/EFI/BOOT/BOOTX64.EFI is signed
✓ /boot/EFI/Linux/nixos-generation-355.efi is signed
✓ /boot/EFI/Linux/nixos-generation-356.efi is signed
✗ /boot/EFI/nixos/0n01vj3mq06pc31i2yhxndvhv4kwl2vp-linux-6.1.3-bzImage.efi is not signed
✓ /boot/EFI/systemd/systemd-bootx64.efi is signed

It is expected that the files starting with kernel- are not signed.

Now, you need to enable Secure Boot so that your firmware enforces signature verification.

Enable Secure Boot

Now that NixOS is ready for Secure Boot, we will setup the firmware. At the end of this section, Secure Boot will be enabled on your system and your firmware will only boot binaries that are signed with your keys.

At least on some ASUS boards and others, you may also need to set the OS Type to "Windows UEFI Mode" in the Secure Boot settings, so that Secure Boot does get enabled.

These instructions are specific to ThinkPads and may need to be adapted on other systems.

Enter Secure Boot Setup Mode

The UEFI firmware allows enrolling Secure Boot keys when it is in Setup Mode.

On a Thinkpad enter the BIOS menu using the "Reboot into Firmware" entry in the systemd-boot boot menu. Once you are in the BIOS menu:

  1. Select the "Security" tab.
  2. Select the "Secure Boot" entry.
  3. Set "Secure Boot" to enabled.
  4. Select "Reset to Setup Mode".

When you are done, press F10 to save and exit.

You can see these steps as a video here.

⚠️ Do not select "Clear All Secure Boot Keys" as it will drop the Forbidden Signature Database (dbx).

Framework Devices: Enter Setup Mode

On Framework laptops (13th generation or newer) you can enter the setup mode like this:

  1. Select "Administer Secure Boot"
  2. Select "Erase all Secure Boot Settings"

[!WARNING] Don't select "Erase all Secure Boot Settings" in the Framework 13 Core Ultra Series 1 firmware. This firmware is bugged, instead delete all keys from the "PK", "KEK" and "DB" sections manually. See this thread on the Framework forum.

When you are done, press F10 to save and exit.

Microsoft Surface Devices: Disable Secure Boot

On Microsoft Surface devices (tested on Surface Book 3 and Surface Go 3), keep Secure Boot disabled in UEFI settings. On Surface Devices, having Secure Boot disabled defaults to "setup mode", and there is no need to re-enable it in this interface. After following these instructions, Lanzaboote should enable Secure Boot for you.

Other Systems

On certain systems (e.g. ASUS desktop motherboards), there is no explicit option to enter Setup Mode. Instead, choose the option to erase the existing Platform Key.

Enroll Keys

Once you've booted your system into NixOS again, you have to enroll your keys to activate Secure Boot. We include Microsoft keys here to avoid boot issues.

$ sudo sbctl enroll-keys --microsoft
Enrolling keys to EFI variables...
With vendor keys from microsoft...✓
Enrolled keys to the EFI variables!

⚠️ During boot, some hardware might include OptionROMs signed with Microsoft keys. By using the --microsoft, we enroll the Microsoft OEM certificates. Another more experimental option would be to enroll OptionROMs checksum seen at last boot using --tpm-eventlog, but these checksums might change later.

You can now reboot your system. After you've booted, Secure Boot is activated and in user mode:

$ bootctl status
System:
      Firmware: UEFI 2.70 (Lenovo 0.4720)
 Firmware Arch: x64
   Secure Boot: enabled (user)
  TPM2 Support: yes
  Boot into FW: supported

⚠️ If you used --microsoft while enrolling the keys, you might want to check that the Secure Boot Forbidden Signature Database (dbx) is not empty. A quick and dirty way is by checking the file size of /sys/firmware/efi/efivars/dbx-*. Keeping an up to date dbx reduces Secure Boot bypasses, see for example: https://uefi.org/sites/default/files/resources/dbx_release_info.pdf.

Framework Devices: Enable Secure Boot

On Framework laptops you may need to manually enable Secure Boot:

  1. Select "Administer Secure Boot"
  2. Enable "Enforce Secure Boot"

When you are done, press F10 to save and exit.

That's all! 🥳

Disable Secure Boot

When you want to permanently get back to a system without the Secure Boot stack, first disable Secure Boot in your firmware settings. Then you can disable the Lanzaboote related settings in the NixOS configuration and rebuild.

You may need to clean up the EFI/Linux directory in the ESP manually to get rid of stale boot entries. Please backup your ESP, before you delete any files in case something goes wrong.

Security Requirements

To provide any security your system needs to defend against an attacker turning UEFI Secure Boot off or being able to sign binaries with the keys we are going to generate.

The easiest way to achieve this is to:

  1. Enable a BIOS password in your system.
  2. Use full disk encryption.

The topic of security around Secure Boot is complex. We are only scratching the surface here and a comprehensive guide is out of scope.

Windows Dual-Boot and BitLocker

For Windows dual-booters and BitLocker users, it is highly recommended that you export your BitLocker recovery keys and confirm that they are correct. Please refer to this Microsoft support article for help. This will be required once you finish this guide to confirm with BitLocker that the PCRs changed during the next measurement are intended and allows the TPM unlocking of Windows to work as normal.

Troubleshooting

Bootloader installation fails with "Failed to install files. … No space left on device (os error 28)"

During the bootloader installation process, Lanzaboote must copy the kernel and initrd to the EFI system partition (ESP). It is quite possible that the ESP is not large enough to hold these files for all installed generations, in which case this error occurs.

In this case, you must first delete some generations (e.g. run nix-collect-garbage --delete-older-than 7d to delete all generations more than one week old). After that, some space on the ESP must be freed manually. To achieve this, delete some kernels and initrds in /boot/EFI/nixos (they will be recreated in the next step if they are in fact still required). Finally, run nixos-rebuild boot again to finish the installation process that was interrupted by the error.

It is recommended run a garbage collection regularly, and monitor the ESP usage (particularly if it is quite small), to prevent this issue from happening again in the future.

Warning: It is recommended to not delete the currently booted kernel and initrd, and to not reboot the system before running nixos-rebuild boot again, to minimize the risk of accidentally rendering the system unbootable.

Note: When upgrading Lanzaboote from version 0.3.0, or from git master prior to the merge of PR #204, ESP space usage is temporarily doubled. Hence it is possible for this error to occur even if there was plenty (but less than half) free space available prior to the installation. In this case, it is not necessary to delete any generations, and you can proceed directly to deleting some kernels and initrds before running the installation again.

Power failed during bootloader installation, and now the system does not boot any more

Due to the shortcomings of the FAT32 filesystem, in rare cases, it is possible for the ESP to become corrupted after power loss. With Lanzaboote enabled, this will lead to "secure boot errors" or "hash verification failures" (the exact wording depends on the firmware). In these cases, recovery is usually still possible with the steps below.

Note: If the system fails to boot after the Linux kernel has already been started, then the problem is not caused by a corrupted ESP. In this case, the steps below will not help, and standard rollback procedures should be followed instead.

The system can still boot an older generation

In case an older generation still works, the recovery can be carried out from within the booted system. Run nix-shell -p openssl sbctl to ensure the tools required for recovery are available.

  1. Run sudo sbctl verify /boot/EFI/Linux/nixos-generation-*.efi to check the Lanzaboote stubs. Files that have a crossmark on their left are corrupted and must be deleted.
  2. Run for file in /boot/EFI/nixos/*.efi; do hash=$(openssl dgst -sha256 -binary "$file" | base32 | tr -d = | LC_ALL=C tr [:upper:] [:lower:]); if [[ $file != *$hash.efi ]]; then echo $file; fi; done to check the kernels and initrds. Any files that are printed are corrupted and must be deleted.
  3. Run nixos-rebuild boot. This should reinstate all files that are required for the newer generations to boot.
  4. Reboot the system, it should now work again.

The system cannot boot any generation anymore

If no available generation can boot any more, the system must be recovered from a rescue system. First make sure that you have a recent NixOS install medium available.

Note: Nix versions from before August 2023 contain a bug that can prevent nixos-enter from working. A more recent medium must be used for the recovery procedure to work reliably.

  1. Disable Secure Boot in the firmware settings. The NixOS install medium is not signed and thus cannot be booted when Secure Boot is active.
  2. Boot the NixOS install medium.
  3. Mount all partitions belonging to the system to be recovered under /mnt, just like you would for installation.
    1. In case the ESP does not mount, or only mounts in read-only mode, due to corruption, try fsck.fat first. If that fails as well or the ESP still does not mount, it needs to be reformatted using mkfs.fat.
  4. Delete the corrupted files on the ESP, using rm -fr /mnt/boot/EFI/Linux/nixos-generation-*.efi /mnt/boot/efi/nixos.
  5. Enter the recovery shell by running nixos-enter. Then, run nixos-rebuild boot to install the bootloader again.
  6. Exit the recovery shell and unmount all filesystems.
  7. Reboot the system to verify that everything works again.
  8. Enable Secure Boot again in the firmware settings.

The system doesn't boot with Secure Boot enabled

It is the most likely issue that Lanzaboote could not verify a cryptographic hash. To recover from this, disable Secure Boot in your firmware settings. Please file a bug, if you hit this issue.