1{2 lib,3 config,4 pkgs,5 ...6}: let7 inherit (lib.strings) hasPrefix removePrefix;8 inherit (lib) mkOption mkOptionDefault mapAttrs stringAfter;9 inherit (lib.types) submodule str attrsOf nullOr unspecified lazyAttrsOf;10 plaintextPrefix = "<PLAINTEXT>";11 plaintextNewlinePrefix = "<PLAINTEXT-NL>";1213 sysConfig = config;14 secretPartType = secretName:15 submodule ({config, ...}: {16 options = {17 raw = mkOption {18 description = "Secret in fleet-specific undocumented format, do not use. Import from fleet.nix";19 internal = true;20 };21 hash = mkOption {22 type = str;23 description = "Hash of secret in encoded format";24 };25 path = mkOption {26 type = str;27 description = "Path to secret part, incorporating data hash (thus it will be updated on secret change)";28 };29 stablePath = mkOption {30 type = str;31 description = "Path to secret part, incorporating data hash (thus it will be updated on secret change)";32 };33 data = mkOption {34 type = str;35 description = "Secret public data (only available for plaintext)";36 };37 };38 config = let39 partName = config._module.args.name;40 in {41 hash = mkOptionDefault (builtins.hashString "sha1" config.raw);42 data = mkOptionDefault (43 if hasPrefix plaintextPrefix config.raw44 then removePrefix plaintextPrefix config.raw45 else if hasPrefix plaintextNewlinePrefix config.raw46 then removePrefix plaintextNewlinePrefix config.raw47 else throw "secret.part.data attribute only works for public plaintext secret parts, got ${config.raw}"48 );49 path = mkOptionDefault "/run/secrets/${secretName}/${config.hash}-${partName}";50 stablePath = mkOptionDefault "/run/secrets/${secretName}/${partName}";51 };52 });53 secretType = submodule ({config, ...}: let54 secretName = config._module.args.name;55 in {56 freeformType = lazyAttrsOf (secretPartType secretName);57 options = {58 shared = mkOption {59 description = "Is this secret owned by this machine, or propagated from shared secrets";60 default = false;61 };62 expectedOwners = mkOption {63 type = nullOr unspecified;64 default = null;65 internal = true;66 };6768 generator = mkOption {69 type = nullOr unspecified;70 description = "Derivation to evaluate for secret generation";71 default = null;72 };73 mode = mkOption {74 type = str;75 description = "Secret mode";76 default = "0440";77 };78 owner = mkOption {79 type = str;80 description = "Owner of the secret";81 default = "root";82 };83 group = mkOption {84 type = str;85 description = "Group of the secret";86 default = sysConfig.users.users.${config.owner}.group;87 };88 };89 });90 processPart = part: {91 inherit (part) raw path stablePath;92 };93 processSecret = secret:94 {95 inherit (secret) group mode owner;96 }97 // (mapAttrs (_: processPart) (removeAttrs secret [98 "shared"99 "generator"100 "mode"101 "group"102 "owner"103104 105 "expectedOwners"106 ]));107 secretsFile = pkgs.writeTextFile {108 name = "secrets.json";109 text =110 builtins.toJSON (mapAttrs (_: processSecret)111 config.secrets);112 };113in {114 options = {115 secrets = mkOption {116 type = attrsOf secretType;117 default = {};118 description = "Host-local secrets";119 };120 };121 config = {122 environment.systemPackages = [pkgs.fleet-install-secrets];123 system.activationScripts.decryptSecrets =124 stringAfter (125 [126 127 "users"128 "groups"129 "specialfs"130 ]131 132 133 134 ++ (lib.optional (config.system.activationScripts ? "persist-files") "persist-files")135 ) ''136 1>&2 echo "setting up secrets"137 ${pkgs.fleet-install-secrets}/bin/fleet-install-secrets install ${secretsFile}138 '';139 };140}