flake/modules/nixos/hardware/disko.nix
2025-10-20 09:49:20 +03:00

343 lines
10 KiB
Nix

{
config,
inputs,
lib,
pkgs,
...
}:
let
cfg = config.osbmModules.hardware.disko;
inherit (config.networking) hostName;
# Default authorized keys for initrd SSH
defaultAuthorizedKeys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDF1TFwXbqdC1UyG75q3HO1n7/L3yxpeRLIq2kQ9DalI"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHYSJ9ywFRJ747tkhvYWFkx/Y9SkLqv3rb7T1UuXVBWo"
];
authorizedKeys =
if cfg.initrd-ssh.authorizedKeys != [ ] then
cfg.initrd-ssh.authorizedKeys
else
defaultAuthorizedKeys;
in
{
imports = [
inputs.disko.nixosModules.default
];
config = lib.mkMerge [
# Systemd-boot setup
(lib.mkIf (cfg.enable && cfg.systemd-boot) {
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
})
# Initrd SSH for remote unlocking
(lib.mkIf (cfg.enable && cfg.initrd-ssh.enable) {
boot.initrd.network.enable = true;
boot.initrd.availableKernelModules = cfg.initrd-ssh.ethernetDrivers;
boot.kernelParams = [ "ip=152.53.152.129::152.53.152.1:255.255.252.0::eth0:none" ];
boot.initrd.network.ssh = {
enable = true;
port = 2222; # different port to avoid conflicts
shell = "/bin/cryptsetup-askpass";
authorizedKeys = authorizedKeys;
hostKeys = [ "/etc/ssh/initrd" ];
};
boot.initrd.secrets = {
"/etc/ssh/initrd" = "/etc/ssh/initrd";
};
})
# ZFS Configuration
(lib.mkIf (cfg.enable && cfg.zfs.enable) {
networking.hostId = cfg.zfs.hostID;
environment.systemPackages = [ pkgs.zfs-prune-snapshots ];
boot = {
# ZFS does not support swapfiles
kernelParams = [
"nohibernate"
"zfs.zfs_arc_max=17179869184" # 16GB ARC max
];
supportedFilesystems = [
"vfat"
"zfs"
];
zfs = {
devNodes = "/dev/disk/by-id/";
forceImportAll = true;
requestEncryptionCredentials = cfg.zfs.root.encrypt;
};
};
services.zfs = {
autoScrub.enable = true;
trim.enable = true;
};
# Disko configuration for ZFS
disko.devices = {
disk = lib.mkMerge [
# Storage pool disks (if enabled and not reinstalling)
(lib.mkIf (cfg.zfs.storage.enable && !cfg.amReinstalling) (
lib.mkMerge (
map (diskname: {
"${diskname}" = {
type = "disk";
device = "/dev/${diskname}";
content = {
type = "gpt";
partitions = {
luks = {
size = "100%";
content = {
type = "luks";
name = "stg${diskname}";
settings.allowDiscards = true;
passwordFile = "/tmp/secret.key";
content = {
type = "zfs";
pool = "zstorage";
};
};
};
};
};
};
}) cfg.zfs.storage.disks
)
))
# Root disk 1 (primary)
{
one = lib.mkIf (cfg.zfs.root.disk1 != "") {
type = "disk";
device = "/dev/${cfg.zfs.root.disk1}";
content = {
type = "gpt";
partitions = {
ESP = {
label = "EFI";
name = "ESP";
size = "2048M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [
"defaults"
"umask=0077"
];
};
};
# Encrypted root partition
luks = lib.mkIf cfg.zfs.root.encrypt {
size = "100%";
content = {
type = "luks";
name = "crypted1";
settings.allowDiscards = true;
passwordFile = "/tmp/secret.key";
content = {
type = "zfs";
pool = "zroot";
};
};
};
# Unencrypted root partition
notluks = lib.mkIf (!cfg.zfs.root.encrypt) {
size = "100%";
content = {
type = "zfs";
pool = "zroot";
};
};
};
};
};
# Root disk 2 (mirror)
two = lib.mkIf (cfg.zfs.root.disk2 != "") {
type = "disk";
device = "/dev/${cfg.zfs.root.disk2}";
content = {
type = "gpt";
partitions = {
luks = {
size = "100%";
content = {
type = "luks";
name = "crypted2";
settings.allowDiscards = true;
passwordFile = "/tmp/secret.key";
content = {
type = "zfs";
pool = "zroot";
};
};
};
};
};
};
}
];
# ZFS pools
zpool = {
# Root pool
zroot = {
type = "zpool";
mode = lib.mkIf cfg.zfs.root.mirror "mirror";
rootFsOptions = {
canmount = "off";
checksum = "edonr";
compression = "zstd";
dnodesize = "auto";
mountpoint = "none";
normalization = "formD";
relatime = "on";
"com.sun:auto-snapshot" = "false";
};
options = {
ashift = "12";
autotrim = "on";
};
datasets = {
# Reserved space for ZFS CoW operations
reserved = {
type = "zfs_fs";
options = {
canmount = "off";
mountpoint = "none";
reservation = cfg.zfs.root.reservation;
};
};
# SSH keys dataset
etcssh = {
type = "zfs_fs";
options.mountpoint = "legacy";
mountpoint = "/etc/ssh";
options."com.sun:auto-snapshot" = "false";
postCreateHook = "zfs snapshot zroot/etcssh@empty";
};
# Persistent data
persist = {
type = "zfs_fs";
options.mountpoint = "legacy";
mountpoint = "/persist";
options."com.sun:auto-snapshot" = "false";
postCreateHook = "zfs snapshot zroot/persist@empty";
};
# Persistent save data
persistSave = {
type = "zfs_fs";
options.mountpoint = "legacy";
mountpoint = "/persist/save";
options."com.sun:auto-snapshot" = "false";
postCreateHook = "zfs snapshot zroot/persistSave@empty";
};
# Nix store
nix = {
type = "zfs_fs";
options.mountpoint = "legacy";
mountpoint = "/nix";
options = {
atime = "off";
canmount = "on";
"com.sun:auto-snapshot" = "false";
};
postCreateHook = "zfs snapshot zroot/nix@empty";
};
# Root filesystem
root = {
type = "zfs_fs";
options.mountpoint = "legacy";
options."com.sun:auto-snapshot" = "false";
mountpoint = "/";
postCreateHook = "zfs snapshot zroot/root@empty";
};
};
};
# Storage pool (if enabled and not reinstalling)
zstorage = lib.mkIf (cfg.zfs.storage.enable && !cfg.amReinstalling) {
type = "zpool";
mode = lib.mkIf cfg.zfs.storage.mirror "mirror";
rootFsOptions = {
canmount = "off";
checksum = "edonr";
compression = "zstd";
dnodesize = "auto";
mountpoint = "none";
normalization = "formD";
relatime = "on";
"com.sun:auto-snapshot" = "false";
};
options = {
ashift = "12";
autotrim = "on";
};
datasets = {
# Reserved space
reserved = {
type = "zfs_fs";
options = {
canmount = "off";
mountpoint = "none";
reservation = cfg.zfs.storage.reservation;
};
};
# Main storage
storage = {
type = "zfs_fs";
mountpoint = "/storage";
options = {
atime = "off";
canmount = "on";
"com.sun:auto-snapshot" = "false";
};
};
# Persistent save in storage
persistSave = {
type = "zfs_fs";
mountpoint = "/storage/save";
options = {
atime = "off";
canmount = "on";
"com.sun:auto-snapshot" = "false";
};
};
};
};
};
};
# Needed for agenix - SSH keys must be available before ZFS mounts
fileSystems."/etc/ssh".neededForBoot = true;
# Needed for impermanence
fileSystems."/persist".neededForBoot = true;
fileSystems."/persist/save".neededForBoot = true;
})
# Impermanence: wipe root on boot
(lib.mkIf (cfg.enable && cfg.zfs.enable && cfg.zfs.root.impermanenceRoot) {
boot.initrd.postResumeCommands = lib.mkAfter ''
zfs rollback -r zroot/root@empty
'';
})
];
}