mirror of
https://github.com/ryantm/agenix.git
synced 2024-11-22 17:50:48 +03:00
Merge pull request #119 from ryantm/order
feature: combine root and nonroot secret install; delay chowning
This commit is contained in:
commit
9f136ecfa5
2 changed files with 72 additions and 53 deletions
121
modules/age.nix
121
modules/age.nix
|
@ -14,13 +14,29 @@ let
|
||||||
|
|
||||||
users = config.users.users;
|
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"
|
||||||
|
'';
|
||||||
|
|
||||||
identities = builtins.concatStringsSep " " (map (path: "-i ${path}") cfg.identityPaths);
|
identities = builtins.concatStringsSep " " (map (path: "-i ${path}") cfg.identityPaths);
|
||||||
installSecret = secretType: ''
|
|
||||||
|
setTruePath = secretType: ''
|
||||||
${if secretType.symlink then ''
|
${if secretType.symlink then ''
|
||||||
_truePath="${cfg.secretsMountPoint}/$_agenix_generation/${secretType.name}"
|
_truePath="${cfg.secretsMountPoint}/$_agenix_generation/${secretType.name}"
|
||||||
'' else ''
|
'' else ''
|
||||||
_truePath="${secretType.path}"
|
_truePath="${secretType.path}"
|
||||||
''}
|
''}
|
||||||
|
'';
|
||||||
|
|
||||||
|
installSecret = secretType: ''
|
||||||
|
${setTruePath secretType}
|
||||||
echo "decrypting '${secretType.file}' to '$_truePath'..."
|
echo "decrypting '${secretType.file}' to '$_truePath'..."
|
||||||
TMP_FILE="$_truePath.tmp"
|
TMP_FILE="$_truePath.tmp"
|
||||||
mkdir -p "$(dirname "$_truePath")"
|
mkdir -p "$(dirname "$_truePath")"
|
||||||
|
@ -32,7 +48,6 @@ let
|
||||||
LANG=${config.i18n.defaultLocale} ${ageBin} --decrypt ${identities} -o "$TMP_FILE" "${secretType.file}"
|
LANG=${config.i18n.defaultLocale} ${ageBin} --decrypt ${identities} -o "$TMP_FILE" "${secretType.file}"
|
||||||
)
|
)
|
||||||
chmod ${secretType.mode} "$TMP_FILE"
|
chmod ${secretType.mode} "$TMP_FILE"
|
||||||
chown ${secretType.owner}:${secretType.group} "$TMP_FILE"
|
|
||||||
mv -f "$TMP_FILE" "$_truePath"
|
mv -f "$TMP_FILE" "$_truePath"
|
||||||
|
|
||||||
${optionalString secretType.symlink ''
|
${optionalString secretType.symlink ''
|
||||||
|
@ -44,14 +59,40 @@ 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;
|
||||||
|
|
||||||
isRootSecret = st: (st.owner == "root" || st.owner == "0") && (st.group == "root" || st.group == "0");
|
cleanupAndLink = ''
|
||||||
isNotRootSecret = st: !(isRootSecret st);
|
_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}
|
||||||
|
|
||||||
rootOwnedSecrets = builtins.filter isRootSecret (builtins.attrValues cfg.secrets);
|
(( _agenix_generation > 1 )) && {
|
||||||
installRootOwnedSecrets = builtins.concatStringsSep "\n" ([ "echo '[agenix] decrypting root secrets...'" ] ++ testIdentities ++ (map installSecret rootOwnedSecrets));
|
echo "[agenix] removing old secrets (generation $(( _agenix_generation - 1 )))..."
|
||||||
|
rm -rf "${cfg.secretsMountPoint}/$(( _agenix_generation - 1 ))"
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
|
||||||
nonRootSecrets = builtins.filter isNotRootSecret (builtins.attrValues cfg.secrets);
|
installSecrets = builtins.concatStringsSep "\n" (
|
||||||
installNonRootSecrets = builtins.concatStringsSep "\n" ([ "echo '[agenix] decrypting non-root secrets...'" ] ++ (map installSecret nonRootSecrets));
|
[ "echo '[agenix] decrypting secrets...'" ]
|
||||||
|
++ testIdentities
|
||||||
|
++ (map installSecret (builtins.attrValues cfg.secrets))
|
||||||
|
++ [ cleanupAndLink ]
|
||||||
|
);
|
||||||
|
|
||||||
|
chownSecret = secretType: ''
|
||||||
|
${setTruePath secretType}
|
||||||
|
chown ${secretType.owner}:${secretType.group} "$_truePath"
|
||||||
|
'';
|
||||||
|
|
||||||
|
# chown the secrets mountpoint and the current generation to the keys group
|
||||||
|
# instead of leaving it root:root.
|
||||||
|
chownMountPoint = ''
|
||||||
|
chown :keys "${cfg.secretsMountPoint}" "${cfg.secretsMountPoint}/$_agenix_generation"
|
||||||
|
'';
|
||||||
|
|
||||||
|
chownSecrets = builtins.concatStringsSep "\n" (
|
||||||
|
[ "echo '[agenix] chowning...'" ]
|
||||||
|
++ [ chownMountPoint ]
|
||||||
|
++ (map chownSecret (builtins.attrValues cfg.secrets)));
|
||||||
|
|
||||||
secretType = types.submodule ({ config, ... }: {
|
secretType = types.submodule ({ config, ... }: {
|
||||||
options = {
|
options = {
|
||||||
|
@ -161,59 +202,37 @@ in
|
||||||
# Create a new directory full of secrets for symlinking (this helps
|
# Create a new directory full of secrets for symlinking (this helps
|
||||||
# ensure removed secrets are actually removed, or at least become
|
# ensure removed secrets are actually removed, or at least become
|
||||||
# invalid symlinks).
|
# invalid symlinks).
|
||||||
system.activationScripts.agenixMountSecrets = {
|
system.activationScripts.agenixNewGeneration = {
|
||||||
text = ''
|
text = newGeneration;
|
||||||
_agenix_generation="$(basename "$(readlink ${cfg.secretsDir})" || echo 0)"
|
|
||||||
(( ++_agenix_generation ))
|
|
||||||
echo "[agenix] symlinking new secrets to ${cfg.secretsDir} (generation $_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"
|
|
||||||
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"
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
# Secrets with root owner and group can be installed before users
|
system.activationScripts.agenixInstall = {
|
||||||
# exist. This allows user password files to be encrypted.
|
text = installSecrets;
|
||||||
system.activationScripts.agenixRoot = {
|
|
||||||
text = installRootOwnedSecrets;
|
|
||||||
deps = [ "agenixMountSecrets" "specialfs" ];
|
|
||||||
};
|
|
||||||
system.activationScripts.users.deps = [ "agenixRoot" ];
|
|
||||||
|
|
||||||
# chown the secrets mountpoint and the current generation to the keys group
|
|
||||||
# instead of leaving it root:root.
|
|
||||||
system.activationScripts.agenixChownKeys = {
|
|
||||||
text = ''
|
|
||||||
chown :keys "${cfg.secretsMountPoint}" "${cfg.secretsMountPoint}/$_agenix_generation"
|
|
||||||
'';
|
|
||||||
deps = [
|
deps = [
|
||||||
"users"
|
"agenixNewGeneration"
|
||||||
"groups"
|
"specialfs"
|
||||||
"agenixMountSecrets"
|
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
# Other secrets need to wait for users and groups to exist.
|
# So user passwords can be encrypted.
|
||||||
|
system.activationScripts.users.deps = [ "agenixInstall" ];
|
||||||
|
|
||||||
|
# Change ownership and group after users and groups are made.
|
||||||
|
system.activationScripts.agenixChown = {
|
||||||
|
text = chownSecrets;
|
||||||
|
deps = [
|
||||||
|
"users"
|
||||||
|
"groups"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
# So other activation scripts can depend on agenix being done.
|
||||||
system.activationScripts.agenix = {
|
system.activationScripts.agenix = {
|
||||||
text = installNonRootSecrets;
|
text = "";
|
||||||
deps = [
|
deps = [ "agenixChown"];
|
||||||
"users"
|
|
||||||
"groups"
|
|
||||||
"specialfs"
|
|
||||||
"agenixMountSecrets"
|
|
||||||
"agenixChownKeys"
|
|
||||||
];
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# Do not copy this! It is insecure. This is only okay because we are testing.
|
# Do not copy this! It is insecure. This is only okay because we are testing.
|
||||||
{
|
{
|
||||||
system.activationScripts.agenixRoot.deps = [ "installSSHHostKeys" ];
|
system.activationScripts.agenixInstall.deps = [ "installSSHHostKeys" ];
|
||||||
|
|
||||||
system.activationScripts.installSSHHostKeys.text = ''
|
system.activationScripts.installSSHHostKeys.text = ''
|
||||||
mkdir -p /etc/ssh
|
mkdir -p /etc/ssh
|
||||||
|
|
Loading…
Reference in a new issue