Compare commits

..

No commits in common. "cf98cb7880851e980567b1c245befd4e3daacc4e" and "396e8121d03be47a940c0456ac36bbb55412cd62" have entirely different histories.

6 changed files with 109 additions and 115 deletions

View file

@ -9,38 +9,28 @@ Machines are configured to act as build servers / binary caches for each other i
### Onboarding a device as a build client ### Onboarding a device as a build client
1. Generate a key pair on the device: 1. Generate a key pair on the device:
```sh
sudo ssh-keygen -t ed25519 -f /etc/nix/distributed-build-key -N "" -C "$(hostname)-nix-builds" && sudo cat /etc/nix/distributed-build-key.pub
``` ```
sudo ssh-keygen -t ed25519 -f /etc/nix/distributed-build-key -N "" -C "$(hostname)-nix-builds"
```
2. Add the public key to the device entry in `devices.nix`: 2. Add the public key to the device entry in `devices.nix`:
```nix ```nix
distributedBuilds.clientPublicKey = "ssh-ed25519 AAAA... <hostname>-nix-builds"; distributedBuilds.clientPublicKey = "ssh-ed25519 AAAA... <hostname>-nix-builds";
``` ```
3. Rebuild all build machines so they pick up the new authorized key. 3. Rebuild all build machines so they pick up the new authorized key.
### Adding a build server ### Adding a build server
1. Add to its entry in `devices.nix`: 1. Add to its entry in `devices.nix`:
```nix ```nix
distributedBuilds.isBuilder = true; distributedBuilds.isBuilder = true;
distributedBuilds.hostPublicKey = "ssh-ed25519 AAAA..."; # from: ssh-keyscan -t ed25519 "$(hostname)" distributedBuilds.hostPublicKey = "ssh-ed25519 AAAA..."; # from: ssh-keyscan -t ed25519 <hostname>
``` ```
2. Generate a store signing key on the builder: 2. Generate a store signing key on the builder:
```
```sh
sudo nix key generate-secret --key-name "$(hostname)" | sudo tee /etc/nix/signing-key.sec | sudo nix key convert-secret-to-public sudo nix key generate-secret --key-name "$(hostname)" | sudo tee /etc/nix/signing-key.sec | sudo nix key convert-secret-to-public
``` ```
3. Add the printed public key to `devices.nix`: 3. Add the printed public key to `devices.nix`:
```nix ```nix
distributedBuilds.storeSigningPublicKey = "<hostname>:<base64...>"; distributedBuilds.storeSigningPublicKey = "<hostname>:<base64...>";
``` ```
4. Rebuild all machines so they trust the new signing key. 4. Rebuild all machines so they trust the new signing key.

View file

