diff options
| author | quentin@aristote.fr <quentin@aristote.fr> | 2025-03-22 18:19:29 +0100 |
|---|---|---|
| committer | quentin@aristote.fr <quentin@aristote.fr> | 2025-03-22 21:09:17 +0100 |
| commit | f2e0f4c86f3318cc6df018b1948dd5bade77f082 (patch) | |
| tree | 91eaef3ca670be49d86b731a816bb9ed5bd4e1fb /modules/nixos | |
| parent | 65eb03f15116223871d06254dc453abc79bcffaa (diff) | |
nixos: split nix and system; use own autoUpgrade script
Diffstat (limited to 'modules/nixos')
| -rw-r--r-- | modules/nixos/personal/default.nix | 1 | ||||
| -rw-r--r-- | modules/nixos/personal/nix.nix | 164 | ||||
| -rw-r--r-- | modules/nixos/personal/system.nix | 220 |
3 files changed, 221 insertions, 164 deletions
diff --git a/modules/nixos/personal/default.nix b/modules/nixos/personal/default.nix index 064260a..dfc7291 100644 --- a/modules/nixos/personal/default.nix +++ b/modules/nixos/personal/default.nix @@ -7,6 +7,7 @@ ./monitoring.nix ./networking ./nix.nix + ./system.nix ./user.nix ]; } diff --git a/modules/nixos/personal/nix.nix b/modules/nixos/personal/nix.nix index cab9128..17471c2 100644 --- a/modules/nixos/personal/nix.nix +++ b/modules/nixos/personal/nix.nix @@ -5,72 +5,10 @@ ... }: let cfg = config.personal.nix; - hasFlake = cfg.flake != null; - hasFlakeInputs = cfg.autoUpgrade.autoUpdateInputs != []; - checkNetwork = { - path = [pkgs.unixtools.ping]; - # Check network connectivity - preStart = "(${lib.concatMapStringsSep " && " (host: "ping -c 1 ${host}") cfg.autoUpgrade.checkHosts}) || kill -s SIGUSR1 $$"; - unitConfig = { - StartLimitIntervalSec = 300; - StartLimitBurst = 5; - }; - serviceConfig = { - Restart = "on-abort"; - RestartSec = 30; - }; - }; in { options.personal.nix = { enable = lib.mkEnableOption "nix configuration"; - autoUpgrade = { - enable = lib.mkEnableOption "automatic system and nixpkgs upgrade"; - autoUpdateInputs = lib.mkOption { - type = with lib.types; listOf str; - default = ["nixpkgs" "my-nixpkgs/nur" "nixos-hardware"]; - }; - checkHosts = lib.mkOption { - type = with lib.types; listOf str; - default = with builtins; concatMap (match "https://([^/]*)/?") config.nix.settings.substituters; - }; - }; - flake = lib.mkOption { - type = with lib.types; nullOr str; - default = null; - }; gc.enable = lib.mkEnableOption "garbage collection"; - remoteBuilds = { - enable = lib.mkEnableOption "remote builds"; - machines.hephaistos = { - enable = lib.mkEnableOption "hephaistos remote builder"; - domain = lib.mkOption { - type = lib.types.str; - }; - user = lib.mkOption { - type = lib.types.str; - default = "nixremote"; - }; - protocol = lib.mkOption { - type = lib.types.str; - # Nix custom ssh-variant that avoids lots of "trusted-users" settings pain - default = "ssh-ng"; - }; - speedFactor = lib.mkOption { - type = - lib.types.int; - default = 8; - }; - require = lib.mkOption { - type = - lib.types.bool; - default = true; - description = '' - Whether this remote builder is required to build the configuration. - If so, network connectivity to this remote builder will be checked prior to building. - ''; - }; - }; - }; }; config = lib.mkIf cfg.enable (lib.mkMerge [ @@ -129,107 +67,5 @@ in { }; }; }) - - (lib.mkIf cfg.autoUpgrade.enable { - personal.boot.unattendedReboot = lib.mkIf config.system.autoUpgrade.allowReboot true; - system.autoUpgrade = { - enable = true; - flags = lib.optional (!hasFlake) "--upgrade-all"; - }; - systemd.services.nixos-upgrade = lib.mkMerge [ - checkNetwork - { - path = [config.nix.package]; - preStart = lib.mkAfter (lib.optionalString hasFlake '' - echo "Downloading flake inputs..." - nix flake archive ${cfg.flake} - '' - + '' - echo "Evaluating configuration..." - ${config.system.build.nixos-rebuild}/bin/nixos-rebuild dry-build ${toString config.system.autoUpgrade.flags} - ''); - personal.monitor = true; - } - (let - luksCfg = config.boot.initrd.luks.devices; - cryptExists = luksCfg ? crypt; - cryptCfg = luksCfg.crypt; - in - lib.mkIf (cryptExists && config.system.autoUpgrade.allowReboot) { - path = [pkgs.cryptsetup]; - script = lib.mkAfter '' - cryptsetup --verbose luksAddKey --key-file /etc/luks/keys/master ${cryptCfg.device} /etc/luks/keys/tmp - ''; - serviceConfig.TimeoutStopSec = "infinity"; - postStop = '' - # if a reboot due to nixos-upgrade happens, - # it should occur within a minute - sleep 60 - # if no reboot has happened, - # disable any leftover keyfile - while cryptsetup --verbose luksRemoveKey ${cryptCfg.device} --key-file /etc/luks/keys/tmp - do - : - done - ''; - }) - ]; - }) - - (lib.mkIf hasFlake { - system.autoUpgrade.flake = cfg.flake; - systemd.services.flake-update = lib.mkIf hasFlakeInputs (lib.mkMerge [ - checkNetwork - { - unitConfig.Description = "Update flake inputs"; - serviceConfig = { - ExecStart = "${config.nix.package}/bin/nix flake update --commit-lock-file --flake ${cfg.flake} " + lib.concatStringsSep " " cfg.autoUpgrade.autoUpdateInputs; - Type = "oneshot"; # Ensure that it finishes before starting nixos-upgrade - }; - before = ["nixos-upgrade.service"]; - requiredBy = ["nixos-upgrade.service"]; - path = [pkgs.git]; - personal.monitor = true; - } - ]); - - programs.git = lib.mkIf (lib.hasPrefix "git+file" cfg.flake) { - enable = true; - config.user = lib.mkDefault { - name = "Root user of ${config.networking.hostName}"; - email = "root@${config.networking.hostName}"; - }; - }; - }) - - (lib.mkIf cfg.remoteBuilds.enable (with cfg.remoteBuilds.machines.hephaistos; { - nix = { - distributedBuilds = true; - settings.builders-use-substitutes = true; - buildMachines = lib.optional enable { - inherit protocol speedFactor; - hostName = "hephaistos.${domain}"; - system = "x86_64-linux"; - maxJobs = 8; - supportedFeatures = ["nixos-test" "benchmark" "big-parallel" "kvm" "recursive-nix"]; - mandatoryFeatures = []; - }; - }; - - personal.nix.autoUpgrade.checkHosts = lib.mkOptionDefault (lib.optional require "hephaistos.${domain}"); - - programs.ssh = { - extraConfig = lib.optionalString enable '' - Host hephaistos.${domain} - # Prevent using ssh-agent or another keyfile, useful for testing - IdentitiesOnly yes - IdentityFile /etc/ssh/${user} - # The weakly privileged user on the remote builder - # If not set, 'root' is used – which will hopefully fail - User ${user} - ''; - knownHosts."hephaistos.${domain}".publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHvtqi8tziBuviUV8LDK2ddQQUbHdJYB02dgWTK5Olxq"; - }; - })) ]); } diff --git a/modules/nixos/personal/system.nix b/modules/nixos/personal/system.nix new file mode 100644 index 0000000..3bd3716 --- /dev/null +++ b/modules/nixos/personal/system.nix @@ -0,0 +1,220 @@ +{ + config, + lib, + pkgs, + ... +}: let + cfg = config.personal.system; + cfgRemote = cfg.autoUpgrade.remoteBuilding; + cfgNix = config.nix; + cfgLuks = config.boot.initrd.luks.devices; + + name = config.networking.hostName; +in { + options.personal.system = { + flake = lib.mkOption { + type = with lib.types; nullOr str; + default = null; + }; + autoUpgrade = { + enable = lib.mkEnableOption "automatic system and nixpkgs upgrade"; + autoUpdateInputs = lib.mkOption { + type = with lib.types; listOf str; + default = ["nixpkgs" "my-nixpkgs/nur" "nixos-hardware"]; + }; + checkHosts = lib.mkOption { + type = with lib.types; listOf str; + default = with builtins; concatMap (match "https://([^/]*)/?") cfgNix.settings.substituters; + }; + remoteBuilding = { + enable = lib.mkEnableOption "remote building of the system configuration"; + builder = { + hostName = lib.mkOption { + type = lib.types.str; + default = "hephaistos"; + }; + domain = lib.mkOption {type = lib.types.str;}; + user = lib.mkOption { + type = lib.types.str; + default = name; + }; + protocol = lib.mkOption { + type = lib.types.str; + # Nix custom ssh-variant that avoids lots of "trusted-users" settings pain + default = "ssh-ng"; + }; + speedFactor = lib.mkOption { + type = + lib.types.int; + default = 8; + }; + }; + }; + }; + }; + + config = let + hasFlake = cfg.flake != null; + hasFlakeInputs = cfg.autoUpgrade.autoUpdateInputs != []; + + reboot = config.system.autoUpgrade.allowReboot; + nixosRebuild = "nixos-rebuild ${toString config.system.autoUpgrade.flags}"; + + remoteBuilder = with cfgRemote.builder; "${hostName}.${domain}"; + + checkNetwork = { + path = [pkgs.unixtools.ping]; + # Check network connectivity + preStart = "(${lib.concatMapStringsSep " && " (host: "ping -c 1 ${host}") cfg.autoUpgrade.checkHosts}) || kill -s SIGUSR1 $$"; + unitConfig = { + StartLimitIntervalSec = 300; + StartLimitBurst = 5; + }; + serviceConfig = { + Restart = "on-abort"; + RestartSec = 30; + }; + }; + in + lib.mkMerge [ + (lib.mkIf hasFlake { + system.autoUpgrade.flake = cfg.flake; + systemd.services.flake-update = lib.mkIf hasFlakeInputs (lib.mkMerge [ + checkNetwork + { + description = "Update flake inputs"; + serviceConfig.Type = "oneshot"; + script = "nix flake update --commit-lock-file --flake ${cfg.flake} " + lib.concatStringsSep " " cfg.autoUpgrade.autoUpdateInputs; + before = ["nixos-upgrade.service"]; + requiredBy = ["nixos-upgrade.service"]; + path = [pkgs.git cfgNix.package]; + personal.monitor = true; + } + ]); + + programs.git = lib.mkIf (lib.hasPrefix "git+file" cfg.flake) { + enable = true; + config.user = lib.mkDefault { + name = "Root user of ${name}"; + email = "root@${name}"; + }; + }; + }) + + ( + lib.mkIf (cfg.autoUpgrade.enable && cfgRemote.enable) { + assertions = [ + { + assertion = hasFlake && lib.hasPrefix "git+file://" cfg.flake; + message = "Auto remote upgrade is only supported when the system is specified by a flake with path of the shape 'git+file://...'"; + } + ]; + + personal.system.autoUpgrade.checkHosts = lib.mkOptionDefault [remoteBuilder]; + + programs.ssh = { + extraConfig = '' + Host ${remoteBuilder} + IdentitiesOnly yes + IdentityFile /etc/ssh/remoteBuilder + User ${cfgRemote.builder.user} + ''; + knownHosts."${remoteBuilder}".publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHvtqi8tziBuviUV8LDK2ddQQUbHdJYB02dgWTK5Olxq"; + }; + } + ) + + (lib.mkIf cfg.autoUpgrade.enable { + personal.boot.unattendedReboot = lib.mkIf reboot true; + system.autoUpgrade = { + enable = true; + flags = lib.optional (!hasFlake) "--upgrade-all"; + }; + systemd.services.nixos-upgrade = lib.mkMerge [ + checkNetwork + { + path = + lib.optional reboot pkgs.coreutils + ++ [ + ( + if cfgRemote.enable + then cfgNix.package + else pkgs.nixos-rebuild + ) + ] + ++ lib.optional (reboot && cfgLuks ? crypt) pkgs.cryptsetup; + personal.monitor = true; + script = lib.mkForce (lib.concatStrings [ + '' + ## build configuration + '' + ( + let + in + if cfgRemote.enable + then '' + # update remote flake + pushd ${lib.removePrefix "git+file://" cfg.flake} + git push --force ${cfgRemote.builder.hostName} master + popd + # build remotely + config=$(ssh ${remoteBuilder} -- \ + 'nix build --print-out-paths \ + git+file://$(pwd)/nixos-configuration#nixosConfigurations.${name}.config.system.build.toplevel') + # copy result locally + nix-copy-closure --from ${remoteBuilder} "$config" + # create new generation + nix-env --profile /nix/var/nix/profiles/system \ + --set "$config" + + switch="$config/bin/switch-to-configuration" + '' + else '' + switch="${nixosRebuild}" + '' + ) + '' + ## check whether a reboot is necessary" + '' + ( + if reboot + then '' + $switch boot + booted="$(readlink /run/booted-system/{initrd,kernel,kernel-modules})" + built="$(readlink /nix/var/nix/profiles/system/{initrd,kernel,kernel-modules})" + reboot="$([ "$booted" = "$built" ] || echo true)" + '' + else '' + reboot="" + '' + ) + '' + ## switch to new configuration + '' + (let + ifcrypt = lib.optionalString (cfgLuks ? crypt); + crypt = cfgLuks.crypt.device; + luksKey = x: "/etc/luks/keys/" + x; + in '' + if [ "$reboot" ] + then + ${ifcrypt '' + cryptsetup luksAddKey ${crypt} ${luksKey "tmp"} \ + --key-file ${luksKey "master"} \ + --verbose + ''} + shutdown -r now ${ifcrypt '' + || cryptsetup luksRemoveKey ${crypt} \ + --key-file ${luksKey "tmp"} \ + --verbose + ''} + else + $switch switch + fi + '') + ]); + } + ]; + }) + ]; +} |
