config/modules/base/boot/default.nix

187 lines
4.9 KiB
Nix

{
config,
lib,
pkgs,
...
}:
let
cfg = config.zfs-root.boot;
inherit (lib)
mkIf
types
mkDefault
mkOption
mkMerge
strings
;
inherit (builtins)
head
toString
map
tail
;
in
{
options.zfs-root.boot = {
enable = mkOption {
description = "Enable root on ZFS support";
type = types.bool;
default = false; # TODO: change by @motiejus
};
devNodes = mkOption {
description = "Specify where to discover ZFS pools";
type = types.str;
apply =
x:
assert (strings.hasSuffix "/" x || abort "devNodes '${x}' must have trailing slash!");
x;
default = "/dev/disk/by-id/";
};
bootDevices = mkOption {
description = "Specify boot devices";
type = types.nonEmptyListOf types.str;
};
availableKernelModules = mkOption {
type = types.nonEmptyListOf types.str;
default = [
"uas"
"nvme"
"ahci"
];
};
kernelParams = mkOption {
type = types.listOf types.str;
default = [ ];
};
immutable = mkOption {
description = "Enable root on ZFS immutable root support";
type = types.bool;
default = false;
};
removableEfi = mkOption {
description = "install bootloader to fallback location";
type = types.bool;
default = true;
};
partitionScheme = mkOption {
default = {
biosBoot = "-part5";
efiBoot = "-part1";
swap = "-part4";
bootPool = "-part2";
rootPool = "-part3";
};
description = "Describe on disk partitions";
type = types.attrsOf types.str;
};
sshUnlock = {
enable = mkOption {
type = types.bool;
default = false;
};
authorizedKeys = mkOption {
type = types.listOf types.str;
default = [ ];
};
};
};
config = mkIf (cfg.enable) (mkMerge [
{
zfs-root.fileSystems.datasets = {
"rpool/nixos/home" = mkDefault "/home";
"rpool/nixos/var/lib" = mkDefault "/var/lib";
"rpool/nixos/var/log" = mkDefault "/var/log";
"rpool/nixos/nix" = mkDefault "/nix";
"bpool/nixos/root" = "/boot";
};
}
(mkIf (!cfg.immutable) {
zfs-root.fileSystems.datasets = {
"rpool/nixos/root" = "/";
};
})
(mkIf cfg.immutable {
zfs-root.fileSystems = {
datasets = {
"rpool/nixos/empty" = "/";
"rpool/nixos/root" = "/oldroot";
};
bindmounts = {
"/oldroot/nix" = "/nix";
"/oldroot/etc/nixos" = "/etc/nixos";
};
};
boot.initrd.postDeviceCommands = ''
if ! grep -q zfs_no_rollback /proc/cmdline; then
zpool import -N rpool
zfs rollback -r rpool/nixos/empty@start
zpool export -a
fi
'';
})
{
zfs-root.fileSystems = {
efiSystemPartitions = map (diskName: diskName + cfg.partitionScheme.efiBoot) cfg.bootDevices;
swapPartitions =
if cfg.partitionScheme ? swap then
map (diskName: diskName + cfg.partitionScheme.swap) cfg.bootDevices
else
[ ];
};
boot = {
initrd.availableKernelModules = cfg.availableKernelModules;
kernelParams = cfg.kernelParams;
supportedFilesystems = [ "zfs" ];
zfs = {
devNodes = cfg.devNodes;
forceImportRoot = mkDefault false;
};
loader = {
efi = {
canTouchEfiVariables = if cfg.removableEfi then false else true;
efiSysMountPoint = "/boot/efis/" + (head cfg.bootDevices) + cfg.partitionScheme.efiBoot;
};
generationsDir.copyKernels = true;
grub = {
enable = true;
devices = map (diskName: cfg.devNodes + diskName) cfg.bootDevices;
efiInstallAsRemovable = cfg.removableEfi;
copyKernels = true;
efiSupport = true;
zfsSupport = true;
extraInstallCommands = toString (
map (diskName: ''
set -x
${pkgs.coreutils-full}/bin/cp -r ${config.boot.loader.efi.efiSysMountPoint}/EFI /boot/efis/${diskName}${cfg.partitionScheme.efiBoot}
set +x
'') (tail cfg.bootDevices)
);
};
};
};
}
(mkIf cfg.sshUnlock.enable {
boot.initrd = {
network = {
enable = true;
ssh = {
enable = true;
hostKeys = [
"/var/lib/ssh_unlock_zfs_ed25519_key"
"/var/lib/ssh_unlock_zfs_rsa_key"
];
authorizedKeys = cfg.sshUnlock.authorizedKeys;
};
postCommands = ''
tee -a /root/.profile >/dev/null <<EOF
if zfs load-key rpool/nixos; then
pkill zfs
fi
exit
EOF'';
};
};
})
]);
}