diff --git a/clanModules/disk-id/README.md b/clanModules/disk-id/README.md new file mode 100644 index 000000000..29069f002 --- /dev/null +++ b/clanModules/disk-id/README.md @@ -0,0 +1,3 @@ +--- +description = "Generates a uuid for use in disk device naming" +--- diff --git a/clanModules/disk-id/default.nix b/clanModules/disk-id/default.nix new file mode 100644 index 000000000..870a2a5fd --- /dev/null +++ b/clanModules/disk-id/default.nix @@ -0,0 +1,26 @@ +{ + config, + pkgs, + ... +}: + +{ + + config = { + clan.core.vars.generators.disk-id = { + files.diskId.secret = false; + runtimeInputs = [ + pkgs.coreutils + pkgs.bash + ]; + script = '' + uuid=$(bash ${../uuid4.sh}) + + # Remove the hyphens from the UUID + uuid_no_hyphens=$(echo -n "$uuid" | tr -d '-') + + echo -n "$uuid_no_hyphens" > "$out/diskId" + ''; + }; + }; +} diff --git a/clanModules/disk-id/roles/default.nix b/clanModules/disk-id/roles/default.nix new file mode 100644 index 000000000..ffcd4415b --- /dev/null +++ b/clanModules/disk-id/roles/default.nix @@ -0,0 +1 @@ +{ } diff --git a/clanModules/flake-module.nix b/clanModules/flake-module.nix index 24ce7cd2e..ca4149039 100644 --- a/clanModules/flake-module.nix +++ b/clanModules/flake-module.nix @@ -5,6 +5,8 @@ borgbackup = ./borgbackup; borgbackup-static = ./borgbackup-static; deltachat = ./deltachat; + machine-id = ./machine-id; + disk-id = ./disk-id; dyndns = ./dyndns; ergochat = ./ergochat; garage = ./garage; diff --git a/clanModules/machine-id/README.md b/clanModules/machine-id/README.md new file mode 100644 index 000000000..f07b8fb4b --- /dev/null +++ b/clanModules/machine-id/README.md @@ -0,0 +1,3 @@ +--- +description = "Sets the /etc/machine-id and exposes it as a nix option" +--- diff --git a/clanModules/machine-id/default.nix b/clanModules/machine-id/default.nix new file mode 100644 index 000000000..8d260c260 --- /dev/null +++ b/clanModules/machine-id/default.nix @@ -0,0 +1,45 @@ +{ + config, + pkgs, + lib, + ... +}: + +let + var = config.clan.core.vars.generators.machine-id.files.machineId or { }; +in +{ + config = lib.mkMerge [ + (lib.mkIf ((var.machineId.value or null) != null) { + assertions = [ + { + assertion = lib.stringLength var.machineId.value == 32; + message = "machineId must be exactly 32 characters long."; + } + ]; + boot.kernelParams = [ + ''systemd.machine_id=${var.machineId.value}'' + ]; + environment.etc."machine-id" = { + text = var.machineId.value; + }; + }) + { + clan.core.vars.generators.machine-id = { + files.machineId.secret = false; + runtimeInputs = [ + pkgs.coreutils + pkgs.bash + ]; + script = '' + uuid=$(bash ${../uuid4.sh}) + + # Remove the hyphens from the UUID + uuid_no_hyphens=$(echo -n "$uuid" | tr -d '-') + + echo -n "$uuid_no_hyphens" > "$out/machineId" + ''; + }; + } + ]; +} diff --git a/clanModules/machine-id/roles/default.nix b/clanModules/machine-id/roles/default.nix new file mode 100644 index 000000000..ffcd4415b --- /dev/null +++ b/clanModules/machine-id/roles/default.nix @@ -0,0 +1 @@ +{ } diff --git a/clanModules/uuid4.sh b/clanModules/uuid4.sh new file mode 100644 index 000000000..ca3a7c47e --- /dev/null +++ b/clanModules/uuid4.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash + +# Read 16 bytes from /dev/urandom +uuid=$(dd if=/dev/urandom bs=1 count=16 2>/dev/null | od -An -tx1 | tr -d ' \n') + +# Break the UUID into pieces and apply the required modifications +byte6=${uuid:12:2} +byte8=${uuid:16:2} + +# Construct the correct version and variant +hex_byte6=$(printf "%x" $((0x$byte6 & 0x0F | 0x40))) +hex_byte8=$(printf "%x" $((0x$byte8 & 0x3F | 0x80))) + +# Rebuild the UUID with the correct fields +uuid_v4="${uuid:0:12}${hex_byte6}${uuid:14:2}${hex_byte8}${uuid:18:14}" + +# Format the UUID correctly 8-4-4-4-12 +uuid_formatted="${uuid_v4:0:8}-${uuid_v4:8:4}-${uuid_v4:12:4}-${uuid_v4:16:4}-${uuid_v4:20:12}" + +echo -n "$uuid_formatted" \ No newline at end of file diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index f6fd03a79..33303f627 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -60,6 +60,7 @@ nav: - reference/clanModules/borgbackup.md - reference/clanModules/deltachat.md - reference/clanModules/dyndns.md + - reference/clanModules/disk-id.md - reference/clanModules/ergochat.md - reference/clanModules/garage.md - reference/clanModules/golem-provider.md @@ -69,6 +70,7 @@ nav: - reference/clanModules/localbackup.md - reference/clanModules/localsend.md - reference/clanModules/matrix-synapse.md + - reference/clanModules/machine-id.md - reference/clanModules/moonlight.md - reference/clanModules/mumble.md - reference/clanModules/nginx.md diff --git a/docs/site/getting-started/disk-encryption.md b/docs/site/getting-started/disk-encryption.md index 8217ef761..b771a43c5 100644 --- a/docs/site/getting-started/disk-encryption.md +++ b/docs/site/getting-started/disk-encryption.md @@ -14,183 +14,202 @@ lsblk --output NAME,ID-LINK,FSTYPE,SIZE,MOUNTPOINT === "**Single Disk**" Below is the configuration for `disko.nix` - ```nix hl_lines="14 40" - { lib, ... }: - let - mirrorBoot = idx: { - type = "disk"; - device = "/dev/disk/by-id/${idx}"; - content = { + ```nix hl_lines="16 47" + { lib, clan-core, ... }: + let + suffix = config.clan.core.vars.generators.disk-id.files.diskId.value; + mirrorBoot = idx: { + # suffix is to prevent disk name collisions + name = idx + suffix; + type = "disk"; + device = "/dev/disk/by-id/${idx}"; + content = { type = "gpt"; partitions = { - "${config.networking.hostName}-boot" = { - size = "1M"; - type = "EF02"; # for grub MBR - priority = 1; + "boot" = { + size = "1M"; + type = "EF02"; # for grub MBR + priority = 1; + }; + "ESP" = lib.mkIf (idx == "nvme-eui.002538b931b59865") { + size = "1G"; + type = "EF00"; + content = { + type = "filesystem"; + format = "vfat"; + mountpoint = "/boot"; + mountOptions = [ "nofail" ]; }; - "${config.networking.hostName}-ESP" = lib.mkIf (idx == "nvme-eui.002538b931b59865") { - size = "1G"; - type = "EF00"; - content = { - type = "filesystem"; - format = "vfat"; - mountpoint = "/boot"; - mountOptions = [ "nofail" ]; - }; - }; - "${config.networking.hostName}-root" = { - size = "100%"; - content = { - type = "zfs"; - pool = "zroot"; - }; + }; + "root" = { + size = "100%"; + content = { + type = "zfs"; + pool = "zroot"; }; + }; }; + }; }; - }; - in - { - boot.loader.systemd-boot.enable = true; + in + { + imports = [ + clan-core.clanModules.disk-id + ]; - disko.devices = { - disk = { - x = mirrorBoot "nvme-eui.002538b931b59865"; - }; - zpool = { - zroot = { + config = { + boot.loader.systemd-boot.enable = true; + + disko.devices = { + disk = { + x = mirrorBoot "nvme-eui.002538b931b59865"; + }; + zpool = { + zroot = { type = "zpool"; rootFsOptions = { - compression = "lz4"; - acltype = "posixacl"; - xattr = "sa"; - "com.sun:auto-snapshot" = "true"; - mountpoint = "none"; + compression = "lz4"; + acltype = "posixacl"; + xattr = "sa"; + "com.sun:auto-snapshot" = "true"; + mountpoint = "none"; }; datasets = { - "root" = { - type = "zfs_fs"; - options = { - mountpoint = "none"; - encryption = "aes-256-gcm"; - keyformat = "passphrase"; - keylocation = "file:///tmp/secret.key"; - }; + "root" = { + type = "zfs_fs"; + options = { + mountpoint = "none"; + encryption = "aes-256-gcm"; + keyformat = "passphrase"; + keylocation = "file:///tmp/secret.key"; }; - "root/nixos" = { - type = "zfs_fs"; - options.mountpoint = "/"; - mountpoint = "/"; - }; - "root/home" = { - type = "zfs_fs"; - options.mountpoint = "/home"; - mountpoint = "/home"; - }; - "root/tmp" = { - type = "zfs_fs"; - mountpoint = "/tmp"; - options = { - mountpoint = "/tmp"; - sync = "disabled"; - }; + }; + "root/nixos" = { + type = "zfs_fs"; + options.mountpoint = "/"; + mountpoint = "/"; + }; + "root/home" = { + type = "zfs_fs"; + options.mountpoint = "/home"; + mountpoint = "/home"; + }; + "root/tmp" = { + type = "zfs_fs"; + mountpoint = "/tmp"; + options = { + mountpoint = "/tmp"; + sync = "disabled"; }; + }; }; + }; }; + }; }; - }; - } + } + ``` === "**Raid 1**" Below is the configuration for `disko.nix` - ```nix hl_lines="14 40 41" - { lib, ... }: - let - mirrorBoot = idx: { - type = "disk"; - device = "/dev/disk/by-id/${idx}"; - content = { + ```nix hl_lines="16 47 48" + { lib, clan-core, ... }: + let + suffix = config.clan.core.vars.generators.disk-id.files.diskId.value; + mirrorBoot = idx: { + # suffix is to prevent disk name collisions + name = idx + suffix; + type = "disk"; + device = "/dev/disk/by-id/${idx}"; + content = { type = "gpt"; partitions = { - boot = { - size = "1M"; - type = "EF02"; # for grub MBR - priority = 1; + "boot" = { + size = "1M"; + type = "EF02"; # for grub MBR + priority = 1; + }; + "ESP" = lib.mkIf (idx == "nvme-eui.002538b931b59865") { + size = "1G"; + type = "EF00"; + content = { + type = "filesystem"; + format = "vfat"; + mountpoint = "/boot"; + mountOptions = [ "nofail" ]; }; - ESP = lib.mkIf (idx == "nvme-eui.002538b931b59865") { - size = "1G"; - type = "EF00"; - content = { - type = "filesystem"; - format = "vfat"; - mountpoint = "/boot"; - mountOptions = [ "nofail" ]; - }; - }; - zfs = { - size = "100%"; - content = { - type = "zfs"; - pool = "zroot"; - }; + }; + "root" = { + size = "100%"; + content = { + type = "zfs"; + pool = "zroot"; }; + }; }; + }; }; - }; - in - { - boot.loader.systemd-boot.enable = true; + in + { + imports = [ + clan-core.clanModules.disk-id + ]; - disko.devices = { - disk = { - x = mirrorBoot "nvme-eui.002538b931b59865"; - y = mirrorBoot "myOtherDrive" - }; - zpool = { - zroot = { + config = { + boot.loader.systemd-boot.enable = true; + + disko.devices = { + disk = { + x = mirrorBoot "nvme-eui.002538b931b59865"; + y = mirrorBoot "my-other-disk"; + }; + zpool = { + zroot = { type = "zpool"; rootFsOptions = { - compression = "lz4"; - acltype = "posixacl"; - xattr = "sa"; - "com.sun:auto-snapshot" = "true"; - mountpoint = "none"; + compression = "lz4"; + acltype = "posixacl"; + xattr = "sa"; + "com.sun:auto-snapshot" = "true"; + mountpoint = "none"; }; datasets = { - "root" = { - type = "zfs_fs"; - options = { - mountpoint = "none"; - encryption = "aes-256-gcm"; - keyformat = "passphrase"; - keylocation = "file:///tmp/secret.key"; - }; + "root" = { + type = "zfs_fs"; + options = { + mountpoint = "none"; + encryption = "aes-256-gcm"; + keyformat = "passphrase"; + keylocation = "file:///tmp/secret.key"; }; - "root/nixos" = { - type = "zfs_fs"; - options.mountpoint = "/"; - mountpoint = "/"; - }; - "root/home" = { - type = "zfs_fs"; - options.mountpoint = "/home"; - mountpoint = "/home"; - }; - "root/tmp" = { - type = "zfs_fs"; - mountpoint = "/tmp"; - options = { - mountpoint = "/tmp"; - sync = "disabled"; - }; + }; + "root/nixos" = { + type = "zfs_fs"; + options.mountpoint = "/"; + mountpoint = "/"; + }; + "root/home" = { + type = "zfs_fs"; + options.mountpoint = "/home"; + mountpoint = "/home"; + }; + "root/tmp" = { + type = "zfs_fs"; + mountpoint = "/tmp"; + options = { + mountpoint = "/tmp"; + sync = "disabled"; }; + }; }; + }; }; + }; }; - }; - } + } ``` Below is the configuration for `initrd.nix`. diff --git a/lib/eval-clan-modules/default.nix b/lib/eval-clan-modules/default.nix index ccef35868..50c5c2c9b 100644 --- a/lib/eval-clan-modules/default.nix +++ b/lib/eval-clan-modules/default.nix @@ -25,6 +25,9 @@ let evaled = lib.evalModules { modules = [ baseModule + { + clan.core.clanDir = ./.; + } clan-core.nixosModules.clanCore ] ++ (map (name: clanModules.${name}) modulenames); }; diff --git a/lib/inventory/build-inventory/interface.nix b/lib/inventory/build-inventory/interface.nix index 133e65c58..d4b26338d 100644 --- a/lib/inventory/build-inventory/interface.nix +++ b/lib/inventory/build-inventory/interface.nix @@ -23,27 +23,27 @@ let description = '' List of imported '.nix' files. - Each filename must be a string and is interpreted relative to the 'directory' passed to buildClan. - The import only happens if the machine is part of the service or role. + Each filename must be a string and is interpreted relative to the 'directory' passed to buildClan. + The import only happens if the machine is part of the service or role. - ## Example + ## Example - To import the `special.nix` file + To import the `special.nix` file - ``` - . Clan Directory - ├── flake.nix - ... - └── modules - ├── special.nix - └── ... - ``` + ``` + . Clan Directory + ├── flake.nix + ... + └── modules + ├── special.nix + └── ... + ``` - ```nix - { - imports = [ "modules/special.nix" ]; - } - ``` + ```nix + { + imports = [ "modules/special.nix" ]; + } + ``` ''; default = [ ]; diff --git a/pkgs/clan-cli/clan_cli/inventory/update.sh b/pkgs/clan-cli/clan_cli/inventory/update.sh new file mode 100755 index 000000000..5d1bf5214 --- /dev/null +++ b/pkgs/clan-cli/clan_cli/inventory/update.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +jsonSchema=$(nix build .#inventory-schema --print-out-paths)/schema.json +nix run .#classgen "$jsonSchema" "$PKG_ROOT/clan_cli/inventory/classes.py" \ No newline at end of file diff --git a/pkgs/installer/flake-module.nix b/pkgs/installer/flake-module.nix index 04cfb96ce..ba425d7f3 100644 --- a/pkgs/installer/flake-module.nix +++ b/pkgs/installer/flake-module.nix @@ -1,54 +1,54 @@ -{ self, lib, ... }: +{ + self, + lib, + ... +}: let - flashInstallerModule = { config, ... }: + { imports = [ ./iwd.nix self.nixosModules.installer - # Allow to download pre-build binaries from our nix caches self.clanModules.trusted-nix-caches ]; system.stateVersion = config.system.nixos.version; nixpkgs.pkgs = self.inputs.nixpkgs.legacyPackages.x86_64-linux; - } - // flashDiskoConfig; - # Important: The partition names need to be different to the clan install - flashDiskoConfig = { - boot.loader.grub.efiSupport = lib.mkDefault true; - boot.loader.grub.efiInstallAsRemovable = lib.mkDefault true; - disko.devices = { - disk = { - main = { - type = "disk"; - device = lib.mkDefault "/dev/null"; - content = { - type = "gpt"; - partitions = { - installer-boot = { - size = "1M"; - type = "EF02"; # for grub MBR - priority = 1; - }; - installer-ESP = { - size = "512M"; - type = "EF00"; - content = { - type = "filesystem"; - format = "vfat"; - mountpoint = "/boot"; + boot.loader.grub.efiSupport = lib.mkDefault true; + boot.loader.grub.efiInstallAsRemovable = lib.mkDefault true; + disko.devices = { + disk = { + "main" = { + type = "disk"; + device = lib.mkDefault "/dev/null"; + content = { + type = "gpt"; + partitions = { + "boot" = { + size = "1M"; + type = "EF02"; # for grub MBR + priority = 1; }; - }; - installer-root = { - size = "100%"; - content = { - type = "filesystem"; - format = "ext4"; - mountpoint = "/"; + "ESP" = { + size = "512M"; + type = "EF00"; + content = { + type = "filesystem"; + format = "vfat"; + mountpoint = "/boot"; + }; + }; + "root" = { + size = "100%"; + content = { + type = "filesystem"; + format = "ext4"; + mountpoint = "/"; + }; }; }; }; @@ -56,7 +56,7 @@ let }; }; }; - }; + in { clan = { diff --git a/templates/flake-parts/modules/disko.nix b/templates/flake-parts/modules/disko.nix index 474a31c39..5bc1e84bd 100644 --- a/templates/flake-parts/modules/disko.nix +++ b/templates/flake-parts/modules/disko.nix @@ -1,10 +1,20 @@ -{ lib, ... }: +{ lib, clan-core, ... }: + +let + suffix = config.clan.core.vars.generators.disk-id.files.diskId.value; +in { + imports = [ + clan-core.clanModules.disk-id + ]; + boot.loader.grub.efiSupport = lib.mkDefault true; boot.loader.grub.efiInstallAsRemovable = lib.mkDefault true; disko.devices = { disk = { - main = { + "main" = { + # suffix is to prevent disk name collisions + name = "main-" + suffix; type = "disk"; # Set the following in flake.nix for each maschine: # device = ; diff --git a/templates/new-clan/modules/disko.nix b/templates/new-clan/modules/disko.nix index b98423bc5..a04142acf 100644 --- a/templates/new-clan/modules/disko.nix +++ b/templates/new-clan/modules/disko.nix @@ -1,12 +1,22 @@ -{ lib, ... }: +{ lib, clan-core, ... }: + +let + suffix = config.clan.core.vars.generators.disk-id.files.diskId.value; +in { + imports = [ + clan-core.clanModules.disk-id + ]; + # TO NOT EDIT THIS FILE AFTER INSTALLATION of a machine # Otherwise your system might not boot because of missing partitions / filesystems boot.loader.grub.efiSupport = lib.mkDefault true; boot.loader.grub.efiInstallAsRemovable = lib.mkDefault true; disko.devices = { disk = { - main = { + "main" = { + # suffix is to prevent disk name collisions + name = "main-" + suffix; type = "disk"; # Set the following in flake.nix for each maschine: # device = ;