This is part 3 in my series of posts learning how to setup NixOS on my Framework 16" AMD laptop. It is not meant as a guide, necessarily, but more as a captain’s log of my thoughts and processes along the way.

Secure Boot with Lanzaboote

Before I get too far into using my laptop, I want to figure out some of the more advanced features I want to use. If I don’t figure them out now, it’ll be a huge pain later if I brick it once I have actual important data on my device.

So first up on my list is Lanzaboote, what appears to be the most-recommended way to enable secure boot on NixOS. They have some setup instructions that I can follow with my flake.nix configuration. But while reading these instructions, I noticed that sbctl places the secureboot keys in /etc/secureboot.

I don’t love this, since it’s putting the secure boot signing keys on the device that relies on the signatures to be valid. What I’d like is for my signing keys to be hardware backed, either in the on-board TPM or in a yubikey. But sbctl doesn’t currently support this and I am new to doing this type of thing, so I’m going to just follow the instructions for now instead of venturing out into manual key generation. But when I am ready to try to do hardware backed key generation, Arch Wiki has me covered.

First I generated some keys with a simple nix-shell -p sbctl and then ran sudo sbctl create-keys from within the nix-shell. Next, I added lanzaboote as an input to my flake.nix file.

  inputs = {
    ...
    lanzaboote = {
	  url = "github:nix-community/lanzaboote/v0.4.1";
	  inputs.nixpkgs.follow = "nixpkgs";
    };
  };

Next, I added lanzaboote as a parameter to the outputs and added the module to my laptop’s configuration.

  outputs = {self, nixpkgs, lanzaboote, ...}: {
    nixosConfigurations.serenity = nixpkgs.lib.nixosSystem {
      specialArgs = {inherit inputs;};
      modules = [
        lanzaboote.nixosModules.lanzaboote
        ./hosts/serenity/configuration.nix
        inputs.home-manager.nixosModules.default
	  ];
    };

Now I am ready to modify the ./hosts/serenity/configuration.nix file to include the necessary configuration to tell nix to use lanzaboote instead of systemd-boot.

{ config, pkgs, lib, inputs, ...};
{
  boot.loader.systemd-boot.enable = lib.mkForce false;
  boot.lanzaboote = {
    enable = true;
    pkiBundle = "/etc/secureboot";
  };
  ...
  environment.systemPackages = [
  ...
  sbctl # Secure boot key management
  ]
}

After running a rebuild (sudo nixos-rebuild switch --flake ~/nixcfg), I am ready to verify signatures with sudo sbctl verify. Everything except my kernel images are signed. The lanzaboote guide says that files ending in .bzImage.efi are not signed. My kernel images don’t end in .bzImage.efi, but I’m pretty sure they are fine.

Before I go any further, I’m going to backup my existing EFI variables, which I found instructions for in the Arch wiki.

for var in PK KEK db dbx ; do efi-readvar -v $var -o old_${var}.esl ; done

Time to get back to the Lanzaboote instructions and reboot my computer to get into secure boot setup mode. shutdown now and then spam the f12 key while rebooting (it has the framework gear on it). This seems to go directly to the boot manager, and getting to the settings from there doesn’t really work correctly, from my experience. But I just went into the nixos boot menu and scrolled to the bottom where it said “Reboot into firmware interface”.

From the framework UEFI interface, we want to go to Administer Secure Boot, then “Erase all Secure Boot Settings.” Toggle this to Enable. Hit f10 to save and exit.

Let NixOS boot up again and login. Open up a terminal and enroll our keys - sudo sbctl enroll-keys --microsoft.

We had to disable secure boot in the UEFI configuration on day one, then we cleared the settings and enrolled our keys. Now we need to enable it again. Reboot into the framework firmware interface again, Administer Secure Boot, and toggle Enforce Secure Boot to enabled. F10 to save and exit, then let NixOS boot up.

Login and open up a terminal, run bootctl status and you should see something like this:

System:
      Firmware: UEFI 2.80 (INSYDE Corp. 0.770)
 Firmware Arch: x64
   Secure Boot: enabled (user)
  TPM2 Support: yes
  Measured UKI: yes
  Boot into FW: supported

Now we’ve got secureboot working. Time to see if we can avoid breaking it when we enable disk encryption. But that will be a problem for my next post.