This commit is contained in:
RingOfStorms (Joshua Bell) 2026-01-06 20:39:27 -06:00
commit dea716f862
2 changed files with 97 additions and 47 deletions

View file

@ -428,10 +428,16 @@ in
description = "Field under .Data.data to render."; description = "Field under .Data.data to render.";
}; };
dependencies = lib.mkOption { softDepend = lib.mkOption {
type = lib.types.listOf lib.types.str; type = lib.types.listOf lib.types.str;
default = [ ]; default = [ ];
description = "Systemd service names to restart after this secret is rendered."; description = "Systemd service names to try-restart when this secret changes (does not block startup).";
};
hardDepend = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
description = "Systemd service names that should only start when this secret exists; started when the secret changes.";
}; };
configChanges = lib.mkOption { configChanges = lib.mkOption {
@ -476,17 +482,62 @@ in
sec sec
]; ];
systemd.tmpfiles.rules = [ systemd.tmpfiles.rules = [
"d /run/openbao 0700 root root - -" "d /run/openbao 0700 root root - -"
"f /run/openbao/zitadel.jwt 0400 root root - -" "f /run/openbao/zitadel.jwt 0400 root root - -"
"d /run/secrets 0711 root root - -" "d /run/secrets 0711 root root - -"
]; ];
systemd.paths = lib.mapAttrs' (
name: secret:
lib.nameValuePair "openbao-secret-${name}" {
description = "Path unit for OpenBao secret ${name}";
wantedBy = [ "multi-user.target" ];
pathConfig = {
PathChanged = secret.path;
PathExists = secret.path;
Unit = "openbao-secret-changed-${name}.service";
};
}
) cfg.secrets;
systemd.services = lib.mkMerge [
(
lib.mkMerge (
lib.concatLists (
lib.mapAttrsToList (
secretName: secret:
map (
svc:
{
systemd.services.${svc} = {
unitConfig.ConditionPathExists = secret.path;
wants = lib.mkAfter [ "openbao-secret-${secretName}.path" ];
after = lib.mkAfter [ "openbao-secret-${secretName}.path" ];
};
}
) secret.hardDepend
) cfg.secrets
)
)
)
{
systemd.timers.zitadel-mint-jwt = {
description = "Refresh Zitadel JWT for OpenBao";
wantedBy = [ "timers.target" ];
timerConfig = {
OnBootSec = "30s";
OnUnitActiveSec = "2m";
Unit = "zitadel-mint-jwt.service";
};
};
zitadel-mint-jwt = {
description = "Mint Zitadel access token (JWT) for OpenBao";
systemd.services = lib.mkMerge [
{
zitadel-mint-jwt = {
description = "Mint Zitadel access token (JWT) for OpenBao";
wantedBy = [ "multi-user.target" ];
after = [ after = [
"network-online.target" "network-online.target"
"nss-lookup.target" "nss-lookup.target"
@ -590,17 +641,16 @@ in
}; };
}; };
vault-agent = { vault-agent = {
description = "OpenBao agent for rendering secrets"; description = "OpenBao agent for rendering secrets";
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
after = [ after = [
"network-online.target" "network-online.target"
"zitadel-mint-jwt.service" ];
]; wants = [
wants = [ "network-online.target"
"network-online.target" "zitadel-mint-jwt.service"
"zitadel-mint-jwt.service" ];
];
serviceConfig = { serviceConfig = {
Type = "simple"; Type = "simple";
@ -617,47 +667,47 @@ in
(lib.mapAttrs' ( (lib.mapAttrs' (
name: secret: name: secret:
lib.nameValuePair "openbao-secret-${name}" { lib.nameValuePair "openbao-secret-changed-${name}" {
description = "Wait for OpenBao secret ${name}"; description = "React to OpenBao secret ${name} changes";
wants = [ "vault-agent.service" ];
after = [ "vault-agent.service" ]; after = [ "vault-agent.service" ];
requires = [ "vault-agent.service" ];
wantedBy = map (svc: "${svc}.service") secret.dependencies;
startLimitIntervalSec = 300; startLimitIntervalSec = 300;
startLimitBurst = 3; startLimitBurst = 6;
serviceConfig = { serviceConfig = {
Type = "oneshot"; Type = "oneshot";
User = "root"; User = "root";
Group = "root"; Group = "root";
UMask = "0077"; UMask = "0077";
ExecStart = pkgs.writeShellScript "openbao-wait-secret-${name}" '' ExecStart = pkgs.writeShellScript "openbao-secret-changed-${name}" ''
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail set -euo pipefail
p=${lib.escapeShellArg secret.path} p=${lib.escapeShellArg secret.path}
for i in {1..60}; do
if [ -s "$p" ]; then
break
fi
sleep 1
done
if [ ! -s "$p" ]; then if [ ! -s "$p" ]; then
echo "Secret file not rendered: $p" >&2 echo "Secret not present (skipping): $p" >&2
exit 1 exit 0
fi fi
${lib.concatStringsSep "\n" ( ${lib.concatStringsSep "\n" (
map (svc: '' map (svc: ''
echo "Restarting ${svc} due to secret ${name}" >&2 echo "Trying restart of ${svc} due to secret ${name}" >&2
systemctl try-restart ${lib.escapeShellArg (svc + ".service")} || true systemctl try-restart ${lib.escapeShellArg (svc + ".service")} || true
'') secret.dependencies '') secret.softDepend
)}
${lib.concatStringsSep "\n" (
map (svc: ''
echo "Starting ${svc} due to secret ${name}" >&2
systemctl start ${lib.escapeShellArg (svc + ".service")} || true
'') secret.hardDepend
)} )}
''; '';
}; };
} }
) cfg.secrets) ) cfg.secrets)
]; ];
} }
] ]

