apply treefmt

main
Jörg Thalheim 2 years ago
parent cc78160e6e
commit 16ab6ae069

@ -1,9 +1,10 @@
# systemd-vaultd - load vault credentials with systemd units # systemd-vaultd - load vault credentials with systemd units
> Mostly written in a train > Mostly written in a train
- Jörg Thalheim - Jörg Thalheim
systemd-vaultd is a proxy between systemd and [vault agent](https://vaultproject.io). systemd-vaultd is a proxy between systemd and [vault agent](https://vaultproject.io).
It provides a unix socket that can be used in systemd services in the It provides a unix socket that can be used in systemd services in the
`LoadCredential` option and then waits for vault agent to write these secrets in `LoadCredential` option and then waits for vault agent to write these secrets in
json format at `/run/systemd-vaultd/<service_name>.service.json`. json format at `/run/systemd-vaultd/<service_name>.service.json`.
@ -61,19 +62,19 @@ vault agent is then expected to write secrets to `/run/systemd-vaultd/` in json
template { template {
# this exposes all secrets in `secret/my-secret` to the service # this exposes all secrets in `secret/my-secret` to the service
contents = "#{{ with secret \"secret/my-secret\" }}{{ .Data.data | toJSON }}{{ end }}" contents = "#{{ with secret \"secret/my-secret\" }}{{ .Data.data | toJSON }}{{ end }}"
# an alternative is to expose only selected secrets like this: # an alternative is to expose only selected secrets like this:
# contents = <<EOF # contents = <<EOF
# {{ with secret "secret/my-secret" }}{{ scratch.MapSet "secrets" "foobar" .Data.data.foo }}{{ end }} # {{ with secret "secret/my-secret" }}{{ scratch.MapSet "secrets" "foobar" .Data.data.foo }}{{ end }}
# {{ scratch.Get "foobar" | explodeMap | toJSON }} # {{ scratch.Get "foobar" | explodeMap | toJSON }}
# EOF # EOF
destination = "/run/systemd-vaultd/secrets/myservice.service.json" destination = "/run/systemd-vaultd/secrets/myservice.service.json"
} }
``` ```
When `myservice` is started, systemd will open a connection to When `myservice` is started, systemd will open a connection to
`systemd-vaultd`'s socket. `systemd-vaultd` then either serve the secrets `systemd-vaultd`'s socket. `systemd-vaultd` then either serve the secrets
from `/run/systemd-vaultd/secrets/myservice.service.json` or it waits with from `/run/systemd-vaultd/secrets/myservice.service.json` or it waits with
inotify on secret directory for vault agent to write the secret. inotify on secret directory for vault agent to write the secret.

@ -1,4 +1,4 @@
{pkgs ? import <nixpkgs> {}}: { pkgs ? import <nixpkgs> { } }:
pkgs.buildGoModule { pkgs.buildGoModule {
name = "systemd-vaultd"; name = "systemd-vaultd";
src = ./.; src = ./.;
@ -7,7 +7,7 @@ pkgs.buildGoModule {
description = "A proxy for secrets between systemd services and vault"; description = "A proxy for secrets between systemd services and vault";
homepage = "https://github.com/numtide/systemd-vaultd"; homepage = "https://github.com/numtide/systemd-vaultd";
license = licenses.mit; license = licenses.mit;
maintainers = with maintainers; [mic92]; maintainers = with maintainers; [ mic92 ];
platforms = platforms.unix; platforms = platforms.unix;
}; };
} }

