1{2 lib,3 fleetLib,4 config,5 pkgs,6 ...7}:8let9 inherit (builtins)10 hashString11 toJSON12 ;13 inherit (lib.stringsWithDeps) stringAfter;14 inherit (lib.options) mkOption literalExpression;15 inherit (lib.lists) optional;16 inherit (lib.attrsets) mapAttrs;17 inherit (lib.modules) mkIf;18 inherit (lib.types)19 submodule20 str21 attrsOf22 nullOr23 unspecified24 uniq25 functionTo26 package27 ;28 inherit (fleetLib.strings) decodeRawSecret;2930 sysConfig = config;31 secretPartType =32 secretName:33 submodule (34 { config, ... }:35 let36 partName = config._module.args.name;37 in38 {39 options = {40 hash = mkOption {41 type = str;42 description = "Hash of secret in encoded format";43 };44 path = mkOption {45 type = str;46 description = "Path to secret part, incorporating data hash (thus it will be updated on secret change)";47 };48 stablePath = mkOption {49 type = str;50 description = "Path to secret part, stable path (users are expected to watch for file changes/re-read secret on demand)";51 };52 data = mkOption {53 type = str;54 description = "Secret public data (only available for plaintext)";55 };56 raw = mkOption {57 type = str;58 description = "Raw (encoded/encrypted secret part data)";59 };60 };61 config = {62 hash = hashString "sha1" config.raw;63 data = decodeRawSecret config.raw;64 path = "/run/secrets/${secretName}/${config.hash}-${partName}";65 stablePath = "/run/secrets/${secretName}/${partName}";66 };67 }68 );69 secretType = submodule (70 {71 config,72 ...73 }:74 let75 secretName = config._module.args.name;76 in77 {78 options = {79 parts = mkOption {80 type = attrsOf (secretPartType secretName);81 description = "Definition of secret parts";82 };83 generator = mkOption {84 type = uniq (nullOr (functionTo package));85 description = "Derivation to evaluate for secret generation";86 default = null;87 };88 mode = mkOption {89 type = str;90 description = "Secret mode";91 default = "0440";92 };93 owner = mkOption {94 type = str;95 description = "Owner of the secret";96 default = "root";97 };98 group = mkOption {99 type = str;100 description = "Group of the secret";101 default = sysConfig.users.users.${config.owner}.group;102 defaultText = literalExpression "config.users.users.$${owner}.group";103 };104 };105 config = {106 parts = builtins.fleetEnsureHostSecret sysConfig.networking.hostName secretName config.generator;107 };108 }109 );110 secretsData = (mapAttrs (_: s: s.definition) config.secrets);111 secretsFile = pkgs.writeTextFile {112 name = "secrets.json";113 text = toJSON secretsData;114 };115 useSysusers =116 (config.systemd ? sysusers && config.systemd.sysusers.enable)117 || (config ? userborn && config.userborn.enable);118in119{120 options = {121 secrets = mkOption {122 type = attrsOf secretType;123 default = { };124 apply = v: (mapAttrs (_: secret: secret.parts // { definition = secret; }) v);125 description = "Host-local secrets";126 };127 system.secretsData = mkOption {128 type = unspecified;129 default = { };130 description = "secrets.json contents";131 };132 };133 config = {134 system = { inherit secretsData; };135 environment.systemPackages = [ pkgs.fleet-install-secrets ];136137 systemd.services.fleet-install-secrets = mkIf useSysusers {138 wantedBy = [ "sysinit.target" ];139 after = [ "systemd-sysusers.service" ];140 restartTriggers = [141 secretsFile142 ];143 aliases = [144 "sops-install-secrets"145 "agenix-install-secrets"146 ];147148 unitConfig.DefaultDependencies = false;149150 serviceConfig = {151 Type = "oneshot";152 RemainAfterExit = true;153 ExecStart = "${pkgs.fleet-install-secrets}/bin/fleet-install-secrets install ${secretsFile}";154 };155 };156 system.activationScripts.decryptSecrets = mkIf (!useSysusers) (157 stringAfter158 (159 [160 161 "users"162 "groups"163 "specialfs"164 ]165 166 167 168 ++ (optional (config.system.activationScripts ? "persist-files") "persist-files")169 )170 ''171 1>&2 echo "setting up secrets"172 ${pkgs.fleet-install-secrets}/bin/fleet-install-secrets install ${secretsFile}173 ''174 );175 };176}