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

difftreelog

source

modules/nixos/secrets.nix6.1 KiBsourcehistory
1{2  lib,3  fleetLib,4  config,5  pkgs,6  host,7  fleetConfiguration,8  ...9}:10let11  inherit (builtins)12    hashString13    toJSON14    ;15  inherit (lib.stringsWithDeps) stringAfter;16  inherit (lib.options) mkOption literalExpression;17  inherit (lib.lists) optional elem;18  inherit (lib.attrsets) mapAttrs mapAttrsToList;19  inherit (lib.modules) mkIf;20  inherit (lib.types)21    submodule22    str23    attrsOf24    unspecified25    uniq26    functionTo27    package28    bool29    enum30    either31    ;32  inherit (fleetLib.strings) decodeRawSecret;3334  sysConfig = config;35  secretPartType =36    secretName:37    submodule (38      { config, ... }:39      let40        partName = config._module.args.name;41      in42      {43        options = {44          hash = mkOption {45            type = str;46            description = "Hash of secret in encoded format";47          };48          path = mkOption {49            type = str;50            description = "Path to secret part, incorporating data hash (thus it will be updated on secret change)";51          };52          stablePath = mkOption {53            type = str;54            description = "Path to secret part, stable path (users are expected to watch for file changes/re-read secret on demand)";55          };56          data = mkOption {57            type = str;58            description = "Secret public data (only available for plaintext)";59          };60          raw = mkOption {61            type = str;62            description = "Raw (encoded/encrypted secret part data)";63          };64        };65        config = {66          hash = hashString "sha1" config.raw;67          data = decodeRawSecret config.raw;68          path = "/run/secrets/${secretName}/${config.hash}-${partName}";69          stablePath = "/run/secrets/${secretName}/${partName}";70        };71      }72    );73  secretType = submodule (74    {75      config,76      ...77    }:78    let79      secretName = config._module.args.name;80      literal = l: enum [ l ];81    in82    {83      options = {84        parts = mkOption {85          type = uniq (attrsOf (secretPartType secretName));86          description = "Definition of secret parts";87        };88        generator = mkOption {89          type = either (functionTo package) (literal "shared");90          description = "Derivation to evaluate for secret generation";91        };92        mode = mkOption {93          type = str;94          description = "Secret mode";95          default = "0440";96        };97        owner = mkOption {98          type = str;99          description = "Owner of the secret";100          default = "root";101        };102        group = mkOption {103          type = str;104          description = "Group of the secret";105          default = sysConfig.users.users.${config.owner}.group;106          defaultText = literalExpression "config.users.users.$${owner}.group";107        };108      };109      config = {110        # C api is broken in regard to thunks111        # https://github.com/NixOS/nix/issues/12800112        parts =113          let114            hostName = host._module.args.name;115            generator = config.generator;116          in117          builtins.deepSeq [118            hostName119            secretName120            generator121          ] (builtins.fleetEnsureHostSecret hostName secretName 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 (144        _: s:145        (removeAttrs s.definition [ "generator" ])146        // {147          parts = mapAttrs (_: part: removeAttrs part [ "data" ]) s.definition.parts;148        }149      ) config.secrets;150      description = "secrets.json contents";151    };152  };153  config = {154    environment.systemPackages = [ pkgs.fleet-install-secrets ];155156    assertions = mapAttrsToList (157      name: secret:158      let159        hasSharedDefinition = fleetConfiguration.secrets ? ${name};160      in161      {162        assertion =163          (secret.definition.generator == "shared") == hasSharedDefinition164          && (165            hasSharedDefinition166            -> (elem host._module.args.name fleetConfiguration.secrets.${name}.expectedOwners)167          );168        message =169          if hasSharedDefinition then170            "secret ${name} has host-specific secret generator, secrets with host-specific generators can not have shared generator in fleet configuration"171          else172            "secret ${name} is declared as shared, for shared secret fleet configuration should include shared secret generator, and expectedOwners should contain this host";173      }174    ) config.secrets;175176    systemd.services.fleet-install-secrets = mkIf useSysusers {177      wantedBy = [ "sysinit.target" ];178      after = [ "systemd-sysusers.service" ];179      restartTriggers = [180        secretsFile181      ];182      aliases = [183        "sops-install-secrets"184        "agenix-install-secrets"185      ];186187      unitConfig.DefaultDependencies = false;188189      serviceConfig = {190        Type = "oneshot";191        RemainAfterExit = true;192        ExecStart = "${pkgs.fleet-install-secrets}/bin/fleet-install-secrets install ${secretsFile}";193      };194    };195    system.activationScripts.decryptSecrets = mkIf (!useSysusers) (196      stringAfter197        (198          [199            # secrets are owned by user/group, thus we need to refer to those200            "users"201            "groups"202            "specialfs"203          ]204          # nixos-impermanence compatibility: secrets are encrypted by host-key,205          # but with impermanence we expect that the host-key is installed by206          # persist-file activation script.207          ++ (optional (config.system.activationScripts ? "persist-files") "persist-files")208        )209        ''210          1>&2 echo "setting up secrets"211          ${pkgs.fleet-install-secrets}/bin/fleet-install-secrets install ${secretsFile}212        ''213    );214  };215}