mirror of
https://github.com/ryantm/agenix.git
synced 2024-11-25 02:58:30 +03:00
modules/age: add secrets change detection
This commit is contained in:
parent
657442730c
commit
69e4bfbe75
1 changed files with 43 additions and 11 deletions
|
@ -15,14 +15,21 @@ let
|
||||||
users = config.users.users;
|
users = config.users.users;
|
||||||
|
|
||||||
newGeneration = ''
|
newGeneration = ''
|
||||||
_agenix_generation="$(basename "$(readlink ${cfg.secretsDir})" || echo 0)"
|
_agenix_last_generation=$(basename "$(readlink ${cfg.secretsDir})" || true)
|
||||||
(( ++_agenix_generation ))
|
if [[ $_agenix_last_generation == ${secretsHash} ]]; then
|
||||||
|
_agenix_is_current=1
|
||||||
|
else
|
||||||
|
_agenix_is_current=
|
||||||
|
fi
|
||||||
|
if [[ ! $_agenix_is_current ]]; then
|
||||||
|
_agenix_generation="${secretsHash}"
|
||||||
echo "[agenix] creating new generation in ${cfg.secretsMountPoint}/$_agenix_generation"
|
echo "[agenix] creating new generation in ${cfg.secretsMountPoint}/$_agenix_generation"
|
||||||
mkdir -p "${cfg.secretsMountPoint}"
|
mkdir -p "${cfg.secretsMountPoint}"
|
||||||
chmod 0751 "${cfg.secretsMountPoint}"
|
chmod 0751 "${cfg.secretsMountPoint}"
|
||||||
grep -q "${cfg.secretsMountPoint} ramfs" /proc/mounts || mount -t ramfs none "${cfg.secretsMountPoint}" -o nodev,nosuid,mode=0751
|
grep -q "${cfg.secretsMountPoint} ramfs" /proc/mounts || mount -t ramfs none "${cfg.secretsMountPoint}" -o nodev,nosuid,mode=0751
|
||||||
mkdir -p "${cfg.secretsMountPoint}/$_agenix_generation"
|
mkdir -p "${cfg.secretsMountPoint}/$_agenix_generation"
|
||||||
chmod 0751 "${cfg.secretsMountPoint}/$_agenix_generation"
|
chmod 0751 "${cfg.secretsMountPoint}/$_agenix_generation"
|
||||||
|
fi
|
||||||
'';
|
'';
|
||||||
|
|
||||||
identities = builtins.concatStringsSep " " (map (path: "-i ${path}") cfg.identityPaths);
|
identities = builtins.concatStringsSep " " (map (path: "-i ${path}") cfg.identityPaths);
|
||||||
|
@ -59,23 +66,30 @@ let
|
||||||
test -f ${path} || echo '[agenix] WARNING: config.age.identityPaths entry ${path} not present!'
|
test -f ${path} || echo '[agenix] WARNING: config.age.identityPaths entry ${path} not present!'
|
||||||
'') cfg.identityPaths;
|
'') cfg.identityPaths;
|
||||||
|
|
||||||
|
# Add suffix `-incomplete` to the generation when creating some secrets has failed.
|
||||||
|
# This ensures that we can try to re-create the generation on subsequent runs.
|
||||||
|
renameOnFailure = ''
|
||||||
|
if (( _localstatus > 0 )); then
|
||||||
|
mv "${cfg.secretsMountPoint}/$_agenix_generation"{,-incomplete}
|
||||||
|
_agenix_generation+=-incomplete
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
|
||||||
cleanupAndLink = ''
|
cleanupAndLink = ''
|
||||||
_agenix_generation="$(basename "$(readlink ${cfg.secretsDir})" || echo 0)"
|
|
||||||
(( ++_agenix_generation ))
|
|
||||||
echo "[agenix] symlinking new secrets to ${cfg.secretsDir} (generation $_agenix_generation)..."
|
echo "[agenix] symlinking new secrets to ${cfg.secretsDir} (generation $_agenix_generation)..."
|
||||||
ln -sfn "${cfg.secretsMountPoint}/$_agenix_generation" ${cfg.secretsDir}
|
ln -sfn "${cfg.secretsMountPoint}/$_agenix_generation" ${cfg.secretsDir}
|
||||||
|
|
||||||
(( _agenix_generation > 1 )) && {
|
[[ $_agenix_last_generation ]] && {
|
||||||
echo "[agenix] removing old secrets (generation $(( _agenix_generation - 1 )))..."
|
echo "[agenix] removing old secrets (generation $_agenix_last_generation)..."
|
||||||
rm -rf "${cfg.secretsMountPoint}/$(( _agenix_generation - 1 ))"
|
rm -rf "${cfg.secretsMountPoint}/$_agenix_last_generation"
|
||||||
}
|
}
|
||||||
'';
|
'';
|
||||||
|
|
||||||
installSecrets = builtins.concatStringsSep "\n" (
|
installSecrets = mkInstallScript (
|
||||||
[ "echo '[agenix] decrypting secrets...'" ]
|
[ "echo '[agenix] decrypting secrets...'" ]
|
||||||
++ testIdentities
|
++ testIdentities
|
||||||
++ (map installSecret (builtins.attrValues cfg.secrets))
|
++ (map installSecret (builtins.attrValues cfg.secrets))
|
||||||
++ [ cleanupAndLink ]
|
++ [ renameOnFailure cleanupAndLink ]
|
||||||
);
|
);
|
||||||
|
|
||||||
chownSecret = secretType: ''
|
chownSecret = secretType: ''
|
||||||
|
@ -89,11 +103,23 @@ let
|
||||||
chown :keys "${cfg.secretsMountPoint}" "${cfg.secretsMountPoint}/$_agenix_generation"
|
chown :keys "${cfg.secretsMountPoint}" "${cfg.secretsMountPoint}/$_agenix_generation"
|
||||||
'';
|
'';
|
||||||
|
|
||||||
chownSecrets = builtins.concatStringsSep "\n" (
|
chownSecrets = mkInstallScript (
|
||||||
[ "echo '[agenix] chowning...'" ]
|
[ "echo '[agenix] chowning...'" ]
|
||||||
++ [ chownMountPoint ]
|
++ [ chownMountPoint ]
|
||||||
++ (map chownSecret (builtins.attrValues cfg.secrets)));
|
++ (map chownSecret (builtins.attrValues cfg.secrets)));
|
||||||
|
|
||||||
|
mkInstallScript = strings: ''
|
||||||
|
[[ $_agenix_is_current ]] || {
|
||||||
|
${builtins.concatStringsSep "\n" strings}
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
|
||||||
|
secretsHash = let
|
||||||
|
sha256-base16 = builtins.hashString "sha256" (builtins.toJSON cfg.secrets);
|
||||||
|
in
|
||||||
|
# Truncate to 128 bits to increase readability
|
||||||
|
substring 0 32 sha256-base16;
|
||||||
|
|
||||||
secretType = types.submodule ({ config, ... }: {
|
secretType = types.submodule ({ config, ... }: {
|
||||||
options = {
|
options = {
|
||||||
name = mkOption {
|
name = mkOption {
|
||||||
|
@ -104,7 +130,13 @@ let
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
file = mkOption {
|
file = mkOption {
|
||||||
type = types.path;
|
type = mkOptionType {
|
||||||
|
name = "nix-path";
|
||||||
|
descriptionClass = "noun";
|
||||||
|
check = builtins.isPath;
|
||||||
|
merge = mergeEqualOption;
|
||||||
|
};
|
||||||
|
|
||||||
description = ''
|
description = ''
|
||||||
Age file the secret is loaded from.
|
Age file the secret is loaded from.
|
||||||
'';
|
'';
|
||||||
|
|
Loading…
Reference in a new issue