{config, inputs, lib, ...}: let cfg = config.malobeo.disks; in { imports = [inputs.disko.nixosModules.disko]; options.malobeo.disks = { enable = lib.mkOption { type = lib.types.bool; default = false; description = "Enable disko disk creation"; }; hostId = lib.mkOption { type = lib.types.str; default = ""; description = "Host ID for zfs disks, generate with 'head -c4 /dev/urandom | od -A none -t x4'"; }; encryption = lib.mkOption { type = lib.types.bool; default = true; description = "Allows encryption to be disabled for testing"; }; devNodes = lib.mkOption { type = lib.types.str; default = "/dev/disk/by-id/"; description = '' where disks should be mounted from https://openzfs.github.io/openzfs-docs/Project%20and%20Community/FAQ.html#selecting-dev-names-when-creating-a-pool-linux use "/dev/disk/by-path/" for vm's ''; }; root = { disk0 = lib.mkOption { type = lib.types.str; default = ""; description = "name ab /dev für root dateisystem"; }; disk1 = lib.mkOption { type = lib.types.str; default = ""; description = "name ab /dev für eventuellen root mirror"; }; swap = lib.mkOption { type = lib.types.str; default = "8G"; description = "size of swap partition (only disk0)"; }; reservation = lib.mkOption { type = lib.types.str; default = "20GiB"; description = "zfs reservation"; }; mirror = lib.mkOption { type = lib.types.bool; default = false; description = "mirror zfs root pool"; }; }; storage = { enable = lib.mkOption { type = lib.types.bool; default = false; description = "Enable storage pool"; }; disks = lib.mkOption { type = lib.types.listOf lib.types.str; default = [ ]; description = "name ab /dev/ für storage pool"; example = "ata-ST16000NE000-2RW103_ZL2P0YSZ"; }; reservation = lib.mkOption { type = lib.types.str; default = "20GiB"; description = "zfs reservation"; }; mirror = lib.mkOption { type = lib.types.bool; default = false; description = "mirror zfs storage pool"; }; }; }; config = lib.mkIf cfg.enable { networking.hostId = cfg.hostId; disko.devices = { disk = lib.mkMerge [ { ssd0 = lib.mkIf (cfg.root.disk0 != "") { type = "disk"; device = "/dev/${cfg.root.disk0}"; content = { type = "gpt"; partitions = { ESP = { size = "1024M"; type = "EF00"; content = { type = "filesystem"; format = "vfat"; mountpoint = "/boot"; mountOptions = [ "umask=0077" ]; }; }; encryptedSwap = lib.mkIf cfg.encryption { size = cfg.root.swap; content = { type = "swap"; randomEncryption = true; }; }; zfs = { size = "100%"; content = { type = "zfs"; pool = "zroot"; }; }; }; }; }; ssd1 = lib.mkIf (cfg.root.disk1 != "") { type = "disk"; device = "/dev/${cfg.root.disk1}"; content = { type = "gpt"; partitions = { zfs = { size = "100%"; content = { type = "zfs"; pool = "zroot"; }; }; }; }; }; } (lib.mkIf cfg.storage.enable ( lib.mkMerge ( map (diskname: { "${diskname}" = { type = "disk"; device = "/dev/${diskname}"; content = { type = "gpt"; partitions = { zfs = { size = "100%"; content = { type = "zfs"; pool = "storage"; }; }; }; }; }; }) cfg.storage.disks ) )) ]; zpool = { zroot = { type = "zpool"; mode = lib.mkIf cfg.root.mirror "mirror"; # Workaround: cannot import 'zroot': I/O error in disko tests options.cachefile = "none"; rootFsOptions = { mountpoint = "none"; xattr = "sa"; # für microvm virtiofs mount acltype = "posixacl"; # für microvm virtiofs mount compression = "zstd"; "com.sun:auto-snapshot" = "false"; }; datasets = { encrypted = { type = "zfs_fs"; options = { mountpoint = "none"; encryption = lib.mkIf cfg.encryption "aes-256-gcm"; keyformat = lib.mkIf cfg.encryption "passphrase"; keylocation = lib.mkIf cfg.encryption "file:///tmp/secret.key"; }; # use this to read the key during boot postCreateHook = lib.mkIf cfg.encryption '' zfs set keylocation="prompt" zroot/encrypted; ''; }; "encrypted/root" = { type = "zfs_fs"; mountpoint = "/"; options.mountpoint = "legacy"; }; "encrypted/var" = { type = "zfs_fs"; mountpoint = "/var"; options.mountpoint = "legacy"; }; "encrypted/etc" = { type = "zfs_fs"; mountpoint = "/etc"; options.mountpoint = "legacy"; }; "encrypted/home" = { type = "zfs_fs"; mountpoint = "/home"; options.mountpoint = "legacy"; }; "encrypted/nix" = { type = "zfs_fs"; mountpoint = "/nix"; options.mountpoint = "legacy"; }; reserved = { # for cow delete if pool is full options = { canmount = "off"; mountpoint = "none"; reservation = "${cfg.root.reservation}"; }; type = "zfs_fs"; }; }; }; storage = lib.mkIf cfg.storage.enable { type = "zpool"; mode = lib.mkIf (cfg.storage.mirror) "mirror"; rootFsOptions = { mountpoint = "none"; xattr = "sa"; # für microvm virtiofs mount acltype = "posixacl"; # für microvm virtiofs mount }; datasets = { encrypted = { type = "zfs_fs"; options = { mountpoint = "none"; encryption = lib.mkIf cfg.encryption "aes-256-gcm"; keyformat = lib.mkIf cfg.encryption "passphrase"; keylocation = lib.mkIf cfg.encryption "file:///tmp/secret.key"; }; # use this to read the key during boot postCreateHook = lib.mkIf cfg.encryption '' zfs set keylocation="prompt" storage/encrypted; ''; }; "encrypted/data" = { type = "zfs_fs"; mountpoint = "/data"; options.mountpoint = "legacy"; }; "encrypted/data/microvms" = { type = "zfs_fs"; mountpoint = "/data/microvms"; options.mountpoint = "legacy"; }; reserved = { # for cow delete if pool is full options = { canmount = "off"; mountpoint = "none"; reservation = "${cfg.storage.reservation}"; }; type = "zfs_fs"; }; }; }; }; }; boot.zfs.devNodes = lib.mkDefault cfg.devNodes; fileSystems."/".neededForBoot = true; fileSystems."/etc".neededForBoot = true; fileSystems."/boot".neededForBoot = true; fileSystems."/var".neededForBoot = true; fileSystems."/home".neededForBoot = true; fileSystems."/nix".neededForBoot = true; }; }