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

difftreelog

source

modules/secrets-data.nix4.5 KiBsourcehistory
1{2  lib,3  fleetLib,4  config,5  ...6}:7let8  inherit (fleetLib.options) mkDataOption;9  inherit (lib.options) mkOption;10  inherit (lib.types)11    nullOr12    listOf13    str14    attrsOf15    submodule16    bool17    unspecified18    ;19  inherit (lib.attrsets)20    mapAttrsToList21    mapAttrs22    filterAttrs23    genAttrs24    ;25  inherit (lib.lists) sort unique concatLists;26  inherit (lib.strings) toJSON;2728  secretDataValue = {29    options = {30      raw = mkOption {31        type = nullOr str;32        description = "Raw secret data in unspecified encoded and optionally encrypted format.";33        default = null;34      };35    };36  };3738  sharedSecretData = {39    freeformType = attrsOf (submodule secretDataValue);40    options = {41      createdAt = mkOption {42        type = str;43        description = "Timestamp of secret generation/last rotation.";44        default = null;45      };46      expiresAt = mkOption {47        type = nullOr str;48        description = "Expiration timestamp triggering mandatory secret rotation.";49        default = null;50      };5152      owners = mkOption {53        type = listOf str;54        description = ''55          List of hosts currently authorized to decrypt this shared secret.5657          If owners differ from expected owners, the secret is considered outdated58          and requires regeneration or re-encryption.59        '';60        default = [ ];61      };62      generationData = mkOption {63        type = unspecified;64        description = "Contextual metadata associated with secret part.";65        default = null;66      };67    };68    config = { };69  };7071  hostSecretData = {72    freeformType = attrsOf (submodule secretDataValue);73    options = {74      createdAt = mkOption {75        type = str;76        description = "Timestamp of secret generation/last rotation.";77        default = null;78      };79      expiresAt = mkOption {80        type = nullOr str;81        description = "Expiration timestamp triggering mandatory secret rotation.";82        default = null;83      };84      shared = mkOption {85        type = bool;86        description = "Indicates if secret is a shared secret, so other hosts might have the same piece of secret data.";87        default = false;88      };89      generationData = mkOption {90        type = unspecified;91        description = "Contextual metadata associated with secret part.";92        default = null;93      };94    };95    config = { };96  };97in98{99  options.data = mkDataOption (100    { config, ... }:101    {102      options = {103        sharedSecrets = mkOption {104          type = attrsOf (submodule sharedSecretData);105          default = { };106          description = "Shared secret data.";107        };108        hostSecrets = mkOption {109          type = attrsOf (attrsOf (submodule hostSecretData));110          default = { };111          description = "Host-specific secrets.";112          internal = true;113        };114      };115      config.hostSecrets =116        let117          hostsWithSharedSecrets = unique (118            concatLists (mapAttrsToList (_: s: s.owners) config.sharedSecrets)119          );120          secretsHavingHost = host: filterAttrs (_: secret: lib.elem host secret.owners) config.sharedSecrets;121          toHostSecret = _: secret: (removeAttrs secret [ "owners" ]) // { shared = true; };122        in123        genAttrs hostsWithSharedSecrets (host: mapAttrs toHostSecret (secretsHavingHost host));124    }125  );126  config = {127    assertions =128      (mapAttrsToList (name: secret: {129        assertion =130          secret.expectedOwners == null131          ||132            sort (a: b: a < b) (config.data.sharedSecrets.${name} or { owners = [ ]; }).owners133            == sort (a: b: a < b) secret.expectedOwners;134        message = "Shared secret ${name} is expected to be encrypted for ${toJSON secret.expectedOwners}, but it is encrypted for ${135          toJSON (config.data.sharedSecrets.${name} or { owners = [ ]; }).owners136        }. Run fleet secrets regenerate to fix";137      }) config.sharedSecrets)138      ++ (mapAttrsToList (name: secret: {139        # TODO: Same aassertion should be in host secrets140        assertion =141          (config.data.sharedSecrets.${name} or { generationData = null; }).generationData142          == secret.expectedGenerationData;143        message = "Shared secret ${name} has unexpected generation data ${toJSON secret.expectedGenerationData} != ${144          toJSON (config.data.sharedSecrets.${name} or { generationData = null; }).generationData145        }. Run fleet secrets regenerate to fix";146      }) config.sharedSecrets);147    sharedSecrets = mapAttrs (_: _: { }) config.data.sharedSecrets;148  };149}