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;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 };50 };5152 hostSecretData = {53 freeformType = attrsOf (submodule secretDataValue);54 options = {55 createdAt = mkOption {56 type = str;57 description = "When this secret was (re)generated";58 default = null;59 };60 expiresAt = mkOption {61 type = nullOr str;62 description = "On which date this secret will expire, someone should regenerate this secret before it expires.";63 default = null;64 };65 shared = mkOption {66 type = bool;67 description = "On which date this secret will expire, someone should regenerate this secret before it expires.";68 default = false;69 };70 };71 };72in {73 options.data = mkDataOption ({config, ...}: {74 options = {75 sharedSecrets = mkOption {76 type = attrsOf (submodule sharedSecretData);77 default = {};78 description = "Stored shared secret data.";79 };80 hostSecrets = mkOption {81 type = attrsOf (attrsOf (submodule hostSecretData));82 default = {};83 description = "Host secrets.";84 internal = true;85 };86 };87 config.hostSecrets = let88 hostsWithSharedSecrets = unique (concatLists (mapAttrsToList (_: s: s.owners) config.sharedSecrets));89 secretsHavingHost = host: filterAttrs (_: secret: lib.elem host secret.owners) config.sharedSecrets;90 toHostSecret = _: secret: (removeAttrs secret ["owners"]) // {shared = true;};91 in92 genAttrs hostsWithSharedSecrets (host: mapAttrs toHostSecret (secretsHavingHost host));93 });94 config = {95 assertions =96 mapAttrsToList97 (name: secret: {98 assertion = secret.expectedOwners == null || sort (a: b: a < b) config.data.sharedSecrets.${name}.owners == sort (a: b: a < b) secret.expectedOwners;99 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";100 })101 config.sharedSecrets;102 sharedSecrets =103 mapAttrs (_: _: {}) config.data.sharedSecrets;104 };105}