-
Notifications
You must be signed in to change notification settings - Fork 119
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add secrets change detection #132
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -15,14 +15,21 @@ let | |||||||||||||||
users = config.users.users; | ||||||||||||||||
|
||||||||||||||||
newGeneration = '' | ||||||||||||||||
_agenix_generation="$(basename "$(readlink ${cfg.secretsDir})" || echo 0)" | ||||||||||||||||
(( ++_agenix_generation )) | ||||||||||||||||
echo "[agenix] creating new generation in ${cfg.secretsMountPoint}/$_agenix_generation" | ||||||||||||||||
mkdir -p "${cfg.secretsMountPoint}" | ||||||||||||||||
chmod 0751 "${cfg.secretsMountPoint}" | ||||||||||||||||
grep -q "${cfg.secretsMountPoint} ramfs" /proc/mounts || mount -t ramfs none "${cfg.secretsMountPoint}" -o nodev,nosuid,mode=0751 | ||||||||||||||||
mkdir -p "${cfg.secretsMountPoint}/$_agenix_generation" | ||||||||||||||||
chmod 0751 "${cfg.secretsMountPoint}/$_agenix_generation" | ||||||||||||||||
_agenix_last_generation=$(basename "$(readlink ${cfg.secretsDir})" || true) | ||||||||||||||||
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" | ||||||||||||||||
mkdir -p "${cfg.secretsMountPoint}" | ||||||||||||||||
chmod 0751 "${cfg.secretsMountPoint}" | ||||||||||||||||
grep -q "${cfg.secretsMountPoint} ramfs" /proc/mounts || mount -t ramfs none "${cfg.secretsMountPoint}" -o nodev,nosuid,mode=0751 | ||||||||||||||||
mkdir -p "${cfg.secretsMountPoint}/$_agenix_generation" | ||||||||||||||||
chmod 0751 "${cfg.secretsMountPoint}/$_agenix_generation" | ||||||||||||||||
fi | ||||||||||||||||
''; | ||||||||||||||||
|
||||||||||||||||
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!' | ||||||||||||||||
'') 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 = '' | ||||||||||||||||
_agenix_generation="$(basename "$(readlink ${cfg.secretsDir})" || echo 0)" | ||||||||||||||||
(( ++_agenix_generation )) | ||||||||||||||||
echo "[agenix] symlinking new secrets to ${cfg.secretsDir} (generation $_agenix_generation)..." | ||||||||||||||||
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 ))" | ||||||||||||||||
[[ $_agenix_last_generation ]] && { | ||||||||||||||||
echo "[agenix] removing old secrets (generation $_agenix_last_generation)..." | ||||||||||||||||
rm -rf "${cfg.secretsMountPoint}/$_agenix_last_generation" | ||||||||||||||||
} | ||||||||||||||||
''; | ||||||||||||||||
|
||||||||||||||||
installSecrets = builtins.concatStringsSep "\n" ( | ||||||||||||||||
installSecrets = mkInstallScript ( | ||||||||||||||||
[ "echo '[agenix] decrypting secrets...'" ] | ||||||||||||||||
++ testIdentities | ||||||||||||||||
++ (map installSecret (builtins.attrValues cfg.secrets)) | ||||||||||||||||
++ [ cleanupAndLink ] | ||||||||||||||||
++ [ renameOnFailure cleanupAndLink ] | ||||||||||||||||
); | ||||||||||||||||
|
||||||||||||||||
chownSecret = secretType: '' | ||||||||||||||||
|
@@ -89,11 +103,23 @@ let | |||||||||||||||
chown :keys "${cfg.secretsMountPoint}" "${cfg.secretsMountPoint}/$_agenix_generation" | ||||||||||||||||
''; | ||||||||||||||||
|
||||||||||||||||
chownSecrets = builtins.concatStringsSep "\n" ( | ||||||||||||||||
chownSecrets = mkInstallScript ( | ||||||||||||||||
[ "echo '[agenix] chowning...'" ] | ||||||||||||||||
++ [ chownMountPoint ] | ||||||||||||||||
++ (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, ... }: { | ||||||||||||||||
options = { | ||||||||||||||||
name = mkOption { | ||||||||||||||||
|
@@ -104,7 +130,13 @@ let | |||||||||||||||
''; | ||||||||||||||||
}; | ||||||||||||||||
file = mkOption { | ||||||||||||||||
type = types.path; | ||||||||||||||||
type = mkOptionType { | ||||||||||||||||
name = "nix-path"; | ||||||||||||||||
descriptionClass = "noun"; | ||||||||||||||||
check = builtins.isPath; | ||||||||||||||||
merge = mergeEqualOption; | ||||||||||||||||
}; | ||||||||||||||||
|
||||||||||||||||
Comment on lines
+133
to
+139
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Does this work? See https://github.com/NixOS/nixpkgs/blob/master/lib/types.nix#L464-L473 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, because at the moment the value is typechecked, it's not yet a store path but a mere path (like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see, yes. |
||||||||||||||||
description = '' | ||||||||||||||||
Age file the secret is loaded from. | ||||||||||||||||
''; | ||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is this about? Maybe worth a comment in the code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Excellent, makes sense. I still don't understand where
_localstatus
comes from or under what conditions it's valid, but maybe my google-fu isn't good enough to find the bash documentation for it.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_localstatus
is defined here.