clanCore: init machineId and diskId v2

This commit is contained in:
Qubasa
2024-09-09 15:51:31 +02:00
parent 7af3e80249
commit a0b3275ed1
28 changed files with 543 additions and 252 deletions

View File

@@ -18,6 +18,7 @@
{ {
clan.core.machineName = "machine"; clan.core.machineName = "machine";
clan.core.clanDir = ./.; clan.core.clanDir = ./.;
clan.core.state.testState.folders = [ "/etc/state" ]; clan.core.state.testState.folders = [ "/etc/state" ];
environment.etc.state.text = "hello world"; environment.etc.state.text = "hello world";
systemd.tmpfiles.settings."vmsecrets" = { systemd.tmpfiles.settings."vmsecrets" = {

View File

@@ -9,6 +9,7 @@
networking.hostName = "machine"; networking.hostName = "machine";
services.openssh.enable = true; services.openssh.enable = true;
services.openssh.startWhenNeeded = false; services.openssh.startWhenNeeded = false;
}; };
testScript = '' testScript = ''
start_all() start_all()

View File

@@ -13,10 +13,14 @@
{ {
imports = [ imports = [
self.clanModules.single-disk self.clanModules.single-disk
self.clanModules.factless
(modulesPath + "/testing/test-instrumentation.nix") # we need these 2 modules always to be able to run the tests (modulesPath + "/testing/test-instrumentation.nix") # we need these 2 modules always to be able to run the tests
(modulesPath + "/profiles/qemu-guest.nix") (modulesPath + "/profiles/qemu-guest.nix")
]; ];
clan.single-disk.device = "/dev/vdb"; clan.single-disk.device = "/dev/vdb";
clan.factless = {
diskId = "ac51e4623c804dcbbce0144ed8e16e55";
};
environment.etc."install-successful".text = "ok"; environment.etc."install-successful".text = "ok";

1
checks/lib/age/privkey Normal file
View File

@@ -0,0 +1 @@
AGE-SECRET-KEY-1KF8E3SR3TTGL6M476SKF7EEMR4H9NF7ZWYSLJUAK8JX276JC7KUSSURKFK

1
checks/lib/age/pubkey Normal file
View File

@@ -0,0 +1 @@
age1dhwqzkah943xzc34tc3dlmfayyevcmdmxzjezdgdy33euxwf59vsp3vk3c

View File

@@ -32,6 +32,7 @@
common common
{ {
clan.core.machineName = "peer1"; clan.core.machineName = "peer1";
environment.etc = { environment.etc = {
"mumble-key".source = ./peer_1/peer_1_test_key; "mumble-key".source = ./peer_1/peer_1_test_key;
"mumble-cert".source = ./peer_1/peer_1_test_cert; "mumble-cert".source = ./peer_1/peer_1_test_cert;

View File

@@ -8,7 +8,9 @@ let
self.nixosModules.clanCore self.nixosModules.clanCore
# This is the only option that is not part of the # This is the only option that is not part of the
# module because it is usually set by flake-parts # module because it is usually set by flake-parts
{ clan.core.clanDir = ./.; } {
clan.core.clanDir = ./.;
}
]; ];
}; };
in in

View File

@@ -0,0 +1,5 @@
---
description = "Disables early fact generation in clanCore"
---
This module isn't meant for everyone. It's made for internal use or for those who prefer not to create facts for a machine.

View File

@@ -0,0 +1,33 @@
{
config,
lib,
...
}:
let
cfg = config.clan.factless;
in
{
# These options are reexported here to allow the inventory to set them
options.clan.factless = {
machineId = lib.mkOption {
type = lib.types.nullOr lib.types.str;
description = "A machine id based on the UUID v4 format";
default = null;
};
diskId = lib.mkOption {
type = lib.types.nullOr lib.types.str;
description = "The disk id, changing this will require a new installation";
default = null;
};
};
config = {
clan.core.machine = {
generateMachineId = false;
generateDiskId = false;
machineId = cfg.machineId;
diskId = cfg.diskId;
};
};
}

View File

@@ -0,0 +1 @@
{ }

View File

@@ -7,6 +7,7 @@
deltachat = ./deltachat; deltachat = ./deltachat;
dyndns = ./dyndns; dyndns = ./dyndns;
ergochat = ./ergochat; ergochat = ./ergochat;
factless = ./factless;
garage = ./garage; garage = ./garage;
golem-provider = ./golem-provider; golem-provider = ./golem-provider;
heisenbridge = ./heisenbridge; heisenbridge = ./heisenbridge;

View File

@@ -1,33 +1,45 @@
{ lib, config, ... }: { lib, config, ... }:
let
cfg = config.clan.single-disk;
suffix = config.clan.core.machine.diskId;
in
{ {
options.clan.single-disk = { options.clan.single-disk = {
device = lib.mkOption { device = lib.mkOption {
default = null; default = null;
type = lib.types.nullOr lib.types.str; type = lib.types.nullOr lib.types.str;
description = "The primary disk device to install the system on"; description = "The primary disk device to install the system on";
# Question: should we set a default here?
# default = "/dev/null";
}; };
}; };
config = { config = lib.mkMerge [
({
assertions = [
{
assertion = suffix != null;
message = "Please run `clan facts generate` or import `clanModules.factless`";
}
];
})
(lib.mkIf (suffix != null) {
boot.loader.grub.efiSupport = lib.mkDefault true; boot.loader.grub.efiSupport = lib.mkDefault true;
boot.loader.grub.efiInstallAsRemovable = lib.mkDefault true; boot.loader.grub.efiInstallAsRemovable = lib.mkDefault true;
disko.devices = { disko.devices = {
disk = { disk = {
main = { "main" = {
name = suffix;
type = "disk"; type = "disk";
# This is set through the UI # This is set through the UI
device = config.clan.single-disk.device; device = cfg.device;
content = { content = {
type = "gpt"; type = "gpt";
partitions = { partitions = {
"${config.networking.hostName}-boot" = { "boot" = {
size = "1M"; size = "1M";
type = "EF02"; # for grub MBR type = "EF02"; # for grub MBR
priority = 1; priority = 1;
}; };
"${config.networking.hostName}-ESP" = { "ESP" = {
size = "512M"; size = "512M";
type = "EF00"; type = "EF00";
content = { content = {
@@ -36,7 +48,7 @@
mountpoint = "/boot"; mountpoint = "/boot";
}; };
}; };
"${config.networking.hostName}-root" = { "root" = {
size = "100%"; size = "100%";
content = { content = {
type = "filesystem"; type = "filesystem";
@@ -49,5 +61,6 @@
}; };
}; };
}; };
}; })
];
} }

View File

@@ -63,6 +63,7 @@ nav:
- reference/clanModules/nginx.md - reference/clanModules/nginx.md
- reference/clanModules/vaultwarden.md - reference/clanModules/vaultwarden.md
- reference/clanModules/ergochat.md - reference/clanModules/ergochat.md
- reference/clanModules/factless.md
- reference/clanModules/garage.md - reference/clanModules/garage.md
- reference/clanModules/golem-provider.md - reference/clanModules/golem-provider.md
- reference/clanModules/index.md - reference/clanModules/index.md
@@ -108,6 +109,7 @@ nav:
- reference/clan-core/sops.md - reference/clan-core/sops.md
- reference/clan-core/state.md - reference/clan-core/state.md
- reference/clan-core/deployment.md - reference/clan-core/deployment.md
- reference/clan-core/machine.md
- reference/clan-core/networking.md - reference/clan-core/networking.md
- Nix API: - Nix API:
- reference/nix-api/index.md - reference/nix-api/index.md

View File

@@ -14,21 +14,23 @@ lsblk --output NAME,ID-LINK,FSTYPE,SIZE,MOUNTPOINT
=== "**Single Disk**" === "**Single Disk**"
Below is the configuration for `disko.nix` Below is the configuration for `disko.nix`
```nix hl_lines="14 40" ```nix hl_lines="16 43"
{ lib, ... }: { lib, ... }:
let let
suffix = config.clan.core.machine.diskId;
mirrorBoot = idx: { mirrorBoot = idx: {
name = suffix;
type = "disk"; type = "disk";
device = "/dev/disk/by-id/${idx}"; device = "/dev/disk/by-id/${idx}";
content = { content = {
type = "gpt"; type = "gpt";
partitions = { partitions = {
"${config.networking.hostName}-boot" = { "boot" = {
size = "1M"; size = "1M";
type = "EF02"; # for grub MBR type = "EF02"; # for grub MBR
priority = 1; priority = 1;
}; };
"${config.networking.hostName}-ESP" = lib.mkIf (idx == "nvme-eui.002538b931b59865") { "ESP" = lib.mkIf (idx == "nvme-eui.002538b931b59865") {
size = "1G"; size = "1G";
type = "EF00"; type = "EF00";
content = { content = {
@@ -38,7 +40,7 @@ lsblk --output NAME,ID-LINK,FSTYPE,SIZE,MOUNTPOINT
mountOptions = [ "nofail" ]; mountOptions = [ "nofail" ];
}; };
}; };
"${config.networking.hostName}-root" = { "root" = {
size = "100%"; size = "100%";
content = { content = {
type = "zfs"; type = "zfs";
@@ -48,8 +50,9 @@ lsblk --output NAME,ID-LINK,FSTYPE,SIZE,MOUNTPOINT
}; };
}; };
}; };
in in
{ {
config = {
boot.loader.systemd-boot.enable = true; boot.loader.systemd-boot.enable = true;
disko.devices = { disko.devices = {
@@ -98,28 +101,32 @@ lsblk --output NAME,ID-LINK,FSTYPE,SIZE,MOUNTPOINT
}; };
}; };
}; };
} };
}
``` ```
=== "**Raid 1**" === "**Raid 1**"
Below is the configuration for `disko.nix` Below is the configuration for `disko.nix`
```nix hl_lines="14 40 41" ```nix hl_lines="16 43 44"
{ lib, ... }: { lib, ... }:
let let
suffix = config.clan.core.machine.diskId;
mirrorBoot = idx: { mirrorBoot = idx: {
name = suffix;
type = "disk"; type = "disk";
device = "/dev/disk/by-id/${idx}"; device = "/dev/disk/by-id/${idx}";
content = { content = {
type = "gpt"; type = "gpt";
partitions = { partitions = {
boot = { "boot" = {
size = "1M"; size = "1M";
type = "EF02"; # for grub MBR type = "EF02"; # for grub MBR
priority = 1; priority = 1;
}; };
ESP = lib.mkIf (idx == "nvme-eui.002538b931b59865") { "ESP" = lib.mkIf (idx == "nvme-eui.002538b931b59865") {
size = "1G"; size = "1G";
type = "EF00"; type = "EF00";
content = { content = {
@@ -129,7 +136,7 @@ lsblk --output NAME,ID-LINK,FSTYPE,SIZE,MOUNTPOINT
mountOptions = [ "nofail" ]; mountOptions = [ "nofail" ];
}; };
}; };
zfs = { "root" = {
size = "100%"; size = "100%";
content = { content = {
type = "zfs"; type = "zfs";
@@ -139,14 +146,15 @@ lsblk --output NAME,ID-LINK,FSTYPE,SIZE,MOUNTPOINT
}; };
}; };
}; };
in in
{ {
config = {
boot.loader.systemd-boot.enable = true; boot.loader.systemd-boot.enable = true;
disko.devices = { disko.devices = {
disk = { disk = {
x = mirrorBoot "nvme-eui.002538b931b59865"; x = mirrorBoot "nvme-eui.002538b931b59865";
y = mirrorBoot "myOtherDrive" y = mirrorBoot "my-other-disk";
}; };
zpool = { zpool = {
zroot = { zroot = {
@@ -190,7 +198,8 @@ lsblk --output NAME,ID-LINK,FSTYPE,SIZE,MOUNTPOINT
}; };
}; };
}; };
} };
}
``` ```
Below is the configuration for `initrd.nix`. Below is the configuration for `initrd.nix`.

View File

@@ -68,6 +68,34 @@
} }
} }
}, },
"factless": {
"default": {
"meta": {
"name": "factless",
"description": null,
"icon": null
},
"roles": {
"default": {
"config": {},
"imports": [],
"machines": ["test-inventory-machine"],
"tags": []
}
},
"config": {},
"imports": [],
"machines": {
"test-inventory-machine": {
"config": {
"diskId": "910662",
"machineId": "fe7f3ff75c844d36ac4e5383b55b5a76"
},
"imports": []
}
}
}
},
"single-disk": { "single-disk": {
"default": { "default": {
"meta": { "meta": {

View File

@@ -25,6 +25,9 @@ let
evaled = lib.evalModules { evaled = lib.evalModules {
modules = [ modules = [
baseModule baseModule
({
clan.core.clanDir = ./.;
})
clan-core.nixosModules.clanCore clan-core.nixosModules.clanCore
] ++ (map (name: clanModules.${name}) modulenames); ] ++ (map (name: clanModules.${name}) modulenames);
}; };

View File

@@ -18,5 +18,7 @@
./vm.nix ./vm.nix
./wayland-proxy-virtwl.nix ./wayland-proxy-virtwl.nix
./zerotier ./zerotier
./machine_id.nix
./disk_id.nix
]; ];
} }

View File

@@ -0,0 +1,48 @@
{
config,
pkgs,
lib,
...
}:
let
cfg = config.clan.core.machine;
facts = config.clan.core.facts.services.diskId.public or { };
in
{
options.clan.core.machine = {
diskId = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
description = "The disk id";
};
generateDiskId = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Generate a new disk id";
};
};
config = lib.mkMerge [
(lib.mkIf ((facts.diskId.value or null) != null) {
clan.core.machine.diskId = lib.mkDefault facts.diskId.value;
})
(lib.mkIf cfg.generateDiskId {
clan.core.facts.services.diskId = {
public.diskId = { };
generator.path = [
pkgs.coreutils
pkgs.bash
];
generator.script = ''
uuid=$(bash ${./uuid4.sh})
# Remove the hyphens from the UUID
uuid_no_hyphens=$(echo -n "$uuid" | tr -d '-')
echo -n "$uuid_no_hyphens" > "$facts/diskId"
'';
};
})
];
}

View File

@@ -0,0 +1,59 @@
{
config,
pkgs,
lib,
...
}:
let
cfg = config.clan.core.machine;
facts = config.clan.core.facts.services.machineId.public or { };
in
{
options.clan.core.machine = {
machineId = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
description = "A machine id based on the UUID v4 format";
};
generateMachineId = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Generate a new machine id";
};
};
config = lib.mkMerge [
(lib.mkIf (cfg.machineId != null) {
assertions = [
{
assertion = lib.stringLength cfg.machineId == 32;
message = "machineId must be exactly 32 characters long.";
}
];
boot.kernelParams = [
''systemd.machine_id=${cfg.machineId}''
];
})
(lib.mkIf ((facts.machineId.value or null) != null) {
clan.core.machine.machineId = lib.mkDefault facts.machineId.value;
})
(lib.mkIf cfg.generateMachineId {
clan.core.facts.services.machineId = {
public.machineId = { };
generator.path = [
pkgs.coreutils
pkgs.bash
];
generator.script = ''
uuid=$(bash ${./uuid4.sh})
# Remove the hyphens from the UUID
uuid_no_hyphens=$(echo -n "$uuid" | tr -d '-')
echo -n "$uuid_no_hyphens" > "$facts/machineId"
'';
};
})
];
}

View File

@@ -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"

View File

@@ -117,6 +117,39 @@ class ServiceBorgbackup:
machines: dict[str, ServiceBorgbackupMachine] = field(default_factory = dict) machines: dict[str, ServiceBorgbackupMachine] = field(default_factory = dict)
@dataclass
class FactlessConfig:
diskId: None | str = field(default = None)
machineId: None | str = field(default = None)
@dataclass
class ServiceFactlesMachine:
config: FactlessConfig = field(default_factory = FactlessConfig)
imports: list[str] = field(default_factory = list)
@dataclass
class ServiceFactlesRoleDefault:
config: FactlessConfig = field(default_factory = FactlessConfig)
imports: list[str] = field(default_factory = list)
machines: list[str] = field(default_factory = list)
tags: list[str] = field(default_factory = list)
@dataclass
class ServiceFactlesRole:
default: ServiceFactlesRoleDefault
@dataclass
class ServiceFactles:
meta: ServiceMeta
roles: ServiceFactlesRole
config: FactlessConfig = field(default_factory = FactlessConfig)
machines: dict[str, ServiceFactlesMachine] = field(default_factory = dict)
@dataclass @dataclass
class IwdConfigNetwork: class IwdConfigNetwork:
ssid: str ssid: str
@@ -222,6 +255,7 @@ class ServiceSingleDisk:
class Service: class Service:
admin: dict[str, ServiceAdmin] = field(default_factory = dict) admin: dict[str, ServiceAdmin] = field(default_factory = dict)
borgbackup: dict[str, ServiceBorgbackup] = field(default_factory = dict) borgbackup: dict[str, ServiceBorgbackup] = field(default_factory = dict)
factless: dict[str, ServiceFactles] = field(default_factory = dict)
iwd: dict[str, ServiceIwd] = field(default_factory = dict) iwd: dict[str, ServiceIwd] = field(default_factory = dict)
packages: dict[str, ServicePackage] = field(default_factory = dict) packages: dict[str, ServicePackage] = field(default_factory = dict)
single_disk: dict[str, ServiceSingleDisk] = field(default_factory = dict, metadata = {"alias": "single-disk"}) single_disk: dict[str, ServiceSingleDisk] = field(default_factory = dict, metadata = {"alias": "single-disk"})

View File

@@ -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"

View File

@@ -1,40 +1,47 @@
{ self, lib, ... }: {
self,
lib,
...
}:
let let
flashInstallerModule = flashInstallerModule =
{ config, ... }: { config, ... }:
let
suffix = config.clan.core.machine.diskId;
in
{ {
imports = [ imports = [
./iwd.nix ./iwd.nix
self.nixosModules.installer self.nixosModules.installer
# Allow to download pre-build binaries from our nix caches
self.clanModules.trusted-nix-caches self.clanModules.trusted-nix-caches
self.clanModules.factless
]; ];
clan.factless = {
diskId = "ac51e4623c804dcbbce0144ed8e16e55";
};
system.stateVersion = config.system.nixos.version; system.stateVersion = config.system.nixos.version;
nixpkgs.pkgs = self.inputs.nixpkgs.legacyPackages.x86_64-linux; 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.efiSupport = lib.mkDefault true;
boot.loader.grub.efiInstallAsRemovable = lib.mkDefault true; boot.loader.grub.efiInstallAsRemovable = lib.mkDefault true;
disko.devices = { disko.devices = {
disk = { disk = {
main = { "main" = {
name = suffix;
type = "disk"; type = "disk";
device = lib.mkDefault "/dev/null"; device = lib.mkDefault "/dev/null";
content = { content = {
type = "gpt"; type = "gpt";
partitions = { partitions = {
installer-boot = { "boot" = {
size = "1M"; size = "1M";
type = "EF02"; # for grub MBR type = "EF02"; # for grub MBR
priority = 1; priority = 1;
}; };
installer-ESP = { "ESP" = {
size = "512M"; size = "512M";
type = "EF00"; type = "EF00";
content = { content = {
@@ -43,7 +50,7 @@ let
mountpoint = "/boot"; mountpoint = "/boot";
}; };
}; };
installer-root = { "root" = {
size = "100%"; size = "100%";
content = { content = {
type = "filesystem"; type = "filesystem";
@@ -57,6 +64,7 @@ let
}; };
}; };
}; };
in in
{ {
clan = { clan = {

View File

@@ -112,15 +112,15 @@ const InstallMachine = (props: InstallMachineProps) => {
e.preventDefault(); e.preventDefault();
const curr_uri = activeURI(); const curr_uri = activeURI();
const disk = getValue(formStore, "disk"); const disk = getValue(formStore, "disk");
const disk_id = props.disks.find((d) => d.name === disk)?.id_link; const diskId = props.disks.find((d) => d.name === disk)?.id_link;
if (!curr_uri || !disk_id || !props.name) { if (!curr_uri || !diskId || !props.name) {
return; return;
} }
const r = await callApi("set_single_disk_uuid", { const r = await callApi("set_single_disk_uuid", {
base_path: curr_uri, base_path: curr_uri,
machine_name: props.name, machine_name: props.name,
disk_uuid: disk_id, disk_uuid: diskId,
}); });
if (r.status === "error") { if (r.status === "error") {
toast.error("Failed to set disk"); toast.error("Failed to set disk");

View File

@@ -1,10 +1,15 @@
{ lib, ... }: { lib, ... }:
let
suffix = config.clan.core.machine.diskId;
in
{ {
boot.loader.grub.efiSupport = lib.mkDefault true; boot.loader.grub.efiSupport = lib.mkDefault true;
boot.loader.grub.efiInstallAsRemovable = lib.mkDefault true; boot.loader.grub.efiInstallAsRemovable = lib.mkDefault true;
disko.devices = { disko.devices = {
disk = { disk = {
main = { "main" = {
name = suffix;
type = "disk"; type = "disk";
# Set the following in flake.nix for each maschine: # Set the following in flake.nix for each maschine:
# device = <uuid>; # device = <uuid>;

View File

@@ -1,4 +1,8 @@
{ lib, ... }: { lib, ... }:
let
suffix = config.clan.core.machine.diskId;
in
{ {
# TO NOT EDIT THIS FILE AFTER INSTALLATION of a machine # TO NOT EDIT THIS FILE AFTER INSTALLATION of a machine
# Otherwise your system might not boot because of missing partitions / filesystems # Otherwise your system might not boot because of missing partitions / filesystems
@@ -6,7 +10,8 @@
boot.loader.grub.efiInstallAsRemovable = lib.mkDefault true; boot.loader.grub.efiInstallAsRemovable = lib.mkDefault true;
disko.devices = { disko.devices = {
disk = { disk = {
main = { "main" = {
name = suffix;
type = "disk"; type = "disk";
# Set the following in flake.nix for each maschine: # Set the following in flake.nix for each maschine:
# device = <uuid>; # device = <uuid>;