overhaul on change actions

compare the decrypted secrets, and make use of /run/nixos/activation-{restart,reload}-list
This commit is contained in:
Taeer Bar-Yam 2022-06-01 07:01:51 -04:00
parent 5a98964a52
commit 34f637d93e
3 changed files with 45 additions and 49 deletions

View file

@ -2,11 +2,11 @@
"nodes": { "nodes": {
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1638587357, "lastModified": 1653996475,
"narHash": "sha256-2ySMW3QARG8BsRPmwe7clTbdCuaObromOKewykP+UJc=", "narHash": "sha256-r/UA7h3Dfgf4dlOCkakpqejf1Tagfb+6T+9OdT0qBgU=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "e34c5379866833f41e2a36f309912fa675d687c7", "rev": "ec6eaba9dfcfdd11547d75a193e91e26701bf7e3",
"type": "github" "type": "github"
}, },
"original": { "original": {

View file

@ -1,4 +1,4 @@
{ config, options, lib, pkgs, ... }: { config, options, lib, utils, pkgs, ... }:
with lib; with lib;
@ -18,8 +18,10 @@ let
installSecret = secretType: '' installSecret = secretType: ''
${if secretType.symlink then '' ${if secretType.symlink then ''
_truePath="${cfg.secretsMountPoint}/$_agenix_generation/${secretType.name}" _truePath="${cfg.secretsMountPoint}/$_agenix_generation/${secretType.name}"
_oldPath="${cfg.secretsMountPoint}/$(( _agenix_generation - 1 ))/${secretType.name}"
'' else '' '' else ''
_truePath="${secretType.path}" _truePath="${secretType.path}"
_oldPath=_truePath
''} ''}
echo "decrypting '${secretType.file}' to '$_truePath'..." echo "decrypting '${secretType.file}' to '$_truePath'..."
TMP_FILE="$_truePath.tmp" TMP_FILE="$_truePath.tmp"
@ -33,8 +35,18 @@ let
) )
chmod ${secretType.mode} "$TMP_FILE" chmod ${secretType.mode} "$TMP_FILE"
chown ${secretType.owner}:${secretType.group} "$TMP_FILE" chown ${secretType.owner}:${secretType.group} "$TMP_FILE"
# see if there's been any change from the last generation
changes=$(${pkgs.rsync}/bin/rsync --dry-run -aHAX -i "$TMP_FILE" "$_oldPath")
mv -f "$TMP_FILE" "$_truePath" mv -f "$TMP_FILE" "$_truePath"
[ "$changes" != "" ] && {
echo '${lib.concatStringsSep "\n" secretType.reloadUnits}' >> /run/nixos/activation-reload-list
echo '${lib.concatStringsSep "\n" secretType.restartUnits}' >> /run/nixos/activation-restart-list
${secretType.onChange}
}
${optionalString secretType.symlink '' ${optionalString secretType.symlink ''
[ "${secretType.path}" != "${cfg.secretsDir}/${secretType.name}" ] && ln -sfn "${cfg.secretsDir}/${secretType.name}" "${secretType.path}" [ "${secretType.path}" != "${cfg.secretsDir}/${secretType.name}" ] && ln -sfn "${cfg.secretsDir}/${secretType.name}" "${secretType.path}"
''} ''}
@ -96,16 +108,22 @@ let
Group of the decrypted secret. Group of the decrypted secret.
''; '';
}; };
action = mkOption { onChange = mkOption {
type = types.str; type = types.str;
default = ""; default = "";
description = "A script to run when secret is updated."; description = "A script to run when secret is updated.";
}; };
services = mkOption { reloadUnits = mkOption {
type = types.listOf types.str; type = types.listOf utils.systemdUtils.lib.unitNameType;
default = []; default = [];
description = "The systemd services that uses this secret. Will be restarted when the secret changes."; description = "The systemd services to reload when the secret changes.";
example = "[ wireguard-wg0 ]"; example = literalExpression ''[ "wireguard-wg0.service" ]'';
};
restartUnits = mkOption {
type = types.listOf utils.systemdUtils.lib.unitNameType;
default = [];
description = "The systemd services to restart when the secret changes.";
example = literalExpression ''[ "wireguard-wg0.service" ]'';
}; };
symlink = mkEnableOption "symlinking secrets to their destination" // { default = true; }; symlink = mkEnableOption "symlinking secrets to their destination" // { default = true; };
}; };
@ -183,11 +201,6 @@ in
mkdir -p "${cfg.secretsMountPoint}/$_agenix_generation" mkdir -p "${cfg.secretsMountPoint}/$_agenix_generation"
chmod 0751 "${cfg.secretsMountPoint}/$_agenix_generation" chmod 0751 "${cfg.secretsMountPoint}/$_agenix_generation"
ln -sfn "${cfg.secretsMountPoint}/$_agenix_generation" ${cfg.secretsDir} ln -sfn "${cfg.secretsMountPoint}/$_agenix_generation" ${cfg.secretsDir}
(( _agenix_generation > 1 )) && {
echo "[agenix] removing old secrets (generation $(( _agenix_generation - 1 )))..."
rm -rf "${cfg.secretsMountPoint}/$(( _agenix_generation - 1 ))"
}
''; '';
deps = [ deps = [
"specialfs" "specialfs"
@ -216,7 +229,7 @@ in
}; };
# Other secrets need to wait for users and groups to exist. # Other secrets need to wait for users and groups to exist.
system.activationScripts.agenix = { system.activationScripts.agenixNonRoot = {
text = installNonRootSecrets; text = installNonRootSecrets;
deps = [ deps = [
"users" "users"
@ -227,38 +240,21 @@ in
]; ];
}; };
systemd.services = lib.mkMerge # named "agenix" for others to depend on the last activationScript (and thereby all the rest)
(lib.mapAttrsToList system.activationScripts.agenix = {
(name: {action, services, file, path, mode, owner, group, ...}: # cleanup old generation
let text = ''
fileHash = builtins.hashFile "sha256" file; (( _agenix_generation > 1 )) && {
restartTriggers = [ fileHash path mode owner group ]; echo "[agenix] removing old secrets (generation $(( _agenix_generation - 1 )))..."
in rm -rf "${cfg.secretsMountPoint}/$(( _agenix_generation - 1 ))"
lib.mkMerge [ }
(lib.genAttrs services (_: { inherit restartTriggers; })) '';
(lib.mkIf (action != "") { deps = [
"agenix-${name}-action" = { "specialfs"
inherit restartTriggers; "agenixMountSecrets"
"agenixRoot"
# We execute the action on reload so that it doesn't happen at "agenixNonRoot"
# startup. The only disadvantage is that it won't trigger the ];
# first time the service is created.
reload = action;
reloadIfChanged = true;
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
}; };
script = " "; # systemd complains if we only set ExecReload
# Give it a reason for starting
wantedBy = [ "multi-user.target" ];
}; };
})
]) cfg.secrets);
};
} }

View file

@ -45,7 +45,7 @@ import "${nixpkgs}/nixos/tests/make-test-python.nix"
age.secrets.ex1 = { age.secrets.ex1 = {
file = ../example/passwordfile-user1.age; file = ../example/passwordfile-user1.age;
action = "echo bar > /tmp/foo"; onChange = "echo bar > /tmp/foo";
}; };
}; };