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 };73 };74in75{76 options = {77 sharedSecrets = mkOption {78 type = attrsOf (submodule sharedSecret);79 default = { };80 description = "Collection of secrets shared across multiple hosts with configurable ownership";81 };82 };83 config = {84 hosts = mapAttrs (_: secretMap: {85 nixos.secrets = mapAttrs (86 _: s:87 removeAttrs s [88 "createdAt"89 "expiresAt"90 "generationData"91 ]92 ) secretMap;93 }) config.data.hostSecrets;94 nixpkgs.overlays = [95 (final: prev: {96 mkSecretGenerators =97 { recipients }:98 rec {99 100 101 102 mkImpureSecretGenerator =103 {104 script,105 106 107 impureOn ? null,108 }:109 (prev.writeShellScript "impureGenerator.sh" ''110 111 set -eu112113 export GENERATOR_HELPER_IDENTITIES="${concatStringsSep "\n" recipients}";114 export PATH=${final.fleet-generator-helper}/bin:$PATH115116 117 tmp=$(mktemp -d)118 cd $tmp119 120121 created_at=$(date -u +"%Y-%m-%dT%H:%M:%S.%NZ")122123 ${script}124125 if ! test -d $out; then126 echo "impure generator script did not produce expected \$out output"127 exit 1128 fi129130 echo -n $created_at > $out/created_at131 echo -n SUCCESS > $out/marker132 '').overrideAttrs133 (old: {134 passthru = {135 inherit impureOn;136 generatorKind = "impure";137 };138 });139 140 mkSecretGenerator = { script }: mkImpureSecretGenerator { inherit script; };141142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 };171 })172 ];173 };174}