diff --git a/docs/02_IMPERMANENCE.md b/docs/02_IMPERMANENCE.md index 941eb93..57fc0cd 100644 --- a/docs/02_IMPERMANENCE.md +++ b/docs/02_IMPERMANENCE.md @@ -225,7 +225,7 @@ reboot You should now be back in your system, with the root subvolume wiped and auto-reconstructed by NixOS. -You can now move on to the next file: [MY_FLAKE](./99_MY_FLAKE.md). +You can now move on to the next file: [SECURE_BOOT](./03_SECURE_BOOT.md). ## Why? diff --git a/docs/03_SECURE_BOOT.md b/docs/03_SECURE_BOOT.md new file mode 100644 index 0000000..69ca0e0 --- /dev/null +++ b/docs/03_SECURE_BOOT.md @@ -0,0 +1,169 @@ +# Secure Boot + +This guide will show you how to set up UEFI Secure Boot with NixOS. Once finished, you will be left with a system that +doesn't allow booting any untrusted EFI images (other operating systems, fraudulently modified kernels) on your machine. + +This guide assumes you're following from the [INSTALLATION](./01_INSTALLATION.md) guide, and that +you have converted your config to a flake. + +## Make sure you're using UEFI + +As a first step, you will want to confirm that you really are on a UEFI system. If you're using any recent hardware, +this is very likely the case. Nevertheless, let's check and make sure: + +```shell +bootctl info +``` + +Make sure the `Firmware` is reported as `UEFI`. + +## Security requirements + +The requirements are optional, as it is possible to set up secure boot without them. That said, they are heavily +encouraged, as without these, setting up secure boot will not be a very effective security measure. + +First requirement is to set up a **BIOS Password**. This is a password that you will be asked for every time you wish to +enter the BIOS (UEFI). This is necessary, as without it, an attacker could very easily just go to the BIOS and disable +Secure Boot. + +The second requirement is having **disk encryption**, at least for the root (or persist, if using impermanence) +partition. This is important, because the UEFI signing keys will be stored here, and you don't want someone to +potentially be able to get access to them, as it would allow them to sign any malicious images, making them pass secure +boot. + +> [!NOTE] +> Even after following all of these, you should be aware that Secure Boot isn't an unbreakable solution. In +> fact, if someone is able to get a hold of your machine, they can simply pull out the CMOS battery, which usually +> resets the UEFI. That means turning off Secure Boot, and getting rid of the BIOS password. +> +> While UEFI is generally a good extra measure to have, it is by no means a reliable way to really prevent others from +> ever being able to boot untrusted systems, unless you use a specialized motherboard, which persists the UEFI state. + +## Create your keys + +To create secure boot keys, you can use `sbctl`, which is a very popular Secure Boot Key Manager. It is available in +nixpkgs as `pkgs.sbctl`. Make sure to add it to your configuration. + +> [!IMPORTANT] +> If you have set up impermanence, you will first need to declare `/etc/secureboot` as a persistent directory. This +> directory will contain the secure boot keys necessary to sign the bootloader and your kernel/initrd images. If you +> lose this key, you will need to go through this process again. + +Once you have sbctl installed, run the following command to create your new keys: + +```shell +sudo sbctl create-keys +``` + +This takes a couple of seconds. When it is done, your Secure Boot keys are located in `/etc/secureboot`. sbctl sets the +permissions of the secret key so that only root can read it. + +## lanzaboote + +`lanzaboote` is a tool to help you set up secure boot in NixOS. To install it, you can add +it as an input for your flake: + +```nix +lanzaboote = { + url = "github:nix-community/lanzaboote/v0.3.0"; + + # Optional but recommended to limit the size of your system closure. + inputs.nixpkgs.follows = "nixpkgs"; + }; +``` + +And include the `lanzaboote.nixosModules.lanzaboote` module. + +Now, you will want to add the following to your configuration: + +```nix +# Lanzaboote replaces systemd-boot +boot.loader.systemd-boot.enable = lib.mkForce false; + +boot.lanzaboote = { + enable = true; + pkiBundle = "/etc/secureboot"; +}; +``` + +You can now rebuild your system: `sudo nixos-rebuild switch --flake .`. + +## Check that your machine is ready for secure-boot environment + +After you rebuild your system, check `sudo sbctl verify` output. You should see it report all of your boot entries as +signed. + +Note that files ending with `bzImage.efi` do not need to be signed. + +### Manually sign an image + +In case you'd ever need to do so, it is possible to sign an image manually with `sbctl`. To do so, you can run: + +```shell +sbctl sign -s /boot/my_image.efi +``` + +The `-s` flag means save. This makes sure the files will be automatically re-signed when we update. (List of files to +re-sign will be stored in the secure boot database, in `/etc/secureboot`) + +Note that you shouldn't really need to use this. + +## Enabling secure boot + +Now that NixOS is ready for secure-boot, we will set up 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. + +### Enter Setup mode + +To allow us to upload new signing keys into the UEFI firmware, we will need to enter "setup mode". This should be +possible by going to the Secure Boot category in your UEFI settings, and clicking on Delete/Clear certificates, or +there could even be a "Setup Mode" option directly. + +This option may not be available without also enabling secure boot. This is fine, while in setup mode, the secure +boot checks will not be performed. + +You can now save the settings and reboot. + +Once booted, to check that you really are in Setup Mode, run: + +```shell +sudo sbctl status +``` + +### Enroll the new keys + +You will now have to enroll your new keys to activate Secure Boot. + +```shell +sudo sbctl enroll-keys -m +``` + +> [!NOTE] +> The `-m` option (also known as `--microsoft`) will make sure to also include the Microsoft +> signing keys. This is required by most motherboards, not using it could brick your device. + +This should automatically enable secure boot in user mode for you. You can now reboot the system. + +### Make sure it worked + +To check that you really are using secure-boot, you can run + +```shell +bootctl status +``` + +It should report `Secure Boot: enabled (user)`. + +## Why bother? + +As I mentioned, secure boot can be bypassed if someone tries hard enough (pulling the CMOS battery). That then +brings to question whether it's even worth it to set it up, when it doesn't really give you that much. On its own, + +I probably wouldn't bother with setting up secure-boot, however secure boot allows me to set up TPM (Trusted Platform +Module) to automatically release the decryption keys for my LUKS encrypted root partition, in a secure way. This means I +won't have to type my disk password every time I boot. + +For more information on this, check out the follow-up guide: [TPM_UNLOCKING](./04_TPM_UNLOCKING.md). + +## Sources / Attribution + +- diff --git a/docs/04_TPM_UNLOCKING.md b/docs/04_TPM_UNLOCKING.md new file mode 100644 index 0000000..c72d6c1 --- /dev/null +++ b/docs/04_TPM_UNLOCKING.md @@ -0,0 +1,3 @@ +# TPM Unlocking + +WIP diff --git a/docs/99_MY_FLAKE.md b/docs/99_MY_FLAKE.md index 4d6770c..0ccd179 100644 --- a/docs/99_MY_FLAKE.md +++ b/docs/99_MY_FLAKE.md @@ -133,6 +133,10 @@ The resulting file should then look something like this: # devices."/dev/disk/by-label/NIXROOT".subvolumes = [ "root" ]; # }; #}; + + # Enable secure boot (requires running some commands afterwards, see the + # option's docs) + #secure-boot.enabled = true; }; device = { virtual-machine = false; diff --git a/flake.lock b/flake.lock index 2b976bb..c4de84b 100644 --- a/flake.lock +++ b/flake.lock @@ -1,5 +1,115 @@ { "nodes": { + "crane": { + "inputs": { + "flake-compat": [ + "lanzaboote", + "flake-compat" + ], + "flake-utils": [ + "lanzaboote", + "flake-utils" + ], + "nixpkgs": [ + "lanzaboote", + "nixpkgs" + ], + "rust-overlay": [ + "lanzaboote", + "rust-overlay" + ] + }, + "locked": { + "lastModified": 1681177078, + "narHash": "sha256-ZNIjBDou2GOabcpctiQykEQVkI8BDwk7TyvlWlI4myE=", + "owner": "ipetkov", + "repo": "crane", + "rev": "0c9f468ff00576577d83f5019a66c557ede5acf6", + "type": "github" + }, + "original": { + "owner": "ipetkov", + "repo": "crane", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1673956053, + "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": [ + "lanzaboote", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1680392223, + "narHash": "sha256-n3g7QFr85lDODKt250rkZj2IFS3i4/8HBU2yKHO3tqw=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "dcc36e45d054d7bb554c9cdab69093debd91a0b5", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1681202837, + "narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "cfacdce06f30d2b68473a46042957675eebb3401", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "lanzaboote", + "pre-commit-hooks-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1660459072, + "narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "a20de23b925fd8264fd7fad6454652e142fd7f73", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, "home-manager": { "inputs": { "nixpkgs": [ @@ -35,6 +145,33 @@ "type": "github" } }, + "lanzaboote": { + "inputs": { + "crane": "crane", + "flake-compat": "flake-compat", + "flake-parts": "flake-parts", + "flake-utils": "flake-utils", + "nixpkgs": [ + "nixpkgs" + ], + "pre-commit-hooks-nix": "pre-commit-hooks-nix", + "rust-overlay": "rust-overlay" + }, + "locked": { + "lastModified": 1682802423, + "narHash": "sha256-Fb5TeRTdvUlo/5Yi2d+FC8a6KoRLk2h1VE0/peMhWPs=", + "owner": "nix-community", + "repo": "lanzaboote", + "rev": "64b903ca87d18cef2752c19c098af275c6e51d63", + "type": "github" + }, + "original": { + "owner": "nix-community", + "ref": "v0.3.0", + "repo": "lanzaboote", + "type": "github" + } + }, "nixpkgs": { "locked": { "lastModified": 1712439257, @@ -51,12 +188,100 @@ "type": "github" } }, + "nixpkgs-stable": { + "locked": { + "lastModified": 1678872516, + "narHash": "sha256-/E1YwtMtFAu2KUQKV/1+KFuReYPANM2Rzehk84VxVoc=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "9b8e5abb18324c7fe9f07cb100c3cd4a29cda8b8", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-22.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "pre-commit-hooks-nix": { + "inputs": { + "flake-compat": [ + "lanzaboote", + "flake-compat" + ], + "flake-utils": [ + "lanzaboote", + "flake-utils" + ], + "gitignore": "gitignore", + "nixpkgs": [ + "lanzaboote", + "nixpkgs" + ], + "nixpkgs-stable": "nixpkgs-stable" + }, + "locked": { + "lastModified": 1681413034, + "narHash": "sha256-/t7OjNQcNkeWeSq/CFLYVBfm+IEnkjoSm9iKvArnUUI=", + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "d3de8f69ca88fb6f8b09e5b598be5ac98d28ede5", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, "root": { "inputs": { "home-manager": "home-manager", "impermanence": "impermanence", + "lanzaboote": "lanzaboote", "nixpkgs": "nixpkgs" } + }, + "rust-overlay": { + "inputs": { + "flake-utils": [ + "lanzaboote", + "flake-utils" + ], + "nixpkgs": [ + "lanzaboote", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1682129965, + "narHash": "sha256-1KRPIorEL6pLpJR04FwAqqnt4Tzcm4MqD84yhlD+XSk=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "2c417c0460b788328220120c698630947547ee83", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } } }, "root": "root", diff --git a/flake.nix b/flake.nix index 62b0673..95cd31c 100644 --- a/flake.nix +++ b/flake.nix @@ -11,6 +11,12 @@ # doesn't offer much above properly used symlinks but it is convenient impermanence.url = "github:nix-community/impermanence"; + + # secure-boot support + lanzaboote = { + url = "github:nix-community/lanzaboote/v0.3.0"; + inputs.nixpkgs.follows = "nixpkgs"; + }; }; outputs = {self, nixpkgs, ...} @ inputs: let diff --git a/hosts/default.nix b/hosts/default.nix index 3e40915..40a5834 100644 --- a/hosts/default.nix +++ b/hosts/default.nix @@ -25,6 +25,7 @@ in ./herugrim inputs.home-manager.nixosModules.home-manager inputs.impermanence.nixosModules.impermanence + inputs.lanzaboote.nixosModules.lanzaboote ] ++ shared; }; } diff --git a/hosts/herugrim/default.nix b/hosts/herugrim/default.nix index 394e4b2..770da1c 100644 --- a/hosts/herugrim/default.nix +++ b/hosts/herugrim/default.nix @@ -37,6 +37,8 @@ devices."/dev/disk/by-label/NIXROOT".subvolumes = [ "root" ]; }; }; + + secure-boot.enabled = true; }; device = { diff --git a/options/system/default.nix b/options/system/default.nix index b5eb76a..f6b7f95 100644 --- a/options/system/default.nix +++ b/options/system/default.nix @@ -4,6 +4,7 @@ in { imports = [ ./impermanence.nix + ./secure-boot.nix ]; options.myOptions.system = { diff --git a/options/system/secure-boot.nix b/options/system/secure-boot.nix new file mode 100644 index 0000000..1cd77c7 --- /dev/null +++ b/options/system/secure-boot.nix @@ -0,0 +1,23 @@ +{ lib, ... }: with lib; let + inherit (lib) mkEnableOption; +in +{ + options.myOptions.system.secure-boot = { + enabled = mkEnableOption '' + secure-boot using lanzaboote. + + Note that you will need to have UEFI firmware, and the rebuild + will report errors until you generate the secure boot keys with: + ```shell + sudo sbctl create-keys + ```` + + Optionally (though enabling this is pointless otherwise), you should + now enter secure-boot setup mode and enroll the keys: + ```shell + sudo sbctl enroll-keys -m + ``` + Then reboot, and secure-boot should be enabled. + ''; + }; +} diff --git a/system/boot/default.nix b/system/boot/default.nix index 684ea28..57a3683 100644 --- a/system/boot/default.nix +++ b/system/boot/default.nix @@ -1,5 +1,6 @@ _: { imports = [ ./systemd-boot.nix + ./secure-boot.nix ]; } diff --git a/system/boot/secure-boot.nix b/system/boot/secure-boot.nix new file mode 100644 index 0000000..baee854 --- /dev/null +++ b/system/boot/secure-boot.nix @@ -0,0 +1,23 @@ +{ config, pkgs, lib, ... }: let + inherit (lib) mkIf; + + cfg = config.myOptions.system.secure-boot; +in { + config = mkIf cfg.enabled { + # Secure Boot Key Manager + environment.systemPackages = [ pkgs.sbctl ]; + + # Persist the secure boot keys (for impermanence) + myOptions.system.impermanence.root.extraDirectories = [ + "/etc/secureboot" + ]; + + # Lanzaboote replaces systemd-boot + boot.loader.systemd-boot.enable = lib.mkForce false; + + boot.lanzaboote = { + enable = true; + pkiBundle = "/etc/secureboot"; + }; + }; +}