@ -9,37 +9,34 @@
treefmt-nix.url = "github:numtide/treefmt-nix"; treefmt-nix.url = "github:numtide/treefmt-nix";
}; };
outputs = inputs @ {flake-parts, ...}: outputs = inputs @ { flake-parts, ... }:
flake-parts.lib.mkFlake {inherit inputs;} { flake-parts.lib.mkFlake { inherit inputs; } {
systems = ["x86_64-linux" "aarch64-linux"]; systems = [ "x86_64-linux" "aarch64-linux" ];
imports = [ imports = [
./nix/checks/flake-module.nix ./nix/checks/flake-module.nix
]; ];
perSystem = { perSystem =
config, { config
self', , pkgs
inputs', , ...
pkgs, }: {
system, packages.default = pkgs.callPackage ./default.nix { };
... devShells.default = pkgs.mkShellNoCC {
}: { buildInputs = with pkgs; [
packages.default = pkgs.callPackage ./default.nix {}; python3.pkgs.pytest
devShells.default = pkgs.mkShellNoCC { python3.pkgs.mypy
buildInputs = with pkgs; [
python3.pkgs.pytest
python3.pkgs.mypy
golangci-lint golangci-lint
vault vault
systemd systemd
hivemind hivemind
go go
just just
config.packages.treefmt config.packages.treefmt
]; ];
}; };
}; };
flake.nixosModules = { flake.nixosModules = {
vaultAgent = ./nix/modules/vault-agent.nix; vaultAgent = ./nix/modules/vault-agent.nix;
systemdVaultd = ./nix/modules/systemd-vaultd.nix; systemdVaultd = ./nix/modules/systemd-vaultd.nix;

@ -1,10 +1,8 @@
{ { config
config, , pkgs
lib, , ...
pkgs,
...
}: { }: {
environment.systemPackages = [pkgs.vault]; environment.systemPackages = [ pkgs.vault ];
services.vault = { services.vault = {
enable = true; enable = true;
dev = true; dev = true;
@ -14,8 +12,8 @@
environment.variables.VAULT_TOKEN = config.services.vault.devRootTokenID; environment.variables.VAULT_TOKEN = config.services.vault.devRootTokenID;
systemd.services.setup-vault-agent-approle = { systemd.services.setup-vault-agent-approle = {
path = [pkgs.jq pkgs.vault pkgs.systemd]; path = [ pkgs.jq pkgs.vault pkgs.systemd ];
wantedBy = ["multi-user.target"]; wantedBy = [ "multi-user.target" ];
serviceConfig = { serviceConfig = {
Type = "oneshot"; Type = "oneshot";
@ -51,7 +49,7 @@
# Make sure our setup service is started before our vault-agent # Make sure our setup service is started before our vault-agent
systemd.services.vault-agent-test = { systemd.services.vault-agent-test = {
wants = ["setup-vault-agent-approle.service"]; wants = [ "setup-vault-agent-approle.service" ];
after = ["setup-vault-agent-approle.service"]; after = [ "setup-vault-agent-approle.service" ];
}; };
} }

@ -1,48 +1,46 @@
{inputs, ...}: { { inputs, ... }: {
imports = [ imports = [
inputs.treefmt-nix.flakeModule inputs.treefmt-nix.flakeModule
]; ];
perSystem = { perSystem =
self', { pkgs
inputs', , ...
pkgs, }: {
system, treefmt = {
... # Used to find the project root
}: { projectRootFile = "flake.lock";
treefmt = {
# Used to find the project root
projectRootFile = "flake.lock";
programs.gofumpt.enable = true; programs.gofumpt.enable = true;
programs.prettier.enable = true; programs.prettier.enable = true;
settings.formatter = { settings.formatter = {
nix = { nix = {
command = "sh"; command = "sh";
options = [ options = [
"-eucx" "-eucx"
'' ''
# First deadnix # First deadnix
${pkgs.lib.getExe pkgs.deadnix} --edit "$@" ${pkgs.lib.getExe pkgs.deadnix} --edit "$@"
# Then nixpkgs-fmt # Then nixpkgs-fmt
${pkgs.lib.getExe pkgs.nixpkgs-fmt} "$@" ${pkgs.lib.getExe pkgs.nixpkgs-fmt} "$@"
'' ''
"--" "--"
]; ];
includes = ["*.nix"]; includes = [ "*.nix" ];
}; };
python = { python = {
command = "sh"; command = "sh";
options = [ options = [
"-eucx" "-eucx"
'' ''
${pkgs.lib.getExe pkgs.ruff} --fix "$@" ${pkgs.lib.getExe pkgs.ruff} --fix "$@"
${pkgs.lib.getExe pkgs.python3.pkgs.black} "$@" ${pkgs.lib.getExe pkgs.python3.pkgs.black} "$@"
'' ''
"--" # this argument is ignored by bash "--" # this argument is ignored by bash
]; ];
includes = ["*.py"]; includes = [ "*.py" ];
};
}; };
}; };
@ -56,13 +54,4 @@
inherit (nixosTests) unittests vault-agent systemd-vaultd; inherit (nixosTests) unittests vault-agent systemd-vaultd;
}; };
}; };
checks = let
nixosTests = pkgs.callPackages ./nix/checks/nixos-test.nix {
makeTest = import (pkgs.path + "/nixos/tests/make-test-python.nix");
};
in {
inherit (nixosTests) unittests vault-agent systemd-vaultd;
};
};
} }

@ -1,13 +1,15 @@
{ { makeTest ? import <nixpkgs/nixos/tests/make-test-python.nix>
makeTest ? import <nixpkgs/nixos/tests/make-test-python.nix>, , pkgs ? (import <nixpkgs> { })
pkgs ? (import <nixpkgs> {}), ,
}: let }:
let
makeTest' = args: makeTest' = args:
makeTest args { makeTest args {
inherit pkgs; inherit pkgs;
inherit (pkgs) system; inherit (pkgs) system;
}; };
in { in
{
vault-agent = makeTest' (import ./vault-agent-test.nix); vault-agent = makeTest' (import ./vault-agent-test.nix);
systemd-vaultd = makeTest' (import ./systemd-vaultd-test.nix); systemd-vaultd = makeTest' (import ./systemd-vaultd-test.nix);
unittests = makeTest' { unittests = makeTest' {

@ -1,76 +1,76 @@
{ {
name = "systemd-vaultd"; name = "systemd-vaultd";
nodes.server = { nodes.server =
config, { config
pkgs, , pkgs
... , ...
}: { }: {
imports = [ imports = [
../modules/vault-agent.nix ../modules/vault-agent.nix
../modules/systemd-vaultd.nix ../modules/systemd-vaultd.nix
./dev-vault-server.nix ./dev-vault-server.nix
]; ];
systemd.services.service1 = { systemd.services.service1 = {
wantedBy = ["multi-user.target"]; wantedBy = [ "multi-user.target" ];
script = '' script = ''
cat $CREDENTIALS_DIRECTORY/foo > /tmp/service1 cat $CREDENTIALS_DIRECTORY/foo > /tmp/service1
echo -n "$SECRET_ENV" > /tmp/service1-env echo -n "$SECRET_ENV" > /tmp/service1-env
'';
#serviceConfig = {
# EnvironmentFile = [ "/run/systemd-vaultd/service1.service.EnvironmentFile" ];
#};
vault = {
template = ''
{{ with secret "secret/my-secret" }}{{ .Data.data | toJSON }}{{ end }}
'';
secrets.foo = {};
environmentTemplate = ''
{{ with secret "secret/my-secret" }}
SECRET_ENV={{ .Data.data.foo }}
{{ end }}
''; '';
#serviceConfig = {
# EnvironmentFile = [ "/run/systemd-vaultd/service1.service.EnvironmentFile" ];
#};
vault = {
template = ''
{{ with secret "secret/my-secret" }}{{ .Data.data | toJSON }}{{ end }}
'';
secrets.foo = { };
environmentTemplate = ''
{{ with secret "secret/my-secret" }}
SECRET_ENV={{ .Data.data.foo }}
{{ end }}
'';
};
}; };
};
systemd.services.service2 = { systemd.services.service2 = {
wantedBy = ["multi-user.target"]; wantedBy = [ "multi-user.target" ];
script = '' script = ''
set -x set -x
while true; do while true; do
cat $CREDENTIALS_DIRECTORY/secret > /tmp/service2 cat $CREDENTIALS_DIRECTORY/secret > /tmp/service2
sleep 0.1 sleep 0.1
done done
'';
serviceConfig.ExecReload = "${pkgs.coreutils}/bin/true";
serviceConfig.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 = {}; serviceConfig.ExecReload = "${pkgs.coreutils}/bin/true";
serviceConfig.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.default.settings = { services.vault.agents.default.settings = {
vault = { vault = {
address = "http://localhost:8200"; address = "http://localhost:8200";
}; };
auto_auth = { auto_auth = {
method = [ method = [
{ {
type = "approle"; type = "approle";
config = { config = {
role_id_file_path = "/tmp/roleID"; role_id_file_path = "/tmp/roleID";
secret_id_file_path = "/tmp/secretID"; secret_id_file_path = "/tmp/secretID";
remove_secret_id_file_after_reading = false; remove_secret_id_file_after_reading = false;
}; };
} }
]; ];
};
}; };
}; };
};
testScript = '' testScript = ''
start_all() start_all()
machine.wait_for_unit("vault.service") machine.wait_for_unit("vault.service")

@ -1,22 +1,23 @@
{ { writeShellScript
writeShellScript, , python3
python3, , pkgs
pkgs, , lib
lib, , coreutils
coreutils, ,
}: let }:
systemd-vaultd = pkgs.callPackage ../../default.nix {}; let
systemd = pkgs.callPackage ../pkgs/systemd.nix {}; systemd-vaultd = pkgs.callPackage ../../default.nix { };
systemd = pkgs.callPackage ../pkgs/systemd.nix { };
in in
writeShellScript "unittests" '' writeShellScript "unittests" ''
set -eu -o pipefail set -eu -o pipefail
export PATH=${lib.makeBinPath [python3.pkgs.pytest coreutils systemd]} export PATH=${lib.makeBinPath [python3.pkgs.pytest coreutils systemd]}
export SYSTEMD_VAULTD_BIN=${systemd-vaultd}/bin/systemd-vaultd export SYSTEMD_VAULTD_BIN=${systemd-vaultd}/bin/systemd-vaultd
export TMPDIR=$(mktemp -d) export TMPDIR=$(mktemp -d)
trap 'rm -rf $TMPDIR' EXIT trap 'rm -rf $TMPDIR' EXIT
cp --no-preserve=mode --preserve=timestamps -r ${../..} "$TMPDIR/source" cp --no-preserve=mode --preserve=timestamps -r ${../..} "$TMPDIR/source"
cd "$TMPDIR/source" cd "$TMPDIR/source"
pytest -s ./tests pytest -s ./tests
# we need this in our nixos tests # we need this in our nixos tests
touch /tmp/success touch /tmp/success
'' ''

@ -1,38 +1,37 @@
{ {
name = "vault-agent"; name = "vault-agent";
nodes.server = { nodes.server =
config, { config
pkgs, , ...
... }: {
}: { imports = [
imports = [ ./dev-vault-server.nix
./dev-vault-server.nix ../modules/vault-agent.nix
../modules/vault-agent.nix ];
];
services.vault.agents.test.settings = { services.vault.agents.test.settings = {
vault = { vault = {
address = "http://localhost:8200"; address = "http://localhost:8200";
}; };
template = { template = {
contents = ''{{ with secret "secret/my-secret" }}{{ .Data.data.foo }}{{ end }}''; contents = ''{{ with secret "secret/my-secret" }}{{ .Data.data.foo }}{{ end }}'';
destination = "/run/render.txt"; destination = "/run/render.txt";
}; };
auto_auth = { auto_auth = {
method = [ method = [
{ {
type = "approle"; type = "approle";
config = { config = {
role_id_file_path = "/tmp/roleID"; role_id_file_path = "/tmp/roleID";
secret_id_file_path = "/tmp/secretID"; secret_id_file_path = "/tmp/secretID";
remove_secret_id_file_after_reading = false; remove_secret_id_file_after_reading = false;
}; };
} }
]; ];
};
}; };
}; };
};
testScript = '' testScript = ''
start_all() start_all()
machine.wait_for_unit("multi-user.target") machine.wait_for_unit("multi-user.target")

@ -1,20 +1,19 @@
{ pkgs
, ...
}:
let
systemd-vaultd = pkgs.callPackage ../../default.nix { };
in
{ {
config,
lib,
pkgs,
...
}: let
systemd-vaultd = pkgs.callPackage ../../default.nix {};
in {
imports = [ imports = [
./vault-secrets.nix ./vault-secrets.nix
]; ];
systemd.package = pkgs.callPackage ../pkgs/systemd.nix {}; systemd.package = pkgs.callPackage ../pkgs/systemd.nix { };
systemd.sockets.systemd-vaultd = { systemd.sockets.systemd-vaultd = {
description = "systemd-vaultd socket"; description = "systemd-vaultd socket";
wantedBy = ["sockets.target"]; wantedBy = [ "sockets.target" ];
socketConfig = { socketConfig = {
ListenStream = "/run/systemd-vaultd/sock"; ListenStream = "/run/systemd-vaultd/sock";
@ -24,8 +23,8 @@ in {
}; };
systemd.services.systemd-vaultd = { systemd.services.systemd-vaultd = {
description = "systemd-vaultd daemon"; description = "systemd-vaultd daemon";
requires = ["systemd-vaultd.socket"]; requires = [ "systemd-vaultd.socket" ];
after = ["systemd-vaultd.socket"]; after = [ "systemd-vaultd.socket" ];
# Restarting can break services waiting for secrets # Restarting can break services waiting for secrets
stopIfChanged = false; stopIfChanged = false;
serviceConfig = { serviceConfig = {

@ -1,11 +1,11 @@
{ { config
config, , lib
lib, , pkgs
pkgs, , ...
... }:
}: let let
cfg = config.services.vault; cfg = config.services.vault;
settingsFormat = pkgs.formats.json {}; settingsFormat = pkgs.formats.json { };
autoAuthMethodModule = lib.types.submodule { autoAuthMethodModule = lib.types.submodule {
freeformType = lib.types.attrsOf lib.types.unspecified; freeformType = lib.types.attrsOf lib.types.unspecified;
@ -27,7 +27,7 @@
options = { options = {
method = lib.mkOption { method = lib.mkOption {
type = lib.types.listOf autoAuthMethodModule; type = lib.types.listOf autoAuthMethodModule;
default = []; default = [ ];
}; };
}; };
}; };
@ -49,18 +49,19 @@
options = { options = {
auto_auth = lib.mkOption { auto_auth = lib.mkOption {
type = autoAuthModule; type = autoAuthModule;
default = {}; default = { };
}; };
template_config = lib.mkOption { template_config = lib.mkOption {
type = templateConfigModule; type = templateConfigModule;
default = {}; default = { };
}; };
}; };
}; };
in { in
{
options.services.vault.agents = lib.mkOption { options.services.vault.agents = lib.mkOption {
default = {}; default = { };
description = "Instances of vault agent"; description = "Instances of vault agent";
type = lib.types.attrsOf (lib.types.submodule { type = lib.types.attrsOf (lib.types.submodule {
options = { options = {
@ -72,22 +73,23 @@ in {
}); });
}; };
config = { config = {
systemd.services = lib.mapAttrs' (name: instanceCfg: systemd.services = lib.mapAttrs'
lib.nameValuePair "vault-agent-${name}" { (name: instanceCfg:
after = ["network.target"]; lib.nameValuePair "vault-agent-${name}" {
wantedBy = ["multi-user.target"]; after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
# Services that also have `stopIfChanged = false` might wait for secrets # Services that also have `stopIfChanged = false` might wait for secrets
# while `vault-agent` is still stopped. This for example happens with nginx.service. # while `vault-agent` is still stopped. This for example happens with nginx.service.
stopIfChanged = false; stopIfChanged = false;
# Needs getent in PATH # Needs getent in PATH
path = [pkgs.glibc]; path = [ pkgs.glibc ];
serviceConfig = { serviceConfig = {
Restart = "on-failure"; Restart = "on-failure";
ExecStart = "${pkgs.vault}/bin/vault agent -config=${settingsFormat.generate "agent.json" instanceCfg.settings}"; ExecStart = "${pkgs.vault}/bin/vault agent -config=${settingsFormat.generate "agent.json" instanceCfg.settings}";
}; };
}) })
cfg.agents; cfg.agents;
}; };
} }

@ -1,11 +1,11 @@
{ { lib
lib, , config
config, , pkgs
pkgs, , ...
... }:
}: let let
secretType = serviceName: secretType = serviceName:
lib.types.submodule ({config, ...}: { lib.types.submodule ({ config, ... }: {
options = { options = {
name = lib.mkOption { name = lib.mkOption {
type = lib.types.str; type = lib.types.str;
@ -59,80 +59,85 @@
vaultTemplates = config: vaultTemplates = config:
(lib.mapAttrsToList (lib.mapAttrsToList
(serviceName: service: (serviceName: _service:
getSecretTemplate serviceName services.${serviceName}.vault) getSecretTemplate serviceName services.${serviceName}.vault)
(lib.filterAttrs (n: v: v.vault.secrets != {} && v.vault.agent == config._module.args.name) services)) (lib.filterAttrs (_n: v: v.vault.secrets != { } && v.vault.agent == config._module.args.name) services))
++ (lib.mapAttrsToList ++ (lib.mapAttrsToList
(serviceName: service: (serviceName: _service:
getEnvironmentTemplate serviceName services.${serviceName}.vault) getEnvironmentTemplate serviceName services.${serviceName}.vault)
(lib.filterAttrs (n: v: v.vault.environmentTemplate != null && v.vault.agent == config._module.args.name) services)); (lib.filterAttrs (_n: v: v.vault.environmentTemplate != null && v.vault.agent == config._module.args.name) services));
in { in
{
options = { options = {
systemd.services = lib.mkOption { systemd.services = lib.mkOption {
type = lib.types.attrsOf (lib.types.submodule ({config, ...}: let type = lib.types.attrsOf (lib.types.submodule ({ config, ... }:
serviceName = config._module.args.name; let
in { serviceName = config._module.args.name;
options.vault = { in
changeAction = lib.mkOption { {
description = '' options.vault = {
What to do with the service if any secrets change changeAction = lib.mkOption {
''; description = ''
type = lib.types.nullOr (lib.types.enum [ What to do with the service if any secrets change
"none" '';
"reload-or-restart" type = lib.types.nullOr (lib.types.enum [
"restart" "none"
]); "reload-or-restart"
default = "reload-or-restart"; "restart"
}; ]);
default = "reload-or-restart";
};
template = lib.mkOption { template = lib.mkOption {
type = lib.types.lines; type = lib.types.lines;
description = '' description = ''
The vault agent template to use for secrets The vault agent template to use for secrets
''; '';
}; };
environmentTemplate = lib.mkOption { environmentTemplate = lib.mkOption {
type = lib.types.nullOr lib.types.lines; type = lib.types.nullOr lib.types.lines;
default = null; default = null;
description = '' description = ''
The vault agent template to use for environment file The vault agent template to use for environment file
''; '';
}; };
agent = lib.mkOption { agent = lib.mkOption {
type = lib.types.str; type = lib.types.str;
default = "default"; default = "default";
description = '' description = ''
Agent instance to use for this service Agent instance to use for this service
''; '';
}; };
secrets = lib.mkOption { secrets = lib.mkOption {
type = lib.types.attrsOf (secretType serviceName); type = lib.types.attrsOf (secretType serviceName);
default = {}; default = { };
description = "List of secrets to load from vault agent template"; description = "List of secrets to load from vault agent template";
example = { example = {
some-secret.template = ''{{ with secret "secret/some-secret" }}{{ .Data.data.some-key }}{{ end }}''; some-secret.template = ''{{ with secret "secret/some-secret" }}{{ .Data.data.some-key }}{{ end }}'';
};
}; };
}; };
}; config =
config = let let
mkIfHasEnv = lib.mkIf (config.vault.environmentTemplate != null); mkIfHasEnv = lib.mkIf (config.vault.environmentTemplate != null);
in { in
after = mkIfHasEnv ["${serviceName}-envfile.service"]; {
bindsTo = mkIfHasEnv ["${serviceName}-envfile.service"]; after = mkIfHasEnv [ "${serviceName}-envfile.service" ];
bindsTo = mkIfHasEnv [ "${serviceName}-envfile.service" ];
serviceConfig = { serviceConfig = {
LoadCredential = lib.mapAttrsToList (_: config: "${config.name}:/run/systemd-vaultd/sock") config.vault.secrets; LoadCredential = lib.mapAttrsToList (_: config: "${config.name}:/run/systemd-vaultd/sock") config.vault.secrets;
EnvironmentFile = mkIfHasEnv ["/run/systemd-vaultd/secrets/${serviceName}.service.EnvironmentFile"]; EnvironmentFile = mkIfHasEnv [ "/run/systemd-vaultd/secrets/${serviceName}.service.EnvironmentFile" ];
}; };
}; };
})); }));
}; };
services.vault.agents = lib.mkOption { services.vault.agents = lib.mkOption {
type = lib.types.attrsOf (lib.types.submodule ({config, ...}: { type = lib.types.attrsOf (lib.types.submodule ({ config, ... }: {
config.settings.template = vaultTemplates config; config.settings.template = vaultTemplates config;
})); }));
}; };
@ -140,14 +145,17 @@ in {
config = { config = {
# we cannot use `systemd.services` here since this would create infinite recursion # we cannot use `systemd.services` here since this would create infinite recursion
systemd.packages = let systemd.packages =
servicesWithEnv = builtins.attrNames (lib.filterAttrs (n: v: v.vault.environmentTemplate != null) services); let
in [ servicesWithEnv = builtins.attrNames (lib.filterAttrs (_n: v: v.vault.environmentTemplate != null) services);
(pkgs.runCommand "env-services" {} in
('' [
(pkgs.runCommand "env-services" { }
(''
mkdir -p $out/lib/systemd/system mkdir -p $out/lib/systemd/system
'' ''
+ (lib.concatMapStringsSep "\n" (service: '' + (lib.concatMapStringsSep "\n"
(service: ''
cat > $out/lib/systemd/system/${service}-envfile.service <<EOF cat > $out/lib/systemd/system/${service}-envfile.service <<EOF
[Unit] [Unit]
Before=${service}.service Before=${service}.service
@ -165,6 +173,6 @@ in {
EOF EOF
'') '')
servicesWithEnv))) servicesWithEnv)))
]; ];
}; };
} }

@ -1,6 +1,6 @@
{ { systemd
systemd, , fetchpatch
fetchpatch, ,
}: }:
systemd.overrideAttrs (old: { systemd.overrideAttrs (old: {
patches = patches =

@ -0,0 +1,20 @@
[tool.ruff]
line-length = 88
select = ["E", "F", "I"]
ignore = [ "E501" ]
[tool.mypy]
python_version = "3.10"
warn_redundant_casts = true
disallow_untyped_calls = true
disallow_untyped_defs = true
no_implicit_optional = true
[[tool.mypy.overrides]]
module = "setuptools.*"
ignore_missing_imports = true
[[tool.mypy.overrides]]
module = "pytest.*"
ignore_missing_imports = true

@ -1,15 +1,15 @@
{pkgs ? import <nixpkgs> {}}: { pkgs ? import <nixpkgs> { } }:
with pkgs; with pkgs;
mkShellNoCC { mkShellNoCC {
buildInputs = [ buildInputs = [
python3.pkgs.pytest python3.pkgs.pytest
python3.pkgs.mypy python3.pkgs.mypy
golangci-lint golangci-lint
vault vault
systemd systemd
hivemind hivemind
go go
just just
]; ];
} }

@ -3,8 +3,8 @@
import os import os
import signal import signal
import subprocess import subprocess
from typing import IO, Any, Dict, Iterator, List, Union
from pathlib import Path from pathlib import Path
from typing import IO, Any, Dict, Iterator, List, Union
import pytest import pytest

@ -1,8 +1,8 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import json
import random import random
import string import string
import json
from dataclasses import dataclass from dataclasses import dataclass
from pathlib import Path from pathlib import Path

@ -1,9 +1,10 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import os import os
import pytest
from pathlib import Path from pathlib import Path
from typing import Optional from typing import Optional
import pytest
from command import run from command import run
BIN: Optional[Path] = None BIN: Optional[Path] = None

@ -1,10 +1,11 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import pytest
from tempfile import TemporaryDirectory
from pathlib import Path from pathlib import Path
from tempfile import TemporaryDirectory
from typing import Iterator from typing import Iterator
import pytest
@pytest.fixture @pytest.fixture
def tempdir() -> Iterator[Path]: def tempdir() -> Iterator[Path]:

@ -1,6 +1,6 @@
import subprocess import subprocess
from pathlib import Path
import time import time
from pathlib import Path
from command import Command from command import Command
from random_service import random_service from random_service import random_service

@ -1,7 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import time
import subprocess import subprocess
import time
from pathlib import Path from pathlib import Path
from command import Command, run from command import Command, run

Loading…
Cancel
Save