nixpkgs/nixos/modules/system/boot/loader/limine/limine.nix
programmerlexi 36ecfe6216 nixos/limine: add support for secure boot
Signed-off-by: John Titor <50095635+JohnRTitor@users.noreply.github.com>
2025-05-21 21:59:23 +05:30

460 lines
14 KiB
Nix

{
config,
lib,
pkgs,
...
}:
let
cfg = config.boot.loader.limine;
efi = config.boot.loader.efi;
limineInstallConfig = pkgs.writeText "limine-install.json" (
builtins.toJSON {
nixPath = config.nix.package;
efiBootMgrPath = pkgs.efibootmgr;
liminePath = cfg.package;
efiMountPoint = efi.efiSysMountPoint;
fileSystems = config.fileSystems;
luksDevices = config.boot.initrd.luks.devices;
canTouchEfiVariables = efi.canTouchEfiVariables;
efiSupport = cfg.efiSupport;
efiRemovable = cfg.efiInstallAsRemovable;
secureBoot = cfg.secureBoot;
biosSupport = cfg.biosSupport;
biosDevice = cfg.biosDevice;
partitionIndex = cfg.partitionIndex;
forceMbr = cfg.forceMbr;
enrollConfig = cfg.enrollConfig;
style = cfg.style;
maxGenerations = if cfg.maxGenerations == null then 0 else cfg.maxGenerations;
hostArchitecture = pkgs.stdenv.hostPlatform.parsed.cpu;
timeout = if config.boot.loader.timeout != null then config.boot.loader.timeout else 10;
enableEditor = cfg.enableEditor;
extraConfig = cfg.extraConfig;
extraEntries = cfg.extraEntries;
additionalFiles = cfg.additionalFiles;
validateChecksums = cfg.validateChecksums;
panicOnChecksumMismatch = cfg.panicOnChecksumMismatch;
}
);
defaultWallpaper = pkgs.nixos-artwork.wallpapers.simple-dark-gray-bootloader.gnomeFilePath;
in
{
meta.maintainers = with lib.maintainers; [
lzcunt
phip1611
programmerlexi
];
options.boot.loader.limine = {
enable = lib.mkEnableOption "the Limine Bootloader";
enableEditor = lib.mkEnableOption null // {
description = ''
Whether to allow editing the boot entries before booting them.
It is recommended to set this to false, as it allows gaining root
access by passing `init=/bin/sh` as a kernel parameter.
'';
};
maxGenerations = lib.mkOption {
default = null;
example = 50;
type = lib.types.nullOr lib.types.int;
description = ''
Maximum number of latest generations in the boot menu.
Useful to prevent boot partition of running out of disk space.
`null` means no limit i.e. all generations that were not
garbage collected yet.
'';
};
extraConfig = lib.mkOption {
default = "";
type = lib.types.lines;
example = lib.literalExpression ''
serial: yes
'';
description = ''
A string which is prepended to limine.conf. The config format can be found [here](https://github.com/limine-bootloader/limine/blob/trunk/CONFIG.md).
'';
};
extraEntries = lib.mkOption {
default = "";
type = lib.types.lines;
example = lib.literalExpression ''
/memtest86
protocol: chainload
path: boot():///efi/memtest86/memtest86.efi
'';
description = ''
A string which is appended to the end of limine.conf. The config format can be found [here](https://github.com/limine-bootloader/limine/blob/trunk/CONFIG.md).
'';
};
additionalFiles = lib.mkOption {
default = { };
type = lib.types.attrsOf lib.types.path;
example = lib.literalExpression ''
{ "efi/memtest86/memtest86.efi" = "''${pkgs.memtest86-efi}/BOOTX64.efi"; }
'';
description = ''
A set of files to be copied to {file}`/boot`. Each attribute name denotes the
destination file name in {file}`/boot`, while the corresponding attribute value
specifies the source file.
'';
};
validateChecksums = lib.mkEnableOption null // {
default = true;
description = ''
Whether to validate file checksums before booting.
'';
};
panicOnChecksumMismatch = lib.mkEnableOption null // {
description = ''
Whether or not checksum validation failure should be a fatal
error at boot time.
'';
};
package = lib.mkPackageOption pkgs "limine" { };
efiSupport = lib.mkEnableOption null // {
default = pkgs.stdenv.hostPlatform.isEfi;
defaultText = lib.literalExpression "pkgs.stdenv.hostPlatform.isEfi";
description = ''
Whether or not to install the limine EFI files.
'';
};
efiInstallAsRemovable = lib.mkEnableOption null // {
default = !efi.canTouchEfiVariables;
defaultText = lib.literalExpression "!config.boot.loader.efi.canTouchEfiVariables";
description = ''
Whether or not to install the limine EFI files as removable.
See {option}`boot.loader.grub.efiInstallAsRemovable`
'';
};
biosSupport = lib.mkEnableOption null // {
default = !cfg.efiSupport && pkgs.stdenv.hostPlatform.isx86;
defaultText = lib.literalExpression "!config.boot.loader.limine.efiSupport && pkgs.stdenv.hostPlatform.isx86";
description = ''
Whether or not to install limine for BIOS.
'';
};
biosDevice = lib.mkOption {
default = "nodev";
type = lib.types.str;
description = ''
Device to install the BIOS version of limine on.
'';
};
partitionIndex = lib.mkOption {
default = null;
type = lib.types.nullOr lib.types.int;
description = ''
The 1-based index of the dedicated partition for limine's second stage.
'';
};
enrollConfig = lib.mkEnableOption null // {
default = cfg.panicOnChecksumMismatch;
defaultText = lib.literalExpression "boot.loader.limine.panicOnChecksumMismatch";
description = ''
Whether or not to enroll the config.
Only works on EFI!
'';
};
forceMbr = lib.mkEnableOption null // {
description = ''
Force MBR detection to work even if the safety checks fail, use absolutely only if necessary!
'';
};
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 = [ ];
example = lib.literalExpression "[ pkgs.nixos-artwork.wallpapers.simple-dark-gray-bootloader.gnomeFilePath ]";
type = lib.types.listOf lib.types.path;
description = ''
A list of wallpapers.
If more than one is specified, a random one will be selected at boot.
'';
};
wallpaperStyle = lib.mkOption {
default = "streched";
type = lib.types.enum [
"centered"
"streched"
"tiled"
];
description = ''
How the wallpaper should be fit to the screen.
'';
};
backdrop = lib.mkOption {
default = null;
example = "7EBAE4";
type = lib.types.nullOr lib.types.str;
description = ''
Color to fill the rest of the screen with when wallpaper_style is centered in RRGGBB format.
'';
};
interface = {
resolution = lib.mkOption {
default = null;
type = lib.types.nullOr lib.types.str;
description = ''
The resolution of the interface.
'';
};
branding = lib.mkOption {
default = null;
type = lib.types.nullOr lib.types.str;
description = ''
The title at the top of the screen.
'';
};
brandingColor = lib.mkOption {
default = null;
type = lib.types.nullOr lib.types.int;
description = ''
Color index of the title at the top of the screen in the range of 0-7 (Limine defaults to 6 (cyan)).
'';
};
helpHidden = lib.mkEnableOption null // {
description = ''
Whether or not to hide the keybinds at the top of the screen.
'';
};
};
graphicalTerminal = {
font = {
scale = lib.mkOption {
default = null;
example = lib.literalExpression "2x2";
type = lib.types.nullOr lib.types.str;
description = ''
The scale of the font in the format <width>x<height>.
'';
};
spacing = lib.mkOption {
default = null;
type = lib.types.nullOr lib.types.int;
description = ''
The horizontal spacing between characters in pixels.
'';
};
};
palette = lib.mkOption {
default = null;
type = lib.types.nullOr lib.types.str;
description = ''
A ; seperated array of 8 colors in the format RRGGBB:
black, red, green, brown, blue, magenta, cyan, and gray.
'';
};
brightPalette = lib.mkOption {
default = null;
type = lib.types.nullOr lib.types.str;
description = ''
A ; seperated array of 8 colors in the format RRGGBB:
dark gray, bright red, bright green, yellow, bright blue, bright magenta, bright cyan, and white.
'';
};
foreground = lib.mkOption {
default = null;
type = lib.types.nullOr lib.types.str;
description = ''
Text foreground color (RRGGBB).
'';
};
background = lib.mkOption {
default = null;
type = lib.types.nullOr lib.types.str;
description = ''
Text background color (TTRRGGBB). TT is transparency.
'';
};
brightForeground = lib.mkOption {
default = null;
type = lib.types.nullOr lib.types.str;
description = ''
Text foreground bright color (RRGGBB).
'';
};
brightBackground = lib.mkOption {
default = null;
type = lib.types.nullOr lib.types.str;
description = ''
Text background bright color (RRGGBB).
'';
};
margin = lib.mkOption {
default = null;
type = lib.types.nullOr lib.types.int;
description = ''
The amount of margin around the terminal.
'';
};
marginGradient = lib.mkOption {
default = null;
type = lib.types.nullOr lib.types.int;
description = ''
The thickness in pixels for the margin around the terminal.
'';
};
};
};
};
config = lib.mkMerge [
{
boot.loader.limine.style.wallpapers = lib.mkDefault [ defaultWallpaper ];
}
(lib.mkIf (cfg.style.wallpapers == [ defaultWallpaper ]) {
boot.loader.limine.style.backdrop = lib.mkDefault "2F302F";
boot.loader.limine.style.wallpaperStyle = lib.mkDefault "streched";
})
(lib.mkIf cfg.enable {
assertions = [
{
assertion =
pkgs.stdenv.hostPlatform.isx86_64
|| pkgs.stdenv.hostPlatform.isi686
|| pkgs.stdenv.hostPlatform.isAarch64;
message = "Limine can only be installed on aarch64 & x86 platforms";
}
{
assertion = cfg.efiSupport || cfg.biosSupport;
message = "Both UEFI support and BIOS support for Limine are disabled, this will result in an unbootable system";
}
];
boot.loader.grub.enable = lib.mkDefault false;
boot.loader.supportsInitrdSecrets = true;
system = {
boot.loader.id = "limine";
build.installBootLoader = pkgs.replaceVarsWith {
src = ./limine-install.py;
isExecutable = true;
replacements = {
python3 = pkgs.python3.withPackages (python-packages: [ python-packages.psutil ]);
configPath = limineInstallConfig;
};
};
};
})
(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;
};
})
];
}