{ lib, config, pkgs, inputs, ... }: with lib; with lib.plusultra; let cfg = config.plusultra.services.vault-agent; # nixos-vault-service places generated files here: # https://github.com/DeterminateSystems/nixos-vault-service/blob/45e65627dff5dc4bb40d0f2595916f37e78959c1/module/helpers.nix#L4 secret-files-root = "/tmp/detsys-vault"; environment-files-root = "/run/keys/environment"; create-environment-files-submodule = service-name: types.submodule ({ name, ... }: { options = { text = mkOpt (types.nullOr types.str) null "An inline template for Vault to template."; source = mkOpt (types.nullOr types.path) null "The file with environment variables for Vault to template."; path = mkOption { readOnly = true; type = types.str; description = "The path to the environment file."; default = "${environment-files-root}/${service-name}/${name}.EnvFile"; defaultText = "${environment-files-root}//.EnvFile"; }; }; }); secret-files-submodule = types.submodule ({ name, ... }: { options = { text = mkOpt (types.nullOr types.str) null "An inline template for Vault to template."; source = mkOpt (types.nullOr types.path) null "The file for Vault to template."; permissions = mkOpt types.str "0400" "The octal mode of this file."; change-action = mkOpt (types.nullOr (types.enum [ "restart" "stop" "none" ])) null "The action to take when secrets change."; path = mkOption { readOnly = true; type = types.str; description = "The path to the secret file."; default = "${secret-files-root}/${name}"; }; }; }); services-submodule = types.submodule ({ name, config, ... }: { options = { enable = mkBoolOpt true "Whether to enable Vault Agent for this service."; settings = mkOpt types.attrs { } "Vault Agent configuration."; secrets = { environment = { force = mkOpt types.bool false "Whether or not to force the use of Vault Agent's environment files."; change-action = mkOpt (types.enum [ "restart" "stop" "none" ]) "restart" "The action to take when secrets change."; templates = mkOpt (types.attrsOf (create-environment-files-submodule name)) { } "Environment variable files for Vault to template."; template = mkOpt (types.nullOr (types.either types.path types.str)) null "An environment variable template."; paths = mkOption { readOnly = true; type = types.listOf types.str; description = "Paths to all of the environment files"; default = if config.secrets.environment.template != null then [ "${environment-files-root}/${name}/EnvFile" ] else ( mapAttrsToList (template-name: value: value.path) config.secrets.environment.templates ); }; }; file = { change-action = mkOpt (types.enum [ "restart" "stop" "none" ]) "restart" "The action to take when secrets change."; files = mkOption { description = "Secret files to template."; default = { }; type = types.attrsOf secret-files-submodule; }; }; }; }; }); in { # imports = [ # inputs.vault-service.nixosModules.nixos-vault-service # ]; options.plusultra.services.vault-agent = { enable = mkEnableOption "Vault Agent"; settings = mkOpt types.attrs { } "Default Vault Agent configuration."; services = mkOption { description = "Services to install Vault Agent into."; default = { }; type = types.attrsOf services-submodule; }; }; config = mkIf cfg.enable { assertions = flatten (mapAttrsToList (service-name: service: (mapAttrsToList (template-name: template: { assertion = (template.source != null && template.text == null) || (template.source == null && template.text != null); message = "plusultra.services.vault-agent.services.${service-name}.secrets.environment.templates.${template-name} must set either `source` or `text`."; } ) service.secrets.environment.templates) ++ (mapAttrsToList (file-name: file: { assertion = (file.source != null && file.text == null) || (file.source == null && file.text != null); message = "plusultra.services.vault-agent.services.${service-name}.secrets.file.files.${file-name} must set either `source` or `text`."; } ) service.secrets.file.files) ) cfg.services); systemd.services = mapAttrs (service-name: value: mkIf value.secrets.environment.force { serviceConfig.EnvironmentFile = mkForce value.secrets.environment.paths; }) cfg.services; detsys.vaultAgent = { defaultAgentConfig = cfg.settings; systemd.services = mapAttrs (service-name: value: { inherit (value) enable; agentConfig = value.settings; environment = { changeAction = value.secrets.environment.change-action; templateFiles = mapAttrs (template-name: value: { file = if value.source != null then value.source else pkgs.writeText "${service-name}-${template-name}-env-template" value.text; }) value.secrets.environment.templates; template = if (builtins.isPath value.secrets.environment.template) || (builtins.isNull value.secrets.environment.template) then value.secrets.environment.template else pkgs.writeText "${service-name}-env-template" value.secrets.environment.template; }; secretFiles = { defaultChangeAction = value.secrets.file.change-action; files = mapAttrs (file-name: value: { changeAction = value.change-action; template = value.text; templateFile = value.source; perms = value.permissions; }) value.secrets.file.files; }; }) cfg.services; }; }; }