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 = {host, ...}: {87 _providedSharedSecrets = filter (name: elem host.name config.secrets.${name}.expectedOwners) (attrNames config.secrets);88 };89 nixpkgs.overlays = [90 (final: prev: {91 mkSecretGenerators =92 { recipients }:93 rec {94 95 96 97 mkImpureSecretGenerator =98 {99 script,100 101 102 impureOn ? null,103 generationData ? null,104 allowDifferent ? true,105 parts,106 }:107 (prev.writeShellScript "impureGenerator.sh" ''108 109 set -eu110111 export GENERATOR_HELPER_IDENTITIES="${concatStringsSep "\n" recipients}";112 export PATH=${final.fleet-generator-helper}/bin:$PATH113114 115 tmp=$(mktemp -d)116 cd $tmp117 118119 created_at=$(date -u +"%Y-%m-%dT%H:%M:%S.%NZ")120121 ${script}122123 if ! test -d $out; then124 echo "impure generator script did not produce expected \$out output"125 exit 1126 fi127128 echo -n $created_at > $out/created_at129 echo -n SUCCESS > $out/marker130 '').overrideAttrs131 (old: {132 passthru = {133 inherit134 impureOn135 parts136 generationData137 allowDifferent138 ;139 generatorKind = "impure";140 };141 });142 143 mkSecretGenerator = { script, parts }: mkImpureSecretGenerator { inherit script parts; };144145 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 })175 ];176 };177}