diff --git a/modules/age.nix b/modules/age.nix index 86c4447..8a05b42 100644 --- a/modules/age.nix +++ b/modules/age.nix @@ -83,6 +83,15 @@ with lib; let test -f "${secretType.file}" || echo '[agenix] WARNING: encrypted file ${secretType.file} does not exist!' test -d "$(dirname "$TMP_FILE")" || echo "[agenix] WARNING: $(dirname "$TMP_FILE") does not exist!" LANG=${config.i18n.defaultLocale or "C"} ${ageBin} --decrypt "''${IDENTITIES[@]}" -o "$TMP_FILE" "${secretType.file}" + ${optionalString (secretType.derive.path != null) '' + TMP_FILE_DERIVED="$_truePath.derive.tmp" + trap "rm -f $TMP_FILE_DERIVED" EXIT + (cat $TMP_FILE && printf ${escapeShellArg secretType.derive.path}) | \ + sha256sum -z | tr -d '-' > $TMP_FILE_DERIVED + openssl enc -aes-256-cbc -kfile "$TMP_FILE_DERIVED" -in /dev/zero -iv "" -nosalt | \ + tr -dc ${escapeShellArg secretType.derive.filter} | \ + head -c ${escapeShellArg secretType.derive.length} > $TMP_FILE + ''} ) chmod ${secretType.mode} "$TMP_FILE" mv -f "$TMP_FILE" "$_truePath" @@ -155,6 +164,20 @@ with lib; let Path where the decrypted secret is installed. ''; }; + derive = { + path = mkOption { + type = with types; nullOr str; + default = null; + }; + filter = mkOption { + type = types.str; + default = "A-Za-z0-9_"; + }; + length = mkOption { + type = types.int; + default = 32; + }; + }; mode = mkOption { type = types.str; default = "0400"; diff --git a/test/integration.nix b/test/integration.nix index e0ee85a..e757471 100644 --- a/test/integration.nix +++ b/test/integration.nix @@ -24,8 +24,18 @@ pkgs.nixosTest { services.openssh.enable = true; - age.secrets.passwordfile-user1 = { - file = ../example/passwordfile-user1.age; + age.secrets = rec { + passwordfile-user1 = { + file = ../example/passwordfile-user1.age; + }; + passwordfile-user1-derived = + passwordfile-user1 + // { + derive = { + length = 16; + path = "test"; + }; + }; }; age.identityPaths = options.age.identityPaths.default ++ ["/etc/ssh/this_key_wont_exist"];