1{2 lib,3 config,4 ...5}:6let7 inherit (lib.options) mkOption;8 inherit (lib.types)9 nullOr10 listOf11 str12 bool13 attrsOf14 submodule15 functionTo16 package17 uniq18 ;19 inherit (lib.strings) concatStringsSep;20 inherit (lib.lists) elem filter;21 inherit (lib.attrsets) attrNames;2223 sharedSecret =24 { config, ... }:25 {26 options = {27 expectedOwners = mkOption {28 type = listOf str;29 description = ''30 Specifies the list of hosts authorized to decrypt and access this shared secret.31 '';32 };33 regenerateOnOwnerAdded = mkOption {34 type = bool;35 description = ''36 Whether the secret prefers to be rotated when new owners are added.3738 Note that this is only a security measure, if the secret needs to be regenerated due to e.g X.509 SANs39 changes - then you most likely want to use generationData for that instead.40 '';41 default = false;42 };43 regenerateOnOwnerRemoved = mkOption {44 type = bool;45 description = ''46 Whether the secret prefers to be rotated when the owners are removed, so the encrypted data47 stored in fleet state can't be decrypted by those. Note that the secrets are still present in encrypted48 form on those hosts until gc happens.49 '';50 default = false;51 };52 allowDifferent = mkOption {53 type = bool;54 description = ''55 When adding owner, do not update secret value for other owners, instead creating a new distribution.5657 Defaults to true, since all secrets might differ on hosts on some point of deployment process.5859 Secret generator might also have opinion on this, like it makes little sense for askPass/synchronizing60 generators to keep old data.61 '';62 default = true;63 };64 generator = mkOption {65 type = uniq (nullOr (functionTo package));66 description = ''67 Function evaluating to nix derivation responsible for (re)generating the secret's content.6869 An input to this function - `pkgs` of a generator host with implementation-defined representation of extra encryption data,70 use `mkSecretGenerator` helpers to implement own generators.71 '';72 default = null;73 };74 };75 };76in77{78 options = {79 secrets = mkOption {80 type = attrsOf (submodule sharedSecret);81 default = { };82 description = "Collection of secrets shared across multiple hosts with configurable ownership";83 };84 };85 config = {86 nixos =87 { host, ... }:88 {89 _providedSharedSecrets = filter (name: elem host.name config.secrets.${name}.expectedOwners) (90 attrNames config.secrets91 );92 };93 nixpkgs.overlays = [94 (final: prev: {95 mkSecretGenerators =96 { recipients }:97 rec {98 99 100 101 mkImpureSecretGenerator =102 {103 script,104 105 106 impureOn ? null,107 generationData ? null,108 allowDifferent ? true,109 parts,110 }:111 (prev.writeShellScript "impureGenerator.sh" ''112 113 set -eu114115 export GENERATOR_HELPER_IDENTITIES="${concatStringsSep "\n" recipients}";116 export PATH=${final.fleet-generator-helper}/bin:$PATH117118 119 tmp=$(mktemp -d)120 cd $tmp121 122123 created_at=$(date -u +"%Y-%m-%dT%H:%M:%S.%NZ")124125 ${script}126127 if ! test -d $out; then128 echo "impure generator script did not produce expected \$out output"129 exit 1130 fi131132 echo -n $created_at > $out/created_at133 echo -n SUCCESS > $out/marker134 '').overrideAttrs135 (old: {136 passthru = {137 inherit138 impureOn139 parts140 generationData141 allowDifferent142 ;143 generatorKind = "impure";144 };145 });146 147 mkSecretGenerator = { script, parts }: mkImpureSecretGenerator { inherit script parts; };148149 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 175 176 177 };178 })179 ];180 };181}