From 06495a406e872791bbd01563d3aabf2d4ea53c42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Thu, 27 Oct 2022 13:55:27 +0200 Subject: [PATCH] add vault.secrets options to systemd services --- nix/checks/nixos-test.nix | 32 +++++------ nix/modules/systemd-vaultd.nix | 4 ++ nix/modules/vault-secrets.nix | 100 +++++++++++++++++++++++++++++++++ 3 files changed, 118 insertions(+), 18 deletions(-) create mode 100644 nix/modules/vault-secrets.nix diff --git a/nix/checks/nixos-test.nix b/nix/checks/nixos-test.nix index 21af67b..f9be5d2 100644 --- a/nix/checks/nixos-test.nix +++ b/nix/checks/nixos-test.nix @@ -78,7 +78,12 @@ in { serviceConfig = { Type = "oneshot"; RemainAfterExit = true; - LoadCredential = ["foo:/run/systemd-vaultd/sock"]; + }; + vault = { + template = '' + {{ with secret "secret/my-secret" }}{{ .Data.data | toJSON }}{{ end }} + ''; + secrets.foo = {}; }; }; @@ -92,28 +97,19 @@ in { RemainAfterExit = true; LoadCredential = ["secret:/run/systemd-vaultd/sock"]; }; + vault = { + template = '' + {{ with secret "secret/blocking-secret" }}{{ scratch.MapSet "secrets" "secret" .Data.data.foo }}{{ end }} + {{ scratch.Get "secrets" | explodeMap | toJSON }} + ''; + secrets.secret = {}; + }; }; - services.vault.agents.test.settings = { + services.vault.agents.default.settings = { vault = { address = "http://localhost:8200"; }; - template = [ - { - contents = '' - {{ with secret "secret/my-secret" }}{{ .Data.data | toJSON }}{{ end }} - ''; - destination = "/run/systemd-vaultd/secrets/service1.service.json"; - } - { - contents = '' - {{ with secret "secret/blocking-secret" }}{{ scratch.MapSet "secrets" "secret" .Data.data.foo }}{{ end }} - {{ scratch.Get "secrets" | explodeMap | toJSON }} - ''; - destination = "/run/systemd-vaultd/secrets/service2.service.json"; - } - ]; - auto_auth = { method = [ { diff --git a/nix/modules/systemd-vaultd.nix b/nix/modules/systemd-vaultd.nix index 89e359c..aa42d5a 100644 --- a/nix/modules/systemd-vaultd.nix +++ b/nix/modules/systemd-vaultd.nix @@ -6,6 +6,10 @@ }: let systemd-vaultd = pkgs.callPackage ../../default.nix {}; in { + imports = [ + ./vault-secrets.nix + ]; + systemd.sockets.systemd-vaultd = { description = "systemd-vaultd socket"; wantedBy = ["sockets.target"]; diff --git a/nix/modules/vault-secrets.nix b/nix/modules/vault-secrets.nix new file mode 100644 index 0000000..70091f2 --- /dev/null +++ b/nix/modules/vault-secrets.nix @@ -0,0 +1,100 @@ +{ + lib, + config, + pkgs, + ... +}: let + secretType = serviceName: + lib.types.submodule ({config, ...}: { + options = { + name = lib.mkOption { + type = lib.types.str; + default = config._module.args.name; + description = '' + Name of the secret used in LoadCredential + ''; + }; + path = lib.mkOption { + type = lib.types.str; + default = "/run/credentials/${serviceName}.service/${config.name}"; + defaultText = "/run/credentials/$service.service/$name"; + description = '' + Absolute path to systemd's loaded credentials. + WARNING: Using this path might break if systemd in future decides to use + a different location but /run/credentials + ''; + }; + }; + }); + + services = config.systemd.services; + + getTemplate = serviceName: vaultConfig: + { + contents = vaultConfig.template; + destination = "/run/systemd-vaultd/secrets/${serviceName}.service.json"; + perms = "0400"; + } + // lib.optionalAttrs (vaultConfig.changeAction != null) { + command = "systemctl ${ + if vaultConfig.changeAction == "restart" + then "try-restart" + else "try-reload-or-restart" + } ${lib.escapeShellArg "${serviceName}.service"}"; + }; + + vaultTemplates = config: + lib.mapAttrsToList + (serviceName: service: + getTemplate serviceName services.${serviceName}.vault) + (lib.filterAttrs (n: v: v.vault.secrets != {} && v.vault.agent == config._module.args.name) services); +in { + options = { + systemd.services = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { + options.vault = { + changeAction = lib.mkOption { + description = "What to do if any secrets in the environment change."; + type = lib.types.nullOr (lib.types.enum [ + "none" + "reload" + "restart" + ]); + default = "none"; + }; + + template = lib.mkOption { + type = lib.types.str; + description = '' + The vault agent template to use for this secret + ''; + }; + + agent = lib.mkOption { + type = lib.types.str; + default = "default"; + description = '' + Agent instance to use for this service + ''; + }; + + secrets = lib.mkOption { + type = lib.types.attrsOf (secretType config._module.args.name); + default = {}; + description = "List of secrets to load from vault agent template"; + example = { + some-secret.template = ''{{ with secret "secret/some-secret" }}{{ .Data.data.some-key }}{{ end }}''; + }; + }; + }; + config.serviceConfig.LoadCredential = lib.mapAttrsToList (_: config: "${config.name}:/run/systemd-vaultd/sock") config.vault.secrets; + })); + }; + + services.vault.agents = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { + config.settings.template = vaultTemplates config; + })); + }; + }; +}