在三周之前,bootspec 作为一个实验性 RFC 合并到了 nixpkgs 中 。这宣告 NixOS 的启动器进入了一个新的时代——无需黑魔法,即可配置安全启动了!

这个 RFC 还处于早期测试阶段。尽管已经并入官方主线,你依然需要一个 额外设置一个 flag 来声明使用它:

boot.bootspec.enable = true

如果开启成功,在你下次 nixos-rebuild 时,控制台会打出这么一串警告:

trace: warning: RFC-0125 is not merged yet, this is a feature preview of bootspec.
        The schema is not definitive and features are not guaranteed to be stable until RFC-0125 is merged.
        See:
        - https://github.com/NixOS/nixpkgs/pull/172237 to track merge status in nixpkgs.
        - https://github.com/NixOS/rfcs/pull/125 to track RFC status.

不必担心,这是正常的例行警告。

配置安全启动

事实上,bootspec 附带一个 secureboot 的实现。不过,我在实际测试时,始终无法正常签发启动文件,遂放弃并寻找替代品。

之后,我发现了一个近乎完美的替代品: lanzaboote 。这是一个 诞生不久 的新生项目,旨在为 NixOS 提供安全启动支持。

以下配置步骤假定你使用 flake。

开启 Setup Mode

进入你的 BIOS 设置页面,将安全启动模式从 User Mode 改为 Setup Mode。保存配置并重启到 NixOS。

引入 lanzaboote

首先,你需要在 inputs 中引入 lanzaboote

...
lanzaboote.url = "github:nix-community/lanzaboote";
lanzaboote.inputs.nixpkgs.follows = "nixos";
...

并在 outputs 中的 modules 里引用它:

modules = [
...
lanzaboote.nixosModules.lanzaboote
...
]

生成安全密钥

引入完成后,你还需要创建安全密钥。输入以下指令即可生成一套:

sbctl create-keys

安全密钥默认生成在 /etc/secureboot 里。

注册安全密钥

输入以下指令,将刚生成的安全密钥注册到固件内:

sbctl enroll-keys --microsoft

上述指令假定需要引入 Windows 的安全启动证书。如需 NixOS + Windows 双启动,这是必要的。反之:

sbctl enroll-keys --yes-this-might-brick-my-machine

即可只注册自己生成的证书和安全密钥。

引入配置文件

你不需要手动添加 bootspec 的实验标志。lanzaboote 会自动开启它。

  boot.lanzaboote = {
    enable = true;
    publicKeyFile = "/etc/secureboot/keys/db/db.pem"; # DB public key
    privateKeyFile = "/etc/secureboot/keys/db/db.key"; # DB private key
  };

生成统一启动镜像

重新构建 NixOS 系统。如果一切顺利,你将在 EFI/Linux 下找到签名好的系统镜像。下次启动时,选择它即可安全启动 NixOS。

警告:请务必使用 sbctl verify 验证你的启动文件签名情况。以下几个文件必须拥有正确的签名,才能让 NixOS 安全启动:

  • EFI/Boot/BOOTX64.EFI
  • EFI/Linux/nixos-generation-xx.efi
  • EFI/systemd/systemd-bootx64.efi

在特定情况下,lanzaboote 不会对上面的某些文件进行签名!这会导致你的 NixOS 无法安全启动! 这是一个 已知问题 。你可以在重建 NixOS 系统前删除以上文件,来规避这一问题。

使用 TPM2

如果你在使用 LUKS 加密分区,或许会对如何使用 TPM 自动解锁某些分区感兴趣。

使用 systemd 作为引导加载程序

事实上,你也可以将 clevis 插入到 preLVM 配置中,以实现 shell script 下自动解锁特定分区的效果。不过,出于性能考虑,本文采用基于 systemd 的方案

你需要将以下配置添加到配置文件中:

boot.initrd.systemd.enable = true;

以启动基于 systemd 的引导程序。

引入内核模块

添加类似于下面的配置:

boot.initrd.kernelModules = [ "tpm" "tpm_tis" "tpm_crb"]

请依据自己的实际情况,按需引入 TPM 内核模块。

向 TPM 注册密钥

以下内容主要来自于 ArchLinux Wiki

在终端里执行以下命令:

systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=0+7 --tpm2-with-pin=true /dev/sdX

其中,--tpm2-with-pin 要求在 TPM 自动解锁前输入一串密码,而不是直接解锁。这可以有限的提升安全性。如果你不需要,请移除它。

注意:该密码虽然叫做 PIN,但其实可以包含字母等不属于数字的内容

大功告成!构建并重启你的 NixOS,即可享受安全启动的安全性,以及自动解锁的便利性。