1{2 lib,3 fleetLib,4 config,5 pkgs,6 ...7}: let8 inherit (builtins) hashString;9 inherit (lib.stringsWithDeps) stringAfter;10 inherit (lib.options) mkOption literalExpression;11 inherit (lib.lists) optional;12 inherit (lib.attrsets) mapAttrs;13 inherit (lib.modules) mkIf;14 inherit (lib.types) submodule str attrsOf nullOr unspecified lazyAttrsOf;15 inherit (fleetLib.strings) decodeRawSecret;1617 sysConfig = config;18 secretPartType = secretName:19 submodule ({config, ...}: let20 partName = config._module.args.name;21 in {22 options = {23 raw = mkOption {24 type = str;25 internal = true;26 description = "Encoded & Encrypted secret part data, passed from fleet.nix";27 };28 hash = mkOption {29 type = str;30 description = "Hash of secret in encoded format";31 };32 path = mkOption {33 type = str;34 description = "Path to secret part, incorporating data hash (thus it will be updated on secret change)";35 };36 stablePath = mkOption {37 type = str;38 description = "Path to secret part, incorporating data hash (thus it will be updated on secret change)";39 };40 data = mkOption {41 type = str;42 description = "Secret public data (only available for plaintext)";43 };44 };45 config = {46 hash = hashString "sha1" config.raw;47 data = decodeRawSecret config.raw;48 path = "/run/secrets/${secretName}/${config.hash}-${partName}";49 stablePath = "/run/secrets/${secretName}/${partName}";50 };51 });52 secretType = submodule ({config, ...}: let53 secretName = config._module.args.name;54 in {55 freeformType = lazyAttrsOf (secretPartType secretName);56 options = {57 shared = mkOption {58 description = "Is this secret owned by this machine, or propagated from shared secrets";59 default = false;60 };6162 generator = mkOption {63 type = nullOr unspecified;64 description = "Derivation to evaluate for secret generation";65 default = null;66 };67 mode = mkOption {68 type = str;69 description = "Secret mode";70 default = "0440";71 };72 owner = mkOption {73 type = str;74 description = "Owner of the secret";75 default = "root";76 };77 group = mkOption {78 type = str;79 description = "Group of the secret";80 default = sysConfig.users.users.${config.owner}.group;81 defaultText = literalExpression "config.users.users.$${owner}.group";82 };83 };84 });85 processPart = part: {86 inherit (part) raw path stablePath;87 };88 processSecret = secret:89 {90 inherit (secret) group mode owner;91 }92 // (mapAttrs (_: processPart) (removeAttrs secret [93 "shared"94 "generator"95 "mode"96 "group"97 "owner"98 ]));99 secretsFile = pkgs.writeTextFile {100 name = "secrets.json";101 text =102 builtins.toJSON (mapAttrs (_: processSecret)103 config.secrets);104 };105 useSysusers = (config.systemd ? sysusers && config.systemd.sysusers.enable) || (config ? userborn && config.userborn.enable);106in {107 options = {108 secrets = mkOption {109 type = attrsOf secretType;110 default = {};111 description = "Host-local secrets";112 };113 };114 config = {115 environment.systemPackages = [pkgs.fleet-install-secrets];116117 systemd.services.fleet-install-secrets = mkIf useSysusers {118 wantedBy = ["sysinit.target"];119 after = ["systemd-sysusers.service"];120 restartTriggers = [121 secretsFile122 ];123 aliases = [124 "sops-install-secrets"125 "agenix-install-secrets"126 ];127128 unitConfig.DefaultDependencies = false;129130 serviceConfig = {131 Type = "oneshot";132 RemainAfterExit = true;133 ExecStart = "${pkgs.fleet-install-secrets}/bin/fleet-install-secrets install ${secretsFile}";134 };135 };136 system.activationScripts.decryptSecrets =137 mkIf (!useSysusers)138 (139 stringAfter (140 [141 142 "users"143 "groups"144 "specialfs"145 ]146 147 148 149 ++ (optional (config.system.activationScripts ? "persist-files") "persist-files")150 ) ''151 1>&2 echo "setting up secrets"152 ${pkgs.fleet-install-secrets}/bin/fleet-install-secrets install ${secretsFile}153 ''154 );155 };156}