git.delta.rocks / jrsonnet / refs/commits / d97c9fe9e5c4

difftreelog

feat ensure shared generators

pwqmwvyvYaroslav Bolyukin2026-01-08parent: #e89ca39.patch.diff
in: trunk

2 files changed

modifiedmodules/nixos.nixdiffbeforeafterboth
--- a/modules/nixos.nix
+++ b/modules/nixos.nix
@@ -21,7 +21,7 @@
   options = {
     nixos = mkOption {
       description = ''
-        Nixos configuration for all hosts.
+        Shared nixos configuration module for all hosts.
       '';
       type = deferredModule;
     };
@@ -76,6 +76,7 @@
             nixosHosts = mapAttrs (_: value: value.nixos_unchecked.config) config.hosts;
             hosts = config.hosts;
             host = hostArgs.config;
+            fleetConfiguration = config;
           };
         };
         nixos_unchecked = hostArgs.config.nixos.extendModules {
modifiedmodules/nixos/secrets.nixdiffbeforeafterboth
before · modules/nixos/secrets.nix
1{2  lib,3  fleetLib,4  config,5  pkgs,6  ...7}:8let9  inherit (builtins)10    hashString11    toJSON12    ;13  inherit (lib.stringsWithDeps) stringAfter;14  inherit (lib.options) mkOption literalExpression;15  inherit (lib.lists) optional;16  inherit (lib.attrsets) mapAttrs;17  inherit (lib.modules) mkIf;18  inherit (lib.types)19    submodule20    str21    attrsOf22    unspecified23    uniq24    functionTo25    package26    ;27  inherit (fleetLib.strings) decodeRawSecret;2829  sysConfig = config;30  secretPartType =31    secretName:32    submodule (33      { config, ... }:34      let35        partName = config._module.args.name;36      in37      {38        options = {39          hash = mkOption {40            type = str;41            description = "Hash of secret in encoded format";42          };43          path = mkOption {44            type = str;45            description = "Path to secret part, incorporating data hash (thus it will be updated on secret change)";46          };47          stablePath = mkOption {48            type = str;49            description = "Path to secret part, stable path (users are expected to watch for file changes/re-read secret on demand)";50          };51          data = mkOption {52            type = str;53            description = "Secret public data (only available for plaintext)";54          };55          raw = mkOption {56            type = str;57            description = "Raw (encoded/encrypted secret part data)";58          };59        };60        config = {61          hash = hashString "sha1" config.raw;62          data = decodeRawSecret config.raw;63          path = "/run/secrets/${secretName}/${config.hash}-${partName}";64          stablePath = "/run/secrets/${secretName}/${partName}";65        };66      }67    );68  secretType = submodule (69    {70      config,71      ...72    }:73    let74      secretName = config._module.args.name;75    in76    {77      options = {78        parts = mkOption {79          type = uniq (attrsOf (secretPartType secretName));80          description = "Definition of secret parts";81        };82        generator = mkOption {83          type = uniq (functionTo package);84          description = "Derivation to evaluate for secret generation";85        };86        mode = mkOption {87          type = str;88          description = "Secret mode";89          default = "0440";90        };91        owner = mkOption {92          type = str;93          description = "Owner of the secret";94          default = "root";95        };96        group = mkOption {97          type = str;98          description = "Group of the secret";99          default = sysConfig.users.users.${config.owner}.group;100          defaultText = literalExpression "config.users.users.$${owner}.group";101        };102      };103      config = {104        # C api is broken in regard to thunks105        # https://github.com/NixOS/nix/issues/12800106        parts = let 107          hostName = sysConfig.networking.hostName;108          generator = config.generator;109        in builtins.deepSeq [110          hostName111          secretName112          generator113        ] (builtins.fleetEnsureHostSecret114          hostName115          secretName116          generator);117      };118    }119  );120  secretsFile = pkgs.writeTextFile {121    name = "secrets.json";122    text = toJSON config.system.secretsData;123  };124  useSysusers =125    (config.systemd ? sysusers && config.systemd.sysusers.enable)126    || (config ? userborn && config.userborn.enable);127in128{129  options = {130    secrets = mkOption {131      type = attrsOf secretType;132      default = { };133      apply = mapAttrs (_: secret: secret.parts // {definition = secret;});134      description = "Host-local secrets";135    };136    system.secretsData = mkOption {137      type = unspecified;138      default = mapAttrs (_: s:139        (removeAttrs s.definition ["generator"]) // {140          parts = mapAttrs (_: part: removeAttrs part ["data"]) s.definition.parts;141        }142      ) config.secrets;143      description = "secrets.json contents";144    };145  };146  config = {147    environment.systemPackages = [ pkgs.fleet-install-secrets ];148149    systemd.services.fleet-install-secrets = mkIf useSysusers {150      wantedBy = [ "sysinit.target" ];151      after = [ "systemd-sysusers.service" ];152      restartTriggers = [153        secretsFile154      ];155      aliases = [156        "sops-install-secrets"157        "agenix-install-secrets"158      ];159160      unitConfig.DefaultDependencies = false;161162      serviceConfig = {163        Type = "oneshot";164        RemainAfterExit = true;165        ExecStart = "${pkgs.fleet-install-secrets}/bin/fleet-install-secrets install ${secretsFile}";166      };167    };168    system.activationScripts.decryptSecrets = mkIf (!useSysusers) (169      stringAfter170        (171          [172            # secrets are owned by user/group, thus we need to refer to those173            "users"174            "groups"175            "specialfs"176          ]177          # nixos-impermanence compatibility: secrets are encrypted by host-key,178          # but with impermanence we expect that the host-key is installed by179          # persist-file activation script.180          ++ (optional (config.system.activationScripts ? "persist-files") "persist-files")181        )182        ''183          1>&2 echo "setting up secrets"184          ${pkgs.fleet-install-secrets}/bin/fleet-install-secrets install ${secretsFile}185        ''186    );187  };188}
after · modules/nixos/secrets.nix
1{2  lib,3  fleetLib,4  config,5  pkgs,6  fleetConfiguration,7  ...8}:9let10  inherit (builtins)11    hashString12    toJSON13    ;14  inherit (lib.stringsWithDeps) stringAfter;15  inherit (lib.options) mkOption literalExpression;16  inherit (lib.lists) optional;17  inherit (lib.attrsets) mapAttrs mapAttrsToList;18  inherit (lib.modules) mkIf;19  inherit (lib.types)20    submodule21    str22    attrsOf23    unspecified24    uniq25    functionTo26    package27    bool28    enum29    either30    ;31  inherit (fleetLib.strings) decodeRawSecret;3233  sysConfig = config;34  secretPartType =35    secretName:36    submodule (37      { config, ... }:38      let39        partName = config._module.args.name;40      in41      {42        options = {43          hash = mkOption {44            type = str;45            description = "Hash of secret in encoded format";46          };47          path = mkOption {48            type = str;49            description = "Path to secret part, incorporating data hash (thus it will be updated on secret change)";50          };51          stablePath = mkOption {52            type = str;53            description = "Path to secret part, stable path (users are expected to watch for file changes/re-read secret on demand)";54          };55          data = mkOption {56            type = str;57            description = "Secret public data (only available for plaintext)";58          };59          raw = mkOption {60            type = str;61            description = "Raw (encoded/encrypted secret part data)";62          };63        };64        config = {65          hash = hashString "sha1" config.raw;66          data = decodeRawSecret config.raw;67          path = "/run/secrets/${secretName}/${config.hash}-${partName}";68          stablePath = "/run/secrets/${secretName}/${partName}";69        };70      }71    );72  secretType = submodule (73    {74      config,75      ...76    }:77    let78      secretName = config._module.args.name;79      literal = l: enum [l];80    in81    {82      options = {83        parts = mkOption {84          type = uniq (attrsOf (secretPartType secretName));85          description = "Definition of secret parts";86        };87        generator = mkOption {88          type = either (functionTo package) (literal "shared");89          description = "Derivation to evaluate for secret generation";90        };91        mode = mkOption {92          type = str;93          description = "Secret mode";94          default = "0440";95        };96        owner = mkOption {97          type = str;98          description = "Owner of the secret";99          default = "root";100        };101        group = mkOption {102          type = str;103          description = "Group of the secret";104          default = sysConfig.users.users.${config.owner}.group;105          defaultText = literalExpression "config.users.users.$${owner}.group";106        };107      };108      config = {109        # C api is broken in regard to thunks110        # https://github.com/NixOS/nix/issues/12800111        parts = let 112          hostName = sysConfig.networking.hostName;113          generator = config.generator;114        in builtins.deepSeq [115          hostName116          secretName117          generator118        ] (builtins.fleetEnsureHostSecret119          hostName120          secretName121          generator);122      };123    }124  );125  secretsFile = pkgs.writeTextFile {126    name = "secrets.json";127    text = toJSON config.system.secretsData;128  };129  useSysusers =130    (config.systemd ? sysusers && config.systemd.sysusers.enable)131    || (config ? userborn && config.userborn.enable);132in133{134  options = {135    secrets = mkOption {136      type = attrsOf secretType;137      default = { };138      apply = mapAttrs (_: secret: secret.parts // {definition = secret;});139      description = "Host-local secrets";140    };141    system.secretsData = mkOption {142      type = unspecified;143      default = mapAttrs (_: s:144        (removeAttrs s.definition ["generator"]) // {145          parts = mapAttrs (_: part: removeAttrs part ["data"]) s.definition.parts;146        }147      ) config.secrets;148      description = "secrets.json contents";149    };150  };151  config = {152    environment.systemPackages = [ pkgs.fleet-install-secrets ];153154    assertions = mapAttrsToList (name: secret: let155      hasSharedDefinition = fleetConfiguration.secrets ? name;156    in {157      assertion = (secret.definition.generator == "shared") == hasSharedDefinition;158      message = if hasSharedDefinition then"secret ${name} has host-specific secret generator, secrets with host-specific generators can not have shared generator in fleet configuration"159      else "secret ${name} is declared as shared, for shared secret fleet configuration should include shared secret generator";160    }) config.secrets;161162    systemd.services.fleet-install-secrets = mkIf useSysusers {163      wantedBy = [ "sysinit.target" ];164      after = [ "systemd-sysusers.service" ];165      restartTriggers = [166        secretsFile167      ];168      aliases = [169        "sops-install-secrets"170        "agenix-install-secrets"171      ];172173      unitConfig.DefaultDependencies = false;174175      serviceConfig = {176        Type = "oneshot";177        RemainAfterExit = true;178        ExecStart = "${pkgs.fleet-install-secrets}/bin/fleet-install-secrets install ${secretsFile}";179      };180    };181    system.activationScripts.decryptSecrets = mkIf (!useSysusers) (182      stringAfter183        (184          [185            # secrets are owned by user/group, thus we need to refer to those186            "users"187            "groups"188            "specialfs"189          ]190          # nixos-impermanence compatibility: secrets are encrypted by host-key,191          # but with impermanence we expect that the host-key is installed by192          # persist-file activation script.193          ++ (optional (config.system.activationScripts ? "persist-files") "persist-files")194        )195        ''196          1>&2 echo "setting up secrets"197          ${pkgs.fleet-install-secrets}/bin/fleet-install-secrets install ${secretsFile}198        ''199    );200  };201}