View file

@ -121,13 +121,14 @@
secrets = { secrets = {
headscale_auth = { headscale_auth = {
kvPath = "kv/data/machines/home_roaming/headscale_auth"; kvPath = "kv/data/machines/home_roaming/headscale_auth";
dependencies = [ "tailscaled" ]; softDepend = [ "tailscaled" ];
configChanges.services.tailscale.authKeyFile = "$SECRET_PATH"; configChanges.services.tailscale.authKeyFile = "$SECRET_PATH";
}; };
"atuin-key-josh" = { "atuin-key-josh" = {
owner = "josh"; owner = "josh";
group = "users"; group = "users";
mode = "0400"; mode = "0400";
hardDepend = [ "atuin-autologin" ];
template = ''{{- with secret "kv/data/machines/home_roaming/atuin-key-josh" -}}{{ printf "%s\n%s\n%s" .Data.data.user .Data.data.password .Data.data.value }}{{- end -}}''; template = ''{{- with secret "kv/data/machines/home_roaming/atuin-key-josh" -}}{{ printf "%s\n%s\n%s" .Data.data.user .Data.data.password .Data.data.value }}{{- end -}}'';
}; };
nix2github = { nix2github = {
@ -305,9 +306,8 @@
systemd.services.atuin-autologin = { systemd.services.atuin-autologin = {
description = "Auto-login to Atuin (if logged out)"; description = "Auto-login to Atuin (if logged out)";
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
after = [ "network-online.target" "openbao-secret-atuin-key-josh.service" ]; after = [ "network-online.target" ];
wants = [ "network-online.target" "openbao-secret-atuin-key-josh.service" ]; wants = [ "network-online.target" ];
requires = [ "openbao-secret-atuin-key-josh.service" ];
serviceConfig = { serviceConfig = {
Type = "oneshot"; Type = "oneshot";
@ -337,9 +337,9 @@
exit 1 exit 1
fi fi
username="$(${pkgs.coreutils}/bin/sed -n '1p' "$secret")" username="$(${pkgs.gnused}/bin/sed -n '1p' "$secret")"
password="$(${pkgs.coreutils}/bin/sed -n '2p' "$secret")" password="$(${pkgs.gnused}/bin/sed -n '2p' "$secret")"
key="$(${pkgs.coreutils}/bin/sed -n '3p' "$secret")" key="$(${pkgs.gnused}/bin/sed -n '3p' "$secret")"
exec ${pkgs.atuin}/bin/atuin login --username "$username" --password "$password" --key "$key" exec ${pkgs.atuin}/bin/atuin login --username "$username" --password "$password" --key "$key"
''; '';