diff --git a/doc/src/SUMMARY.md b/doc/src/SUMMARY.md index a190f47..001e937 100644 --- a/doc/src/SUMMARY.md +++ b/doc/src/SUMMARY.md @@ -11,6 +11,9 @@ - [Website](./server/website.md) - [musik](./projekte/musik.md) - [TODO](./todo.md) + - [Modules]() + - [Initrd-ssh](./module/initssh.md) + - [Disks](./module/disks.md) - [How-to]() - [Create New Host](./anleitung/create.md) - [Sops](./anleitung/sops.md) diff --git a/doc/src/module/disks.md b/doc/src/module/disks.md new file mode 100644 index 0000000..7febccb --- /dev/null +++ b/doc/src/module/disks.md @@ -0,0 +1,117 @@ +# Disks +The disks module can be used by importing `inputs.self.nixosModules.malobeo.disko` + + +#### `let cfg = malobeo.disks` + +#### `cfg.enable` (bool) +- **Type:** `bool` +- **Default:** `false` +- **Description:** + Enables the disk creation process using the `disko` tool. Set to `true` to initialize disk setup. + +#### `cfg.hostId` (string) +- **Type:** `string` +- **Default:** `""` +- **Description:** + The host ID used for ZFS disks. This ID should be generated using a command like `head -c4 /dev/urandom | od -A none -t x4`. + +#### `cfg.encryption` (bool) +- **Type:** `bool` +- **Default:** `true` +- **Description:** + Determines if encryption should be enabled. Set to `false` to disable encryption for testing purposes. + +#### `cfg.devNodes` (string) +- **Type:** `string` +- **Default:** `"/dev/disk/by-id/"` +- **Description:** + Specifies where the disks should be mounted from. + - Use `/dev/disk/by-id/` for general systems. + - Use `/dev/disk/by-path/` for VMs. + - For more information on disk name conventions, see [OpenZFS FAQ](https://openzfs.github.io/openzfs-docs/Project%20and%20Community/FAQ.html#selecting-dev-names-when-creating-a-pool-linux). + +#### `let cfg = malobeo.disks.root` +#### `cfg.disk0` (string) +- **Type:** `string` +- **Default:** `""` +- **Description:** + The device name (beginning after `/dev/` e.g., `sda`) for the root filesystem. + +#### `cfg.disk1` (string) +- **Type:** `string` +- **Default:** `""` +- **Description:** + The device name (beginning after `/dev/` e.g., `sdb`) for the optional mirror disk of the root filesystem. + +#### `cfg.swap` (string) +- **Type:** `string` +- **Default:** `"8G"` +- **Description:** + Size of the swap partition on `disk0`. This is applicable only for the root disk configuration. + +#### `cfg.reservation` (string) +- **Type:** `string` +- **Default:** `"20GiB"` +- **Description:** + The ZFS reservation size for the root pool. + +#### `cfg.mirror` (bool) +- **Type:** `bool` +- **Default:** `false` +- **Description:** + Whether to configure a mirrored ZFS root pool. Set to `true` to mirror the root filesystem across `disk0` and `disk1`. + +#### `let cfg = malobeo.disks.storage` +#### `cfg.enable` (bool) +- **Type:** `bool` +- **Default:** `false` +- **Description:** + Enables the creation of an additional storage pool. Set to `true` to create the storage pool. + +#### `cfg.disks` (list of strings) +- **Type:** `listOf string` +- **Default:** `[]` +- **Description:** + A list of device names without /dev/ prefix (e.g., `sda`, `sdb`) to include in the storage pool. + Example: `["disks/by-id/ata-ST16000NE000-2RW103_ZL2P0YSZ"]`. + +#### `cfg.reservation` (string) +- **Type:** `string` +- **Default:** `"20GiB"` +- **Description:** + The ZFS reservation size for the storage pool. + +#### `cfg.mirror` (bool) +- **Type:** `bool` +- **Default:** `false` +- **Description:** + Whether to configure a mirrored ZFS storage pool. Set to `true` to mirror the storage pool. + +## Example Configuration + +```nix +{ + options.malobeo.disks = { + enable = true; + hostId = "abcdef01"; + encryption = true; + devNodes = "/dev/disk/by-id/"; + + root = { + disk0 = "sda"; + disk1 = "sdb"; + swap = "8G"; + reservation = "40GiB"; + mirror = true; + }; + + storage = { + enable = true; + disks = [ "sdc" "sdd" "disks/by-uuid/sde" ]; + reservation = "100GiB"; + mirror = false; + }; + }; +} +``` diff --git a/doc/src/module/initssh.md b/doc/src/module/initssh.md new file mode 100644 index 0000000..d87fbe0 --- /dev/null +++ b/doc/src/module/initssh.md @@ -0,0 +1,29 @@ +# Initrd-ssh +The initssh module can be used by importing `inputs.self.nixosModules.malobeo.initssh` + +#### `let cfg = malobeo.initssh` + +## cfg.enable +Enable the initssh module + +*Default* +false + + +## cfg.authorizedKeys +Authorized keys for the initrd ssh + +*Default* +`[ ]` + + +## cfg.ethernetDrivers + +Ethernet drivers to load in the initrd. +Run ` lspci -k | grep -iA4 ethernet ` + +*Default:* +` [ ] ` + +*Example:* +`[ "r8169" ]` \ No newline at end of file diff --git a/machines/.sops.yaml b/machines/.sops.yaml index db170a9..6b73de0 100644 --- a/machines/.sops.yaml +++ b/machines/.sops.yaml @@ -43,3 +43,17 @@ creation_rules: age: - *machine_vpn - *admin_atlan + - path_regex: testvm/disk.key + key_groups: + - pgp: + - *admin_kalipso + - *admin_kalipso_dsktp + age: + - *admin_atlan + - path_regex: fanny/disk.key + key_groups: + - pgp: + - *admin_kalipso + - *admin_kalipso_dsktp + age: + - *admin_atlan diff --git a/machines/configuration.nix b/machines/configuration.nix index 9aa4633..a55a370 100644 --- a/machines/configuration.nix +++ b/machines/configuration.nix @@ -118,8 +118,6 @@ in specialArgs.inputs = inputs; modules = defaultModules ++ [ ./fanny/configuration.nix - inputs.disko.nixosModules.disko - ./modules/disko/fanny.nix ]; }; @@ -150,4 +148,11 @@ in ./lucia/hardware_configuration.nix ]; }; + + testvm = nixosSystem { + system = "x86_64-linux"; + specialArgs.inputs = inputs; + specialArgs.self = self; + modules = defaultModules ++ [ ./testvm ]; + }; } diff --git a/machines/fanny/configuration.nix b/machines/fanny/configuration.nix index 7997d71..c2b7ab1 100644 --- a/machines/fanny/configuration.nix +++ b/machines/fanny/configuration.nix @@ -1,5 +1,7 @@ -{ config, pkgs, ... }: - +{ inputs, pkgs, ... }: +let + sshKeys = import ../ssh_keys.nix; +in { imports = [ # Include the results of the hardware scan. @@ -8,6 +10,8 @@ ../modules/sshd.nix ../modules/minimal_tools.nix ../modules/autoupdate.nix + inputs.self.nixosModules.malobeo.initssh + inputs.self.nixosModules.malobeo.disko ]; malobeo.autoUpdate = { @@ -18,11 +22,26 @@ cacheurl = "https://cache.dynamicdiscord.de"; }; - boot.initrd.systemd.enable = true; - boot.loader.systemd-boot.enable = true; - nix.settings.experimental-features = [ "nix-command" "flakes" ]; + malobeo.disks = { + enable = true; + hostId = "a3c3101f"; + root = { + disk0 = "disk/by-id/ata-SAMSUNG_MZ7LN256HCHP-000L7_S20HNAAH200381"; + }; + storage = { + disks = ["disk/by-id/wwn-0x50014ee265b53b60" "disk/by-id/wwn-0x50014ee2bb0a194a"]; + mirror = true; + }; + }; + + malobeo.initssh = { + enable = true; + authorizedKeys = sshKeys.admins; + ethernetDrivers = ["r8169"]; + }; + services.tor = { enable = true; client.enable = true; @@ -34,7 +53,6 @@ services.acpid.enable = true; networking.hostName = "fanny"; - networking.hostId = "1312acab"; networking.networkmanager.enable = true; virtualisation.vmVariant.virtualisation.graphics = false; diff --git a/machines/fanny/disk.key b/machines/fanny/disk.key new file mode 100644 index 0000000..7a30f5e --- /dev/null +++ b/machines/fanny/disk.key @@ -0,0 +1,31 @@ +{ + "data": "ENC[AES256_GCM,data:1I8fN241VOaW4GaNUe/OVr+1HQKmtYL1GSuIfsE=,iv:aHdgEUj5QhusEavG9mVgtTQ4uqLJD2ozQ/kVVtFakYY=,tag:JJUbt4kgpa4hVD3HjLXGOg==,type:str]", + "sops": { + "kms": null, + "gcp_kms": null, + "azure_kv": null, + "hc_vault": null, + "age": [ + { + "recipient": "age1ljpdczmg5ctqyeezn739hv589fwhssjjnuqf7276fqun6kc62v3qmhkd0c", + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBEUGpORk5zWXU1OVpqc2hT\nVW5PYlNLT3lKQVpTdCtMT1M3YlZ3Uno5bVJjCkJXR3I2Y3lDT0dJNThCcDN1NXYr\nK3VucjRKU0dac3BtQmV5ZFdrZXkrS1EKLS0tIGRGMGxDM0ZGbzVPTnJQK01GS3VW\nRHpJQWZLU1lrRS9ScXM0L0dyTjhGTGsKJEYq5vKxxYBAgkqUEkBwESur0reNIDPb\nK3rtflNi3dUYYZdLFNFV5rQX5q8aDnM6fO/zYPkzfBn7Ewq3jbBIIg==\n-----END AGE ENCRYPTED FILE-----\n" + } + ], + "lastmodified": "2025-01-05T19:35:48Z", + "mac": "ENC[AES256_GCM,data:z7elJ0+3r0bWc/H6h4rI36xC7Uj0NS04VssjPDNVZM17LeN4ansSOfcOKPaUMziV/z5Aq8RVLROR+FImzxBZGaZm37frCoN1OP3WjeDnP6AsoY9dY+S/aYmErVEsQEIi8T4RAdQP2c3BUt1oKZ9Nki2pu3IBRabBlFhaTI0bspc=,iv:8Nn8r9ancHwBJOaJSsv8Vj3s+d0UvRmKIeCDNzx1qRg=,tag:BSO2yu70H2wjen3BCGC4Gw==,type:str]", + "pgp": [ + { + "created_at": "2025-01-05T19:32:11Z", + "enc": "-----BEGIN PGP MESSAGE-----\n\nhQGMA5HdvEwzh/H7AQv+JpNwP+BLJf4+0pSr17TToviCo0yWmcaP1dIUqClBSoDO\nI3ZzqHdImAj4QgExif2zsuzz1+WC+sjvFqEmX5pBKza/e30qCZirkelz9mzc0mhG\nLhTzfhqC6fLbV5f+pDp6N40ommu+LX1pIz6nViCUjqBdnAkCb+tqLU4eQJQqVmlz\n7BToLsvYomPK1nJ6f4rt1nTR9wkBI68AYM/K0SgCJXjwj1LpZ/+3yElkiCqZ9uZB\n1jrDKX+QPySlZ7OERL70UT7Eh8DTUNzFnozvliBnyxe00wwiiucCgrC94TmaKCmh\ni/FOdS6Izm3QwcWB0eMCX6GQBvlUWpjSz5xF4+YODJe9tGNz/sNxpk6B8xG5NuG2\n61nohMHoml6X3Z9dOwu/Svl+eS8SV/r278W/F9miE8YeayyLlPxHF3DXjd6WeDhZ\n20NExQUJYIRf6w/XQPQZ+E39NkIHxz8v+P29ncmSsRPWS6d2MK0Yj+UW0vT0u1vJ\n+lAs24xYofbu5tmBbnK10lgBrZMXDJM2nQbKMKSkVVjzbzmOe5jzMBxuWLX+ykeI\npaj32wQDWvfBqLPH1Kwvy5nqHvy375jPZ7RTzT7W0d4jKQf7xapbi4CEepHHfxCF\nD0HIEi8RUlXJ\n=KVUJ\n-----END PGP MESSAGE-----", + "fp": "c4639370c41133a738f643a591ddbc4c3387f1fb" + }, + { + "created_at": "2025-01-05T19:32:11Z", + "enc": "-----BEGIN PGP MESSAGE-----\n\nhQIMA98TrrsQEbXUARAAqowFMavIniFheNvt03EH1iEn64xNmExotYcDt2L0bR39\nXQdLvg7cJ/Jh7EuZ44mHTs21mpbYIlygMs6kimqQ8iO30vGTEcn5bt/eUEoGHciM\nYVHktWNR81ZgjvKCcmTUK3ld+DMKmg2BABr4auUOYLu4ToSnFb1fv+fvZG0D3iQs\nm6LJuafH+4utM16Vnkp9+ziY/ieMPYfbOFuSFq0UWxGK9P+koSYVGnYhH55Lksyf\nBb/esEGCY671/Jl/qHw8so4TELeRsW/v/xAcNqbE1Msdeas7WJy/B6WqXQgK/Y+J\nPsyZ2XHKhPRitN77/eDJXVBi0mKBTE/RCzDzMYxKA7IQm28v8+u+wpdCajewnyF4\ns2HACaYs/TWRpIUzqxRlznc0nMpk8xUaeVb0N7nrtSDEBF8ETOGOcPk1AmdKMR4M\nsy0vu+K2oJ9L7e/o1ntpejKHN7t2Lzq+CvszBYKmyw/KgxeqY0hx4cJTUDsdgLjI\nMTrs6bySVXDyRaw3rHo7OvA+5c8dLfnWJd1R78nZTx89CYCvjJeMo7PNvN6C9HxK\nJoCOCnZo6a3j4NqJvXD5GNqGSP6m1lqBRWYQUIhWaOfz8aTY1Z3EXX0/4tv5C+A/\nknhc694ujtmBXio4XgDIrSz3jr9G8+ZLvig88xV12HTJfsatypQdHVIZj08EeR/S\nWAG872Q/DVD/aDmhaOlq/o/QBoEyrnJdkRHT9NX8iBboQ81wezfJxWUWlWyHaXVq\n5YBLFQvQAZLz3h05EBkMOiS2dHUa8OnNImj8txnCePAlcUdv7LIVxHA=\n=9APA\n-----END PGP MESSAGE-----", + "fp": "aef8d6c7e4761fc297cda833df13aebb1011b5d4" + } + ], + "unencrypted_suffix": "_unencrypted", + "version": "3.9.2" + } +} \ No newline at end of file diff --git a/machines/modules/disko/default.nix b/machines/modules/disko/default.nix new file mode 100644 index 0000000..2794fff --- /dev/null +++ b/machines/modules/disko/default.nix @@ -0,0 +1,278 @@ +{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 = { + 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"; + }; + 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; + }; +} diff --git a/machines/modules/disko/fanny.nix b/machines/modules/disko/fanny.nix deleted file mode 100644 index 0ac2a1c..0000000 --- a/machines/modules/disko/fanny.nix +++ /dev/null @@ -1,157 +0,0 @@ -{ - disko.devices = { - disk = { - ssd = { - type = "disk"; - device = "/dev/sda"; - content = { - type = "gpt"; - partitions = { - ESP = { - size = "1024M"; - type = "EF00"; - content = { - type = "filesystem"; - format = "vfat"; - mountpoint = "/boot"; - mountOptions = [ "umask=0077" ]; - }; - }; - encryptedSwap = { - size = "8G"; #set to 100M for testing - content = { - type = "swap"; - randomEncryption = true; - }; - }; - zfs = { - size = "100%"; - content = { - type = "zfs"; - pool = "zroot"; - }; - }; - }; - }; - }; - - hdd0 = { - type = "disk"; - device = "/dev/sdb"; - content = { - type = "gpt"; - partitions = { - zfs = { - size = "100%"; - content = { - type = "zfs"; - pool = "storage"; - }; - }; - }; - }; - }; - - hdd1 = { - type = "disk"; - device = "/dev/sdc"; - content = { - type = "gpt"; - partitions = { - zfs = { - size = "100%"; - content = { - type = "zfs"; - pool = "storage"; - }; - }; - }; - }; - }; - }; - - zpool = { - zroot = { - type = "zpool"; - mode = ""; - # Workaround: cannot import 'zroot': I/O error in disko tests - options.cachefile = "none"; - rootFsOptions = { - mountpoint = "none"; - compression = "zstd"; - "com.sun:auto-snapshot" = "false"; - }; - - datasets = { - encrypted = { - type = "zfs_fs"; - options = { - mountpoint = "none"; - encryption = "aes-256-gcm"; - keyformat = "passphrase"; - keylocation = "file:///tmp/root.key"; - }; - # use this to read the key during boot - postCreateHook = '' - zfs set keylocation="prompt" zroot/encrypted; - ''; - }; - "encrypted/root" = { - type = "zfs_fs"; - mountpoint = "/"; - }; - "encrypted/var" = { - type = "zfs_fs"; - mountpoint = "/var"; - }; - "encrypted/etc" = { - type = "zfs_fs"; - mountpoint = "/etc"; - }; - "encrypted/home" = { - type = "zfs_fs"; - mountpoint = "/home"; - }; - "encrypted/nix" = { - type = "zfs_fs"; - mountpoint = "/nix"; - }; - }; - }; - - storage = { - type = "zpool"; - mode = "mirror"; - rootFsOptions = { mountpoint = "none"; }; - - datasets = { - encrypted = { - type = "zfs_fs"; - options = { - mountpoint = "none"; - encryption = "aes-256-gcm"; - keyformat = "passphrase"; - keylocation = "file:///tmp/storage.key"; - }; - - # use this to read the key during boot - postCreateHook = '' - zfs set keylocation="prompt" storage/encrypted; - ''; - }; - "encrypted/data" = { - type = "zfs_fs"; - mountpoint = "/data"; - }; - }; - }; - }; - }; - - fileSystems."/".neededForBoot = true; - fileSystems."/boot".neededForBoot = true; - fileSystems."/var".neededForBoot = true; - fileSystems."/etc".neededForBoot = true; - fileSystems."/home".neededForBoot = true; - fileSystems."/nix".neededForBoot = true; -} diff --git a/machines/modules/malobeo/initssh.nix b/machines/modules/malobeo/initssh.nix new file mode 100644 index 0000000..8286084 --- /dev/null +++ b/machines/modules/malobeo/initssh.nix @@ -0,0 +1,66 @@ +{ config, lib, pkgs, ... }: +let + cfg = config.malobeo.initssh; + inherit (config.networking) hostName; + +in +{ + options.malobeo.initssh = { + enable = lib.mkOption { + type = lib.types.bool; + default = false; + description = "Enable initrd-ssh"; + }; + authorizedKeys = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ ]; + description = "Authorized keys for the initrd ssh"; + }; + ethernetDrivers = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ ]; + description = "Ethernet drivers to load: run `lspci -k | grep -iA4 ethernet`"; + example = "r8169"; + }; + }; + + config = lib.mkIf (cfg.enable && config.malobeo.disks.encryption) { + boot = { + loader.systemd-boot.enable = true; + loader.efi.canTouchEfiVariables = true; + supportedFilesystems = [ "vfat" "zfs" ]; + zfs = { + forceImportAll = true; + requestEncryptionCredentials = true; + + }; + initrd = { + availableKernelModules = cfg.ethernetDrivers; + systemd = { + enable = true; + network.enable = true; + }; + network.ssh = { + enable = true; + port = 222; + authorizedKeys = cfg.authorizedKeys; + hostKeys = [ "/etc/ssh/initrd" ]; + }; + secrets = { + "/etc/ssh/initrd" = "/etc/ssh/initrd"; + }; + systemd.services.zfs-remote-unlock = { + description = "Prepare for ZFS remote unlock"; + wantedBy = ["initrd.target"]; + after = ["systemd-networkd.service"]; + path = with pkgs; [ zfs ]; + serviceConfig.Type = "oneshot"; + script = '' + echo "systemctl default" >> /var/empty/.profile + ''; + }; + }; + kernelParams = [ "ip=::::${hostName}-initrd::dhcp" ]; + }; + }; +} \ No newline at end of file diff --git a/machines/testvm/default.nix b/machines/testvm/default.nix new file mode 100644 index 0000000..b338fbc --- /dev/null +++ b/machines/testvm/default.nix @@ -0,0 +1,59 @@ +{ config, pkgs, inputs, ... }: +let + sshKeys = import ../ssh_keys.nix; +in +{ + imports = + [ # Include the results of the hardware scan. + #./hardware-configuration.nix + ../modules/malobeo_user.nix + ../modules/sshd.nix + ../modules/minimal_tools.nix + inputs.self.nixosModules.malobeo.initssh + inputs.self.nixosModules.malobeo.disko + ]; + + + boot.initrd.systemd.enable = true; + boot.loader.systemd-boot.enable = true; + malobeo.initssh = { + enable = true; + authorizedKeys = sshKeys.admins; + ethernetDrivers = ["virtio_net"]; + }; + + malobeo.disks = { + enable = true; + encryption = false; + hostId = "83abc8cb"; + devNodes = "/dev/disk/by-path/"; + root = { + disk0 = "disk/by-path/pci-0000:04:00.0"; + swap = "1G"; + reservation = "1G"; + mirror = false; + }; + storage = { + enable = true; + disks = ["disk/by-path/pci-0000:08:00.0" "disk/by-path/pci-0000:09:00.0"]; + reservation = "1G"; + mirror = true; + }; + }; + + boot.initrd.kernelModules = ["virtio_blk" "zfs" "virtio_console" "virtio_pci" "virtio" "virtio_net"]; + + nix.settings.experimental-features = [ "nix-command" "flakes" ]; + + # needed for printing drivers + nixpkgs.config.allowUnfree = true; + + services.acpid.enable = true; + + networking.hostName = "testvm"; + networking.networkmanager.enable = true; + + time.timeZone = "Europe/Berlin"; + system.stateVersion = "23.05"; # Do.. Not.. Change.. +} + diff --git a/machines/testvm/disk.key b/machines/testvm/disk.key new file mode 100644 index 0000000..6d40735 --- /dev/null +++ b/machines/testvm/disk.key @@ -0,0 +1,31 @@ +{ + "data": "ENC[AES256_GCM,data:GH71ek6+a++P9sDUjO0IPojdU1epX98wcTqmoEgsu0j+,iv:LysgsJdPDvKOUz7l0IyV58QHN2RHvHP14bt1p4571NM=,tag:1WrqC3S+Z6bkE2d76RYtXA==,type:str]", + "sops": { + "kms": null, + "gcp_kms": null, + "azure_kv": null, + "hc_vault": null, + "age": [ + { + "recipient": "age1ljpdczmg5ctqyeezn739hv589fwhssjjnuqf7276fqun6kc62v3qmhkd0c", + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHOVI3b1dBa2d5SElHcFdq\nVHZwWlpIU3NpYm8zQnY3aVhOVkxnU1pkZUJNCkJ6bzhqdU5EVy9Wa0creXJHZ1pu\nbkRPVTR1K0o0dmlYbGVIbVRiWjFyL1kKLS0tIHl0aFpUYy9hWmpsNUFoY2JpWUhL\nalluN1RRSTBNUlprZWFISlFoUExXUXMKaULQKgVLNfHX8m0Ac1YhcbM/yhioyNCu\na1AUDjBmruKL9ngqz9Dwzxx0sJJOIFKMdYMVn9uQfui/XCHewO6uRw==\n-----END AGE ENCRYPTED FILE-----\n" + } + ], + "lastmodified": "2024-12-31T02:35:20Z", + "mac": "ENC[AES256_GCM,data:7K8G7ZFaA7wT0lwujkuJP0HL8WW0m/IkMjgFU9ikWe/GVZMlFDWTafaRNLxdBHNhHwilM8suH2z0P36Xae6pReh47PpID5JS8NC1V38fzww5qW74eFkHq3Pu8HRWb66u7zA/LiyOcEQgtrdP1zbnfmHUgakyNluSn7W1gOtsfxw=,iv:l65AiYn7ETRySF1Wr9nOUk9Fd1I4VGqd/zZbqkCyxYA=,tag:TeVyRa8aN6hIn3iIKPPvbQ==,type:str]", + "pgp": [ + { + "created_at": "2024-12-31T02:35:05Z", + "enc": "-----BEGIN PGP MESSAGE-----\n\nhQGMA5HdvEwzh/H7AQv/ZITVtnQl5xO2XLTTaNAZ50WhHkVV1G9H2TyxO0NbaUPj\nbo7LdbuB/+cv3wpg5oy5VpWW/JLElqizxbrE5gzQCzorwGE7lpKW0XQubofW8t9l\n+6k9UFXxyfVQJHwcIbexYfL2UhN62eSzzxPiKYVyNw4oM9ySeU+MCeCiv0omLUPg\nWSdOH4q1QYkRGJO8db7KlJSdvCoVjyEiCaLwKdWnPk5pbC+U7wp75fPdFwmzBchc\np9TXKeFF8dVGI7DKuGXA7lBm4ZzgSt4wNdZmc7mvTrTInaDVFA/ptbAfhh2/hNEx\npOijlXbc8ARKAhuLASPy6j37Nm2QdNm/8dl5x6eA7Sx7FcO8qV38Q//V4/DZZddJ\nT3NLC4tWLglpdyFX7H0zmZ+jQOLGJHorwzO+NgSOEj3N4venHYvJyI+vwVGjVCjQ\n1tZUIxGMx5iu959PinvlvBYI7oeKITPLyo8pRRx2EaA+UEBR2f3y+R0bTiBhChKM\nieUIVIK/fbvhdXhwwfRe0lgBm05hL/Vmdbal9QU8o/HIPeGTNitaqLQ59Ets7qm4\nf2FhHaOMO0YaDPtCNBGbRh/mEWH8tjhnI1sLJg/0rR9sOQ/oCzzIYILogIkm3ueE\notFqp95QQPVA\n=P16c\n-----END PGP MESSAGE-----", + "fp": "c4639370c41133a738f643a591ddbc4c3387f1fb" + }, + { + "created_at": "2024-12-31T02:35:05Z", + "enc": "-----BEGIN PGP MESSAGE-----\n\nhQIMA98TrrsQEbXUAQ//fAGV0oLuiwL4TmQnrHF88ixvZ/HghKI9k/5zlORIdoaR\na1w6U32coX8HpEfcqON45ZQWSCFtlizlmL55jb1ugXFY/bS+KECO8XaMDhHXNkB/\ndfeCmASvqIlFkl/X3YeD2FhHa3ZlcS93x0duJ+oo18WIErkNuECOL7hwkh+m5YfS\nWtW9Z3J51qfS5S6ctdm9vKcYSrgTkADsyVQp9GqxO3xZGpWudGWDaK0gVBX5wk5t\n1uKhDpnIZdFZ42N5Oy/UqXF5pfEQ0OwxlOS8VMleq1wEPc/DPVku23HRSReS0k7x\nuVeFZpaOfe22ncgI4TVQln8JT0+ZPeAwqBn6LWp0XnPnQdkyE79ARMPqBTPN/6Pn\nFkVpInBVukVJ1AiGpHHxESPtiKoMUZpE+k3WG2dRFWmaON+n0kR4VFpOju3apxTH\n8RGN+Uyn6MswNOZDKoDjlVtkcwgJgar/KwxXNlF7BU3/KMDEBf1UHuQE58Y2eBsC\nI85AEpbskEeOu+MF1SNJkdx/BR+lUaR6ax+dVzOIwxLyyDoCGg4SEoL1Hh1nNRth\nxRZnYfN3FBGv3FnvpaCbfbBDLLkWxzst5HRjp+v2lyPM4eVtyvYPGdfYM5FK1den\nXVawulE3cjM786/Z7X2IK5IDzrvo8nIs/Keg2YqnZe0UgM3XFCoYnwxi2Rev1J3S\nWAHTBs22q/cEk3SLlfzLyqWochY33gI6fC2amOvC5HNhcs7vr6CF1W44d3Yx6WCO\npqxY9jmc4gVWeBLZV/d9T95qLwOQK7L1/tokdbggQcEXFOqpvPzm5pc=\n=qp/h\n-----END PGP MESSAGE-----", + "fp": "aef8d6c7e4761fc297cda833df13aebb1011b5d4" + } + ], + "unencrypted_suffix": "_unencrypted", + "version": "3.9.2" + } +} \ No newline at end of file diff --git a/outputs.nix b/outputs.nix index bd44a0b..f79fd18 100644 --- a/outputs.nix +++ b/outputs.nix @@ -40,6 +40,8 @@ in (utils.lib.eachSystem (builtins.filter filter_system utils.lib.defaultSystems }; packages = { + remote-install = pkgs.writeShellScriptBin "remote-install" (builtins.readFile ./scripts/remote-install-encrypt.sh); + boot-unlock = pkgs.writeShellScriptBin "boot-unlock" (builtins.readFile ./scripts/unlock-boot.sh); docs = pkgs.stdenv.mkDerivation { name = "malobeo-docs"; phases = [ "buildPhase" ]; @@ -116,6 +118,8 @@ in (utils.lib.eachSystem (builtins.filter filter_system utils.lib.defaultSystems host.imports = [ ./machines/durruti/host_config.nix ]; microvm.imports = [ ./machines/modules/malobeo/microvm_host.nix ]; vpn.imports = [ ./machines/modules/malobeo/wireguard.nix ]; + initssh.imports = [ ./machines/modules/malobeo/initssh.nix ]; + disko.imports = [ ./machines/modules/disko ]; }; hydraJobs = nixpkgs.lib.mapAttrs (_: nixpkgs.lib.hydraJob) ( diff --git a/scripts/remote-install-encrypt.sh b/scripts/remote-install-encrypt.sh new file mode 100755 index 0000000..2783fcc --- /dev/null +++ b/scripts/remote-install-encrypt.sh @@ -0,0 +1,47 @@ +set -o errexit +set -o nounset +set -o pipefail + +if [ $# -lt 2 ]; then + echo + echo "Install NixOS to the host system with secrets and encryption" + echo "Usage: $0 (user)" + exit 1 +fi + +hostname=$1 +ipaddress=$2 + +# Create a temporary directory +temp=$(mktemp -d) + +# Function to cleanup temporary directory on exit +cleanup() { + rm -rf "$temp" +} +trap cleanup EXIT + +# Create the directory where sshd expects to find the host keys +install -d -m755 "$temp/etc/ssh/" + +diskKey=$(sops -d machines/$hostname/disk.key) +echo "$diskKey" > /tmp/secret.key + +ssh-keygen -f $temp/etc/ssh/"$hostname" -t ed25519 -N "" +ssh-keygen -f $temp/etc/ssh/initrd -t ed25519 -N "" + +# # Set the correct permissions so sshd will accept the key +chmod 600 "$temp/etc/ssh/$hostname" +chmod 600 "$temp/etc/ssh/initrd" + +# Install NixOS to the host system with our secrets and encription +# optional --build-on-remote +if [ $# = 3 ] + then + nix run github:numtide/nixos-anywhere -- --extra-files "$temp" \ + --disk-encryption-keys /tmp/secret.key /tmp/secret.key --flake .#$hostname $3@$ipaddress + +else +nix run github:numtide/nixos-anywhere -- --extra-files "$temp" \ + --disk-encryption-keys /tmp/secret.key /tmp/secret.key --flake .#$hostname root@$ipaddress +fi \ No newline at end of file diff --git a/scripts/unlock-boot.sh b/scripts/unlock-boot.sh new file mode 100644 index 0000000..e519bc3 --- /dev/null +++ b/scripts/unlock-boot.sh @@ -0,0 +1,30 @@ +set -o errexit +set -o pipefail + +sshoptions="-o StrictHostKeyChecking=no -o ServerAliveInterval=1 -o ServerAliveCountMax=1 -p 222 -T" +HOSTNAME=$1 + +echo +diskkey=$(sops -d machines/$HOSTNAME/disk.key) + +if [ $# = 1 ] + then + echo "$diskkey" | ssh $sshoptions root@$HOSTNAME-initrd "systemd-tty-ask-password-agent" #storage + + echo "$diskkey" | ssh $sshoptions root@$HOSTNAME-initrd "systemd-tty-ask-password-agent" #root + +elif [ $# = 2 ] + then + IP=$2 + + echo "$diskkey" | ssh $sshoptions root@$IP "systemd-tty-ask-password-agent" #storage + + echo "$diskkey" | ssh $sshoptions root@$IP "systemd-tty-ask-password-agent" #root + +else + echo + echo "Unlock the root disk on a remote host." + echo "Usage: $0 [ip]" + echo "If an IP is not provided, the hostname will be used as the IP address." + exit 1 +fi \ No newline at end of file