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

difftreelog

source

modules/nixos/secrets.nix6.0 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 = let 113          hostName = host._module.args.name;114          generator = config.generator;115        in builtins.deepSeq [116          hostName117          secretName118          generator119        ] (builtins.fleetEnsureHostSecret120          hostName121          secretName122          generator);123      };124    }125  );126  secretsFile = pkgs.writeTextFile {127    name = "secrets.json";128    text = toJSON config.system.secretsData;129  };130  useSysusers =131    (config.systemd ? sysusers && config.systemd.sysusers.enable)132    || (config ? userborn && config.userborn.enable);133in134{135  options = {136    secrets = mkOption {137      type = attrsOf secretType;138      default = { };139      apply = mapAttrs (_: secret: secret.parts // {definition = secret;});140      description = "Host-local secrets";141    };142    system.secretsData = mkOption {143      type = unspecified;144      default = mapAttrs (_: s:145        (removeAttrs s.definition ["generator"]) // {146          parts = mapAttrs (_: part: removeAttrs part ["data"]) s.definition.parts;147        }148      ) config.secrets;149      description = "secrets.json contents";150    };151  };152  config = {153    environment.systemPackages = [ pkgs.fleet-install-secrets ];154155    assertions = mapAttrsToList (name: secret: let156      hasSharedDefinition = fleetConfiguration.secrets ? name;157    in {158      assertion = (secret.definition.generator == "shared") == hasSharedDefinition && hasSharedDefinition -> (elem host._module.args.name fleetConfiguration.secrets.${name}.expectedOwners);159      message = if hasSharedDefinition then"secret ${name} has host-specific secret generator, secrets with host-specific generators can not have shared generator in fleet configuration"160      else "secret ${name} is declared as shared, for shared secret fleet configuration should include shared secret generator, and expectedOwners should contain this host";161    }) config.secrets;162163    systemd.services.fleet-install-secrets = mkIf useSysusers {164      wantedBy = [ "sysinit.target" ];165      after = [ "systemd-sysusers.service" ];166      restartTriggers = [167        secretsFile168      ];169      aliases = [170        "sops-install-secrets"171        "agenix-install-secrets"172      ];173174      unitConfig.DefaultDependencies = false;175176      serviceConfig = {177        Type = "oneshot";178        RemainAfterExit = true;179        ExecStart = "${pkgs.fleet-install-secrets}/bin/fleet-install-secrets install ${secretsFile}";180      };181    };182    system.activationScripts.decryptSecrets = mkIf (!useSysusers) (183      stringAfter184        (185          [186            # secrets are owned by user/group, thus we need to refer to those187            "users"188            "groups"189            "specialfs"190          ]191          # nixos-impermanence compatibility: secrets are encrypted by host-key,192          # but with impermanence we expect that the host-key is installed by193          # persist-file activation script.194          ++ (optional (config.system.activationScripts ? "persist-files") "persist-files")195        )196        ''197          1>&2 echo "setting up secrets"198          ${pkgs.fleet-install-secrets}/bin/fleet-install-secrets install ${secretsFile}199        ''200    );201  };202}