feature: add -d/--decrypt option to decrypt a file to stdout

This commit is contained in:
muvlon 2023-02-21 02:15:37 +01:00 committed by Ryan Mulligan
parent 2d735d6518
commit 9cf1967039
3 changed files with 45 additions and 5 deletions

View file

@ -483,6 +483,8 @@ Overriding `age.secretsMountPoint` example:
### agenix CLI reference ### agenix CLI reference
``` ```
agenix - edit and rekey age secret files
agenix -e FILE [-i PRIVATE_KEY] agenix -e FILE [-i PRIVATE_KEY]
agenix -r [-i PRIVATE_KEY] agenix -r [-i PRIVATE_KEY]
@ -490,6 +492,7 @@ options:
-h, --help show help -h, --help show help
-e, --edit FILE edits FILE using $EDITOR -e, --edit FILE edits FILE using $EDITOR
-r, --rekey re-encrypts all secrets with specified recipients -r, --rekey re-encrypts all secrets with specified recipients
-d, --decrypt FILE decrypts FILE to STDOUT
-i, --identity identity to use when decrypting -i, --identity identity to use when decrypting
-v, --verbose verbose output -v, --verbose verbose output
@ -499,6 +502,8 @@ PRIVATE_KEY a path to a private SSH key used to decrypt file
EDITOR environment variable of editor to use when editing FILE EDITOR environment variable of editor to use when editing FILE
If STDIN is not interactive, EDITOR will be set to "cp /dev/stdin"
RULES environment variable with path to Nix file specifying recipient public keys. RULES environment variable with path to Nix file specifying recipient public keys.
Defaults to './secrets.nix' Defaults to './secrets.nix'
``` ```

View file

@ -14,6 +14,7 @@ function show_help () {
# shellcheck disable=SC2016 # shellcheck disable=SC2016
echo '-e, --edit FILE edits FILE using $EDITOR' echo '-e, --edit FILE edits FILE using $EDITOR'
echo '-r, --rekey re-encrypts all secrets with specified recipients' echo '-r, --rekey re-encrypts all secrets with specified recipients'
echo '-d, --decrypt FILE decrypts FILE to STDOUT'
echo '-i, --identity identity to use when decrypting' echo '-i, --identity identity to use when decrypting'
echo '-v, --verbose verbose output' echo '-v, --verbose verbose output'
echo ' ' echo ' '
@ -45,6 +46,7 @@ function err() {
test $# -eq 0 && (show_help && exit 1) test $# -eq 0 && (show_help && exit 1)
REKEY=0 REKEY=0
DECRYPT_ONLY=0
DEFAULT_DECRYPT=(--decrypt) DEFAULT_DECRYPT=(--decrypt)
while test $# -gt 0; do while test $# -gt 0; do
@ -77,6 +79,17 @@ while test $# -gt 0; do
shift shift
REKEY=1 REKEY=1
;; ;;
-d|--decrypt)
shift
DECRYPT_ONLY=1
if test $# -gt 0; then
export FILE=$1
else
echo "no FILE specified"
exit 1
fi
shift
;;
-v|--verbose) -v|--verbose)
shift shift
set -x set -x
@ -89,7 +102,6 @@ while test $# -gt 0; do
done done
RULES=${RULES:-./secrets.nix} RULES=${RULES:-./secrets.nix}
function cleanup { function cleanup {
if [ -n "${CLEARTEXT_DIR+x}" ] if [ -n "${CLEARTEXT_DIR+x}" ]
then then
@ -102,10 +114,13 @@ function cleanup {
} }
trap "cleanup" 0 2 3 15 trap "cleanup" 0 2 3 15
function edit { function keys {
FILE=$1 (@nixInstantiate@ --eval -E "(let rules = import $RULES; in builtins.concatStringsSep \"\n\" rules.\"$1\".publicKeys)" | @sedBin@ 's/"//g' | @sedBin@ 's/\\n/\n/g') | @sedBin@ '/^$/d' || exit 1
KEYS=$( (@nixInstantiate@ --eval -E "(let rules = import $RULES; in builtins.concatStringsSep \"\n\" rules.\"$FILE\".publicKeys)" | @sedBin@ 's/"//g' | @sedBin@ 's/\\n/\n/g') | @sedBin@ '/^$/d' || exit 1) }
function decrypt {
FILE=$1
KEYS=$2
if [ -z "$KEYS" ] if [ -z "$KEYS" ]
then then
err "There is no rule for $FILE in $RULES." err "There is no rule for $FILE in $RULES."
@ -132,6 +147,12 @@ function edit {
@ageBin@ "${DECRYPT[@]}" || exit 1 @ageBin@ "${DECRYPT[@]}" || exit 1
cp "$CLEARTEXT_FILE" "$CLEARTEXT_FILE.before" cp "$CLEARTEXT_FILE" "$CLEARTEXT_FILE.before"
fi fi
}
function edit {
FILE=$1
KEYS=$(keys "$FILE") || exit 1
decrypt "$FILE" "$KEYS" || exit 1
[ -t 0 ] || EDITOR='cp /dev/stdin' [ -t 0 ] || EDITOR='cp /dev/stdin'
@ -160,6 +181,14 @@ function edit {
mv -f "$REENCRYPTED_FILE" "$1" mv -f "$REENCRYPTED_FILE" "$1"
} }
function decrypt_only {
FILE=$1
KEYS=$(keys "$FILE") || exit 1
decrypt "$FILE" "$KEYS"
printf "%s" "$(<"${CLEARTEXT_FILE}")"
cleanup
}
function rekey { function rekey {
FILES=$( (@nixInstantiate@ --eval -E "(let rules = import $RULES; in builtins.concatStringsSep \"\n\" (builtins.attrNames rules))" | @sedBin@ 's/"//g' | @sedBin@ 's/\\n/\n/g') || exit 1) FILES=$( (@nixInstantiate@ --eval -E "(let rules = import $RULES; in builtins.concatStringsSep \"\n\" (builtins.attrNames rules))" | @sedBin@ 's/"//g' | @sedBin@ 's/\\n/\n/g') || exit 1)
@ -172,4 +201,5 @@ function rekey {
} }
[ $REKEY -eq 1 ] && rekey && exit 0 [ $REKEY -eq 1 ] && rekey && exit 0
[ $DECRYPT_ONLY -eq 1 ] && decrypt_only "${FILE}" && exit 0
edit "$FILE" && cleanup && exit 0 edit "$FILE" && cleanup && exit 0

View file

@ -90,6 +90,11 @@ pkgs.nixosTest {
# user1 can edit a secret by piping in contents # user1 can edit a secret by piping in contents
system1.succeed(userDo("echo 'secret1234' | agenix -e passwordfile-user1.age")) system1.succeed(userDo("echo 'secret1234' | agenix -e passwordfile-user1.age"))
assert "secret1234" in system1.succeed(userDo("EDITOR=cat agenix -e passwordfile-user1.age"))
# and get it back out via --decrypt
assert "secret1234" in system1.succeed(userDo("agenix -d passwordfile-user1.age"))
# finally, the plain text should not linger around anywhere in the filesystem.
system1.fail("grep -r secret1234 /tmp")
''; '';
} }