1{2 lib,3 fleetLib,4 config,5 ...6}:7with lib;8with fleetLib; let9 sharedSecret = with types; ({config, ...}: {10 freeformType = types.lazyAttrsOf unspecified;11 options = {12 expectedOwners = mkOption {13 type = nullOr (listOf str);14 description = ''15 List of hosts to encrypt secret for. null if managed by user (= via owners field from fleet.nix)1617 Secrets would be decrypted and stored to /run/secrets/$\{name} on owners18 '';19 default = null;20 };21 22 regenerateOnOwnerAdded = mkOption {23 type = bool;24 description = ''25 Is this secret owner-dependent, and needs to be regenerated on ownership set change, or it may be just reencrypted.2627 You want to have this option set to true, when this secret contains some reference to its owners, i.e x509 SANs.28 '';29 };30 regenerateOnOwnerRemoved = mkOption {31 default = config.regenerateOnOwnerAdded;32 type = bool;33 description = ''34 Should this secret be removed on owner removal, or it may be just reencrypted3536 Most probably its value should be equal to regenerateOnOwnerAdded, override only if you know what are you doing.37 Contrary to regenerateOnOwnerAdded, you may want to set this option to false, when host permissions are revoked38 in some other way than by this secret ownership, I.e by firewall/etc.39 '';40 };41 generator = mkOption {42 type = nullOr unspecified;43 description = "Derivation to evaluate for secret generation";44 default = null;45 };46 createdAt = mkOption {47 type = nullOr str;48 description = "When this secret was (re)generated";49 default = null;50 };51 expiresAt = mkOption {52 type = nullOr str;53 description = "On which date this secret will expire, someone should regenerate this secret before it expires.";54 default = null;55 };5657 owners = mkOption {58 type = listOf str;59 description = ''60 For which owners this secret is currently encrypted,61 if not matches expectedOwners - then this secret is considered outdated, and62 should be regenerated/reencrypted.6364 Imported from fleet.nix65 '';66 default = [];67 };68 };69 });70 hostSecret = with types; {71 freeformType = types.lazyAttrsOf unspecified;72 options = {73 createdAt = mkOption {74 type = nullOr str;75 default = null;76 };77 expiresAt = mkOption {78 type = nullOr str;79 default = null;80 };81 };82 };83in {84 options = with types; {85 version = mkOption {86 type = str;87 default = "";88 internal = true;89 };90 sharedSecrets = mkOption {91 type = attrsOf (submodule sharedSecret);92 default = {};93 description = "Shared secrets";94 };95 hostSecrets = mkOption {96 type = attrsOf (attrsOf (submodule hostSecret));97 default = {};98 description = "Host secrets. Imported from fleet.nix";99 internal = true;100 };101 };102 config = {103 assertions =104 mapAttrsToList105 (name: secret: {106 assertion = secret.expectedOwners == null || builtins.sort (a: b: a < b) secret.owners == builtins.sort (a: b: a < b) secret.expectedOwners;107 message = "Shared secret ${name} is expected to be encrypted for ${builtins.toJSON secret.expectedOwners}, but it is encrypted for ${builtins.toJSON secret.owners}. Run fleet secrets regenerate to fix";108 })109 config.sharedSecrets;110 hosts = hostsToAttrs (host: {111 nixosModules = let112 113 processSecret = v:114 (removeAttrs v ["createdAt" "expiresAt" "expectedOwners" "owners" "regenerateOnOwnerAdded" "regenerateOnOwnerRemoved"])115 // {116 shared = true;117 };118 in [119 {120 secrets =121 (122 mapAttrs (_: processSecret)123 (filterAttrs (_: v: builtins.elem host v.owners) config.sharedSecrets)124 )125 // (mapAttrs (_: processSecret) (config.hostSecrets.${host} or {}));126 }127 ];128 });129 130 overlays = [131 (final: prev: let132 lib = final.lib;133 inherit (lib) strings concatMap;134 inherit (strings) escapeShellArgs;135 in {136 mkEncryptSecret = {137 rage ? prev.rage,138 recipients,139 }:140 prev.writeShellScript "encryptor" ''141 142 exec ${rage}/bin/rage ${escapeShellArgs (concatMap (r: ["-r" r]) recipients)} -e "$@"143 '';144 145 146 147 148 mkImpureSecretGenerator = {149 script,150 151 152 impureOn ? null,153 }:154 (prev.writeShellScript "impureGenerator.sh" ''155 156 set -eu157158 159 tmp=$(mktemp -d)160 cd $tmp161 162163 created_at=$(date -u +"%Y-%m-%dT%H:%M:%S.%NZ")164165 ${script}166167 if ! test -d $out; then168 echo "impure generator script did not produce expected \$out output"169 exit 1170 fi171172 echo -n $created_at > $out/created_at173 echo -n SUCCESS > $out/marker174 '')175 .overrideAttrs (old: {176 passthru = {177 inherit impureOn;178 generatorKind = "impure";179 };180 });181 182 mkSecretGenerator = {script}: final.mkImpureSecretGenerator {inherit script;};183184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 })213 ];214 };215}