@ -26,12 +26,6 @@ in
}; };
forgejo-runner-1 = { forgejo-runner-1 = {
system = "aarch64-linux"; system = "aarch64-linux";
distributedBuilds = {
isBuilder = true;
clientPublicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK0NLgg0sFobBWz/bjYs9WkrMvlcvJC5F6+3jQ/b+AnD forgejo-runner-1-nix-builds";
hostPublicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIANGC89GiT5xCsFICwrharrbV3q7acWHqk6ZwOUXbtGT";
storeSigningPublicKey = "forgejo-runner-1:ln1FVLL8G5+IveQuBi/Kn3SaqFZ1gaiQrE3yPlMhCMA=";
};
}; };
hetzner-vpn2 = { hetzner-vpn2 = {
system = "aarch64-linux"; system = "aarch64-linux";

View file

@ -146,11 +146,7 @@
niri = niri.overlays.niri; niri = niri.overlays.niri;
}; };
nixosModules = (importModuleDir ./nixosModules) // { nixosModules = importModuleDir ./nixosModules;
default = {
imports = builtins.attrValues (builtins.removeAttrs self.nixosModules [ "default" ]);
};
};
homeModules = importModuleDir ./homeModules; homeModules = importModuleDir ./homeModules;
homeConfigurations = { homeConfigurations = {

View file

@ -3,7 +3,7 @@
lib, lib,
}: }:
let let
allDevices = import ./devices.nix { inherit (inputs) self; }; devices = import ./devices.nix { inherit (inputs) self; };
inherit (inputs) inherit (inputs)
self self
home-manager home-manager
@ -15,7 +15,7 @@ let
stylix stylix
zerforschen-plus zerforschen-plus
; ;
forDevice = f: lib.mapAttrs (device: value: f (value // { inherit device; })) allDevices; forDevice = f: lib.mapAttrs (device: value: f (value // { inherit device; })) devices;
in in
forDevice ( forDevice (
{ {
@ -24,15 +24,10 @@ forDevice (
home-manager-users ? { }, home-manager-users ? { },
nixosSystem ? inputs.nixpkgs.lib.nixosSystem, nixosSystem ? inputs.nixpkgs.lib.nixosSystem,
... ...
}@thisDevice: }:
let let
specialArgs = inputs // { specialArgs = inputs // {
inherit inherit device home-manager-users devices;
device
home-manager-users
allDevices
thisDevice
;
}; };
in in
nixosSystem { nixosSystem {

39
nixosModules/default.nix Normal file
View file

@ -0,0 +1,39 @@
{ ... }:
{
imports = [
# keep-sorted start
./allowed-unfree-list.nix
./amd-graphics.nix
./autoupdate.nix
./distributed-builds.nix
./en-de.nix
./extra-caches.nix
./firmware-updates.nix
./git.nix
./globalinstalls.nix
./gnome.nix
./intel-graphics.nix
./kdeconnect.nix
./latex.nix
./lix-is-nix.nix
./modern-desktop.nix
./muede-desktop-settings.nix
./nix-ld.nix
./nixpkgs-overlays.nix
./openssh.nix
./podman.nix
./printing.nix
./prometheus-node.nix
./pxvirt-guest.nix
./quiet-boot.nix
./secure-boot.nix
./steam.nix
./stylix.nix
./systemd-boot.nix
./tailscale.nix
./user-muede.nix
./user-ronja.nix
./wine-gaming.nix
# keep-sorted end
];
}

View file

@ -1,27 +1,24 @@
{ {
config, config,
lib, lib,
allDevices, devices,
thisDevice,
... ...
}: }:
let let
clientSshKeyPath = "/etc/nix/distributed-build-key"; sshKeyPath = "/etc/nix/distributed-build-key";
buildUser = "remotebuild"; buildUser = "remotebuild";
# Collect all per-device public keys that have been registered. # Collect all per-device public keys that have been registered.
allClientPublicKeys = lib.pipe allDevices [ authorizedPublicKeys = lib.pipe devices [
(lib.filterAttrs (_: v: (v.distributedBuilds or { }) ? clientPublicKey)) (lib.filterAttrs (_: v: (v.distributedBuilds or { }) ? clientPublicKey))
(lib.mapAttrsToList (_: v: v.distributedBuilds.clientPublicKey)) (lib.mapAttrsToList (_: v: v.distributedBuilds.clientPublicKey))
]; ];
isClient = (thisDevice.distributedBuilds or { }) ? clientPublicKey;
buildServerDevices = lib.filterAttrs ( buildServerDevices = lib.filterAttrs (
_: v: (v.distributedBuilds or { }).isBuilder or false _: v: (v.distributedBuilds or { }).isBuilder or false
) allDevices; ) devices;
buildServerKnownHosts = lib.pipe buildServerDevices [ knownHosts = lib.pipe buildServerDevices [
(lib.filterAttrs (_: v: v.distributedBuilds ? hostPublicKey)) (lib.filterAttrs (_: v: v.distributedBuilds ? hostPublicKey))
(lib.mapAttrs ( (lib.mapAttrs (
_: v: { _: v: {
@ -30,21 +27,17 @@ let
)) ))
]; ];
remoteBuildServerDevices = builtins.filter ( buildMachineList = lib.mapAttrsToList (
m: m.hostName != config.networking.hostName hostName: v:
) (lib.mapAttrsToList (name: v: v // { hostName = name; }) buildServerDevices);
buildMachines = map (
m:
{ {
hostName = m.hostName; inherit hostName;
systems = [ m.system ]; systems = [ v.system ];
sshUser = buildUser; sshUser = buildUser;
sshKey = clientSshKeyPath; sshKey = sshKeyPath;
protocol = "ssh-ng"; protocol = "ssh-ng";
} }
// lib.optionalAttrs (m.distributedBuilds ? speedFactor) { // lib.optionalAttrs (v.distributedBuilds ? speedFactor) {
speedFactor = m.distributedBuilds.speedFactor; speedFactor = v.distributedBuilds.speedFactor;
} }
// { // {
supportedFeatures = [ supportedFeatures = [
@ -54,73 +47,60 @@ let
"benchmark" "benchmark"
]; ];
} }
) remoteBuildServerDevices; ) buildServerDevices;
remoteMachines = builtins.filter (m: m.hostName != config.networking.hostName) buildMachineList;
in in
{ {
options.my.distributedBuilds.enable = lib.mkEnableOption "distributed Nix builds"; options.my.distributedBuilds.enable = lib.mkEnableOption "distributed Nix builds";
config = lib.mkIf config.my.distributedBuilds.enable ( config = lib.mkIf config.my.distributedBuilds.enable {
lib.mkMerge [ programs.ssh.knownHosts = knownHosts;
# All machines # Dedicated user for receiving distributed build connections
{ users.users.${buildUser} = {
nix.settings = { isSystemUser = true;
trusted-public-keys = lib.pipe buildServerDevices [ group = buildUser;
(lib.mapAttrsToList (_: v: v.distributedBuilds.storeSigningPublicKey or null)) useDefaultShell = true;
(builtins.filter (k: k != null)) openssh.authorizedKeys.keys = map (
]; k: ''command="nix daemon --stdio",restrict ${k}''
max-jobs = (thisDevice.distributedBuilds or { }).maxJobs or "auto"; ) authorizedPublicKeys;
cores = 0; };
min-free = 10 * 1024 * 1024; users.groups.${buildUser} = { };
max-free = 200 * 1024 * 1024;
};
systemd.services.nix-daemon.serviceConfig = {
MemoryAccounting = true;
MemoryMax = "90%";
OOMScoreAdjust = 500;
};
}
# Server: accept incoming build connections nix = {
(lib.mkIf (thisDevice.distributedBuilds.isBuilder or false) { distributedBuilds = remoteMachines != [ ];
users.users.${buildUser} = { buildMachines = remoteMachines;
isSystemUser = true; settings = {
group = buildUser; trusted-users = [ buildUser ];
useDefaultShell = true; builders-use-substitutes = true;
openssh.authorizedKeys.keys = map ( # Use build machines as binary caches so already-built paths are downloaded
k: ''command="nix daemon --stdio",restrict ${k}'' # rather than rebuilt. Only machines with a storeSigningPublicKey are used.
) allClientPublicKeys; substituters = lib.pipe buildServerDevices [
}; (lib.filterAttrs (_: v: v.distributedBuilds ? storeSigningPublicKey))
users.groups.${buildUser} = { }; (lib.mapAttrsToList (hostName: _: "ssh-ng://${buildUser}@${hostName}"))
nix.settings = { (lib.filter (s: s != "ssh-ng://${buildUser}@${config.networking.hostName}"))
trusted-users = [ buildUser ]; ];
secret-key-files = [ "/etc/nix/signing-key.sec" ]; trusted-public-keys = lib.pipe buildServerDevices [
}; (lib.mapAttrsToList (_: v: v.distributedBuilds.storeSigningPublicKey or null))
}) (builtins.filter (k: k != null))
];
secret-key-files =
let
thisDevice = devices.${config.networking.hostName} or { };
in
lib.optional (thisDevice.distributedBuilds.isBuilder or false) "/etc/nix/signing-key.sec";
max-jobs = (devices.${config.networking.hostName}.distributedBuilds or { }).maxJobs or "auto";
cores = 0;
min-free = 10 * 1024 * 1024;
max-free = 200 * 1024 * 1024;
};
};
# Client: connect to build servers for building and substitution systemd.services.nix-daemon.serviceConfig = {
(lib.mkIf isClient { MemoryAccounting = true;
programs.ssh = { MemoryMax = "90%";
knownHosts = buildServerKnownHosts; OOMScoreAdjust = 500;
extraConfig = '' };
Host ${lib.concatStringsSep " " (lib.attrNames buildServerDevices)} };
User ${buildUser}
IdentityFile ${clientSshKeyPath}
IdentitiesOnly yes
'';
};
nix = {
distributedBuilds = buildMachines != [ ];
buildMachines = buildMachines;
settings = {
builders-use-substitutes = true;
substituters = map (m: "ssh-ng://${buildUser}@${m.hostName}") (
builtins.filter (m: m.distributedBuilds ? storeSigningPublicKey) remoteBuildServerDevices
);
};
};
})
]
);
} }