nixos/limine: add support for secure boot

Signed-off-by: John Titor <50095635+JohnRTitor@users.noreply.github.com>
This commit is contained in:
programmerlexi 2025-05-18 14:57:11 +02:00 committed by Masum Reza
parent b97b6308bd
commit 36ecfe6216
6 changed files with 150 additions and 2 deletions

View file

@ -34,4 +34,3 @@
### Additions and Improvements {#sec-nixpkgs-release-25.11-lib-additions-improvements}
- Create the first release note entry in this section!

View file

@ -4,7 +4,7 @@
<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
- Create the first release note entry in this section!
- Secure boot support can now be enabled for the Limine bootloader through {option}`boot.loader.limine.secureBoot.enable`. Bootloader install script signs the bootloader, then kernels are hashed during system rebuild and written to a config. This allows Limine to boot only the kernels installed through NixOS system.
## New Modules {#sec-release-25.11-new-modules}

View file

@ -249,6 +249,10 @@ def main():
partition formatted as FAT.
'''))
if config('secureBoot')['enable'] and not config('secureBoot')['createAndEnrollKeys'] and not os.path.exists("/var/lib/sbctl"):
print("There are no sbctl secure boot keys present. Please generate some.")
sys.exit(1)
if not os.path.exists(limine_dir):
os.makedirs(limine_dir)
else:
@ -352,6 +356,28 @@ def main():
print('error: failed to enroll limine config.', file=sys.stderr)
sys.exit(1)
if config('secureBoot')['enable']:
sbctl = os.path.join(config('secureBoot')['sbctl'], 'bin', 'sbctl')
if config('secureBoot')['createAndEnrollKeys']:
print("TEST MODE: creating and enrolling keys")
try:
subprocess.run([sbctl, 'create-keys'])
except:
print('error: failed to create keys', file=sys.stderr)
sys.exit(1)
try:
subprocess.run([sbctl, 'enroll-keys', '--yes-this-might-brick-my-machine'])
except:
print('error: failed to enroll keys', file=sys.stderr)
sys.exit(1)
print('signing limine...')
try:
subprocess.run([sbctl, 'sign', dest_path])
except:
print('error: failed to sign limine', file=sys.stderr)
sys.exit(1)
if not config('efiRemovable') and not config('canTouchEfiVariables'):
print('warning: boot.loader.efi.canTouchEfiVariables is set to false while boot.loader.limine.efiInstallAsRemovable.\n This may render the system unbootable.')

View file

@ -18,6 +18,7 @@ let
canTouchEfiVariables = efi.canTouchEfiVariables;
efiSupport = cfg.efiSupport;
efiRemovable = cfg.efiInstallAsRemovable;
secureBoot = cfg.secureBoot;
biosSupport = cfg.biosSupport;
biosDevice = cfg.biosDevice;
partitionIndex = cfg.partitionIndex;
@ -177,6 +178,41 @@ in
'';
};
secureBoot = {
enable = lib.mkEnableOption null // {
description = ''
Whether to use sign the limine binary with sbctl.
::: {.note}
This requires you to already have generated the keys and enrolled them with {command}`sbctl`.
To create keys use {command}`sbctl create-keys`.
To enroll them first reset secure boot to "Setup Mode". This is device specific.
Then enroll them using {command}`sbctl enroll-keys -m -f`.
You can now rebuild your system with this option enabled.
Afterwards turn setup mode off and enable secure boot.
:::
'';
};
createAndEnrollKeys = lib.mkEnableOption null // {
internal = true;
description = ''
Creates secure boot signing keys and enrolls them during bootloader installation.
::: {.note}
This is used for automated nixos tests.
NOT INTENDED to be used on a real system.
:::
'';
};
sbctl = lib.mkPackageOption pkgs "sbctl" { };
};
style = {
wallpapers = lib.mkOption {
default = [ ];
@ -368,5 +404,57 @@ in
};
};
})
(lib.mkIf (cfg.enable && cfg.secureBoot.enable) {
assertions = [
{
assertion = cfg.enrollConfig;
message = "Disabling enrollConfig allows bypassing secure boot.";
}
{
assertion = cfg.validateChecksums;
message = "Disabling validateChecksums allows bypassing secure boot.";
}
{
assertion = cfg.panicOnChecksumMismatch;
message = "Disabling panicOnChecksumMismatch allows bypassing secure boot.";
}
{
assertion = cfg.efiSupport;
message = "Secure boot is only supported on EFI systems.";
}
];
boot.loader.limine.enrollConfig = true;
boot.loader.limine.validateChecksums = true;
boot.loader.limine.panicOnChecksumMismatch = true;
})
# Fwupd binary needs to be signed in secure boot mode
(lib.mkIf (cfg.enable && cfg.secureBoot.enable && config.services.fwupd.enable) {
systemd.services.fwupd = {
environment.FWUPD_EFIAPPDIR = "/run/fwupd-efi";
};
systemd.services.fwupd-efi = {
description = "Sign fwupd EFI app for secure boot";
wantedBy = [ "fwupd.service" ];
partOf = [ "fwupd.service" ];
before = [ "fwupd.service" ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
RuntimeDirectory = "fwupd-efi";
};
script = ''
cp ${config.services.fwupd.package.fwupd-efi}/libexec/fwupd/efi/fwupd*.efi /run/fwupd-efi/
chmod +w /run/fwupd-efi/fwupd*.efi
${lib.getExe pkgs.sbctl} sign /run/fwupd-efi/fwupd*.efi
'';
};
services.fwupd.uefiCapsuleSettings = {
DisableShimForSecureBoot = true;
};
})
];
}

View file

@ -4,5 +4,6 @@
}:
{
checksum = runTest ./checksum.nix;
secureBoot = runTest ./secure-boot.nix;
uefi = runTest ./uefi.nix;
}

View file

@ -0,0 +1,34 @@
{ lib, ... }:
{
name = "secureBoot";
meta.maintainers = with lib.maintainers; [
programmerlexi
];
meta.platforms = [
"aarch64-linux"
"i686-linux"
"x86_64-linux"
];
nodes.machine =
{ pkgs, ... }:
{
virtualisation.useBootLoader = true;
virtualisation.useEFIBoot = true;
virtualisation.useSecureBoot = true;
virtualisation.efi.OVMF = pkgs.OVMFFull.fd;
virtualisation.efi.keepVariables = true;
boot.loader.efi.canTouchEfiVariables = true;
boot.loader.limine.enable = true;
boot.loader.limine.efiSupport = true;
boot.loader.limine.secureBoot.enable = true;
boot.loader.limine.secureBoot.createAndEnrollKeys = true;
boot.loader.timeout = 0;
};
testScript = ''
machine.start()
assert "Secure Boot: enabled (user)" in machine.succeed("bootctl status")
'';
}