1{2 lib,3 fleetLib,4 config,5 ...6}: let7 inherit (fleetLib.options) mkDataOption;8 inherit (lib.options) mkOption;9 inherit (lib.types) nullOr listOf str attrsOf submodule bool unspecified;10 inherit (lib.attrsets) mapAttrsToList mapAttrs filterAttrs genAttrs;11 inherit (lib.lists) sort unique concatLists;12 inherit (lib.strings) toJSON;1314 secretDataValue = {15 options = {16 raw = mkOption {17 type = nullOr str;18 description = "Encrypted + encoded secret data";19 default = null;20 };21 };22 };2324 sharedSecretData = {25 freeformType = attrsOf (submodule secretDataValue);26 options = {27 createdAt = mkOption {28 type = str;29 description = "When this secret was (re)generated";30 default = null;31 };32 expiresAt = mkOption {33 type = nullOr str;34 description = "On which date this secret will expire, someone should regenerate this secret before it expires.";35 default = null;36 };3738 owners = mkOption {39 type = listOf str;40 description = ''41 For which owners this secret is currently encrypted,42 if not matches expectedOwners - then this secret is considered outdated, and43 should be regenerated/reencrypted.4445 Imported from fleet.nix46 '';47 default = [];48 };49 generationData = mkOption {50 type = unspecified;51 description = "Data that is embedded into secret part";52 default = null;53 };54 };55 };5657 hostSecretData = {58 freeformType = attrsOf (submodule secretDataValue);59 options = {60 createdAt = mkOption {61 type = str;62 description = "When this secret was (re)generated";63 default = null;64 };65 expiresAt = mkOption {66 type = nullOr str;67 description = "On which date this secret will expire, someone should regenerate this secret before it expires.";68 default = null;69 };70 shared = mkOption {71 type = bool;72 description = "On which date this secret will expire, someone should regenerate this secret before it expires.";73 default = false;74 };75 generationData = mkOption {76 type = unspecified;77 description = "Data that is embedded into secret part";78 default = null;79 };80 };81 };82in {83 options.data = mkDataOption ({config, ...}: {84 options = {85 sharedSecrets = mkOption {86 type = attrsOf (submodule sharedSecretData);87 default = {};88 description = "Stored shared secret data.";89 };90 hostSecrets = mkOption {91 type = attrsOf (attrsOf (submodule hostSecretData));92 default = {};93 description = "Host secrets.";94 internal = true;95 };96 };97 config.hostSecrets = let98 hostsWithSharedSecrets = unique (concatLists (mapAttrsToList (_: s: s.owners) config.sharedSecrets));99 secretsHavingHost = host: filterAttrs (_: secret: lib.elem host secret.owners) config.sharedSecrets;100 toHostSecret = _: secret: (removeAttrs secret ["owners"]) // {shared = true;};101 in102 genAttrs hostsWithSharedSecrets (host: mapAttrs toHostSecret (secretsHavingHost host));103 });104 config = {105 assertions =106 (mapAttrsToList107 (name: secret: {108 assertion = secret.expectedOwners == null || sort (a: b: a < b) config.data.sharedSecrets.${name}.owners == sort (a: b: a < b) secret.expectedOwners;109 message = "Shared secret ${name} is expected to be encrypted for ${toJSON secret.expectedOwners}, but it is encrypted for ${toJSON config.data.sharedSecrets.${name}.owners}. Run fleet secrets regenerate to fix";110 })111 config.sharedSecrets)112 ++ (mapAttrsToList113 (name: secret: {114 115 assertion = config.data.sharedSecrets.${name}.generationData == secret.expectedGenerationData;116 message = "Shared secret ${name} has unexpected generation data ${toJSON secret.expectedGenerationData} != ${toJSON config.data.sharedSecrets.${name}.expectedGenerationData}. Run fleet secrets regenerate to fix";117 })118 config.sharedSecrets);119 sharedSecrets =120 mapAttrs (_: _: {}) config.data.sharedSecrets;121 };122}