1{2 lib,3 config,4 ...5}:6let7 inherit (lib.options) mkOption literalExpression;8 inherit (lib.types)9 unspecified10 nullOr11 listOf12 str13 bool14 attrsOf15 submodule16 functionTo17 package18 uniq19 ;20 inherit (lib.strings) concatStringsSep;21 inherit (lib.attrsets) mapAttrs;2223 sharedSecret =24 { config, ... }:25 {26 options = {27 expectedOwners = mkOption {28 type = nullOr (listOf str);29 description = ''30 Specifies the list of hosts authorized to decrypt and access this shared secret.3132 When null, secret ownership is managed manually via fleet.nix and CLI.33 Decrypted secrets will be stored at /run/secrets/$\{name} on authorized hosts.34 '';35 default = null;36 };37 regenerateOnOwnerAdded = mkOption {38 type = bool;39 description = ''40 Controls whether the secret must be regenerated when new owners are added.4142 Set to true when the secret contains owner-specific references (e.g., X.509 Subject Alternative Names).43 When true, adding a new owner will trigger secret regeneration instead of simple re-encryption.44 '';45 };46 regenerateOnOwnerRemoved = mkOption {47 default = config.regenerateOnOwnerAdded;48 defaultText = literalExpression "regenerateOnOwnerAdded";49 type = bool;50 description = ''51 Determines secret behavior when owners are removed from the configuration.5253 Typically mirrors regenerateOnOwnerAdded. Override cautiously.54 Set to false if host permissions are revoked through alternative mechanisms like firewall rules.55 '';56 };57 generator = mkOption {58 type = uniq (nullOr (functionTo package));59 description = ''60 Function evaluating to nix derivation responsible for (re)generating the secret's content.6162 An input to this function - `pkgs` of a generator host with implementation-defined representation of extra encryption data,63 use `mkSecretGenerator` helpers to implement own generators.64 '';65 default = null;66 };67 expectedGenerationData = mkOption {68 type = unspecified;69 description = "Contextual metadata embedded within the secret part value";70 default = null;71 };72 expectedPrivateParts = mkOption {73 type = listOf str;74 default = [ ];75 description = "List of parts that are expected to be encrypted";76 };77 expectedPublicParts = mkOption {78 type = listOf str;79 default = [ ];80 description = "List of parts that are expected to be public";81 };82 };83 };84in85{86 options = {87 sharedSecrets = mkOption {88 type = attrsOf (submodule sharedSecret);89 default = { };90 description = "Collection of secrets shared across multiple hosts with configurable ownership";91 };92 };93 config = {94 hosts = mapAttrs (95 _: secretMap:96 let97 partsOf =98 s:99 removeAttrs s [100 "createdAt"101 "expiresAt"102 "generationData"103 ];104105 in106 {107 nixos.data.secrets = mapAttrs (_: s: partsOf s) secretMap;108 109 110 111 }112 ) config.data.hostSecrets;113 nixpkgs.overlays = [114 (final: prev: {115 mkSecretGenerators =116 { recipients }:117 rec {118 119 120 121 mkImpureSecretGenerator =122 {123 script,124 125 126 impureOn ? null,127 parts,128 }:129 (prev.writeShellScript "impureGenerator.sh" ''130 131 set -eu132133 export GENERATOR_HELPER_IDENTITIES="${concatStringsSep "\n" recipients}";134 export PATH=${final.fleet-generator-helper}/bin:$PATH135136 137 tmp=$(mktemp -d)138 cd $tmp139 140141 created_at=$(date -u +"%Y-%m-%dT%H:%M:%S.%NZ")142143 ${script}144145 if ! test -d $out; then146 echo "impure generator script did not produce expected \$out output"147 exit 1148 fi149150 echo -n $created_at > $out/created_at151 echo -n SUCCESS > $out/marker152 '').overrideAttrs153 (old: {154 passthru = {155 inherit impureOn parts;156 generatorKind = "impure";157 };158 });159 160 mkSecretGenerator = { script, parts }: mkImpureSecretGenerator { inherit script parts; };161162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 };191 })192 ];193 };194}