2023-01-29 22:58:38 +03:00
#!/usr/bin/env bash
set -Eeuo pipefail
PACKAGE = "agenix"
function show_help ( ) {
echo " $PACKAGE - edit and rekey age secret files "
echo " "
echo " $PACKAGE -e FILE [-i PRIVATE_KEY] "
echo " $PACKAGE -r [-i PRIVATE_KEY] "
echo ' '
echo 'options:'
echo '-h, --help show help'
# shellcheck disable=SC2016
echo '-e, --edit FILE edits FILE using $EDITOR'
2024-11-24 14:13:31 +03:00
echo '-f, --encrypt FILE OUTPUT encrypts a given FILE to OUTPUT'
2023-01-29 22:58:38 +03:00
echo '-r, --rekey re-encrypts all secrets with specified recipients'
2023-02-21 04:15:37 +03:00
echo '-d, --decrypt FILE decrypts FILE to STDOUT'
2023-01-29 22:58:38 +03:00
echo '-i, --identity identity to use when decrypting'
echo '-v, --verbose verbose output'
echo ' '
echo 'FILE an age-encrypted file'
echo ' '
echo 'PRIVATE_KEY a path to a private SSH key used to decrypt file'
echo ' '
echo 'EDITOR environment variable of editor to use when editing FILE'
echo ' '
2023-02-19 21:20:07 +03:00
echo 'If STDIN is not interactive, EDITOR will be set to "cp /dev/stdin"'
echo ' '
2023-01-29 22:58:38 +03:00
echo 'RULES environment variable with path to Nix file specifying recipient public keys.'
echo "Defaults to './secrets.nix'"
echo ' '
echo "agenix version: @version@"
echo "age binary path: @ageBin@"
echo " age version: $( @ageBin@ --version) "
}
2023-02-21 22:46:44 +03:00
function warn( ) {
printf '%s\n' " $* " >& 2
}
function err( ) {
warn " $* "
exit 1
}
2023-01-29 22:58:38 +03:00
test $# -eq 0 && ( show_help && exit 1)
REKEY = 0
2023-02-21 04:15:37 +03:00
DECRYPT_ONLY = 0
2024-11-24 14:13:31 +03:00
ENCRYPT_FILE = 0
2023-01-29 22:58:38 +03:00
DEFAULT_DECRYPT = ( --decrypt)
while test $# -gt 0; do
case " $1 " in
-h| --help)
show_help
exit 0
; ;
-e| --edit)
shift
if test $# -gt 0; then
export FILE = $1
else
echo "no FILE specified"
exit 1
fi
shift
; ;
2024-11-24 14:13:31 +03:00
-f| --encrypt)
shift
ENCRYPT_FILE = 1
if test $# -gt 0; then
export FILE = $1
else
echo "no FILE specified"
exit 1
fi
shift
if test $# -gt 0; then
export OUTPUT = $1
else
echo "no OUTPUT specified"
exit 1
fi
shift
; ;
2023-01-29 22:58:38 +03:00
-i| --identity)
shift
if test $# -gt 0; then
DEFAULT_DECRYPT += ( --identity " $1 " )
else
echo "no PRIVATE_KEY specified"
exit 1
fi
shift
; ;
-r| --rekey)
shift
REKEY = 1
; ;
2023-02-21 04:15:37 +03:00
-d| --decrypt)
shift
DECRYPT_ONLY = 1
if test $# -gt 0; then
export FILE = $1
else
echo "no FILE specified"
exit 1
fi
shift
; ;
2023-01-29 22:58:38 +03:00
-v| --verbose)
shift
set -x
; ;
*)
show_help
exit 1
; ;
esac
done
RULES = ${ RULES :- ./secrets.nix }
function cleanup {
if [ -n " ${ CLEARTEXT_DIR +x } " ]
then
rm -rf " $CLEARTEXT_DIR "
fi
if [ -n " ${ REENCRYPTED_DIR +x } " ]
then
rm -rf " $REENCRYPTED_DIR "
fi
}
trap "cleanup" 0 2 3 15
2023-02-21 04:15:37 +03:00
function keys {
2023-11-15 20:29:09 +03:00
( @nixInstantiate@ --json --eval --strict -E " (let rules = import $RULES ; in rules.\" $1 \".publicKeys) " | @jqBin@ -r .[ ] ) || exit 1
2023-02-21 04:15:37 +03:00
}
2023-01-29 22:58:38 +03:00
2023-02-21 04:15:37 +03:00
function decrypt {
FILE = $1
KEYS = $2
2023-01-29 22:58:38 +03:00
if [ -z " $KEYS " ]
then
2023-02-21 22:46:44 +03:00
err " There is no rule for $FILE in $RULES . "
2023-01-29 22:58:38 +03:00
fi
if [ -f " $FILE " ]
then
DECRYPT = ( " ${ DEFAULT_DECRYPT [@] } " )
2023-02-18 22:55:58 +03:00
if [ [ " ${ DECRYPT [*] } " != *"--identity" * ] ] ; then
if [ -f " $HOME /.ssh/id_rsa " ] ; then
DECRYPT += ( --identity " $HOME /.ssh/id_rsa " )
fi
if [ -f " $HOME /.ssh/id_ed25519 " ] ; then
DECRYPT += ( --identity " $HOME /.ssh/id_ed25519 " )
fi
2023-01-29 22:58:38 +03:00
fi
if [ [ " ${ DECRYPT [*] } " != *"--identity" * ] ] ; then
2023-02-21 22:46:44 +03:00
err " No identity found to decrypt $FILE . Try adding an SSH key at $HOME /.ssh/id_rsa or $HOME /.ssh/id_ed25519 or using the --identity flag to specify a file. "
2023-01-29 22:58:38 +03:00
fi
2023-02-24 11:00:48 +03:00
@ageBin@ " ${ DECRYPT [@] } " " $FILE " || exit 1
2023-01-29 22:58:38 +03:00
fi
2023-02-21 04:15:37 +03:00
}
function edit {
FILE = $1
KEYS = $( keys " $FILE " ) || exit 1
2023-02-24 11:00:48 +03:00
CLEARTEXT_DIR = $( @mktempBin@ -d)
CLEARTEXT_FILE = " $CLEARTEXT_DIR / $( basename " $FILE " ) "
DEFAULT_DECRYPT += ( -o " $CLEARTEXT_FILE " )
2023-02-21 04:15:37 +03:00
decrypt " $FILE " " $KEYS " || exit 1
2023-01-29 22:58:38 +03:00
2023-03-19 07:17:27 +03:00
[ ! -f " $CLEARTEXT_FILE " ] || cp " $CLEARTEXT_FILE " " $CLEARTEXT_FILE .before "
2023-02-24 11:00:48 +03:00
2023-02-19 21:20:07 +03:00
[ -t 0 ] || EDITOR = 'cp /dev/stdin'
2023-01-29 22:58:38 +03:00
$EDITOR " $CLEARTEXT_FILE "
if [ ! -f " $CLEARTEXT_FILE " ]
then
2023-02-21 22:46:44 +03:00
warn " $FILE wasn't created. "
2023-01-29 22:58:38 +03:00
return
fi
2023-02-21 22:46:44 +03:00
[ -f " $FILE " ] && [ " $EDITOR " != ":" ] && @diffBin@ -q " $CLEARTEXT_FILE .before " " $CLEARTEXT_FILE " && warn " $FILE wasn't changed, skipping re-encryption. " && return
2023-01-29 22:58:38 +03:00
ENCRYPT = ( )
while IFS = read -r key
do
2024-04-13 00:50:07 +03:00
if [ -n " $key " ] ; then
ENCRYPT += ( --recipient " $key " )
fi
2023-01-29 22:58:38 +03:00
done <<< " $KEYS "
REENCRYPTED_DIR = $( @mktempBin@ -d)
REENCRYPTED_FILE = " $REENCRYPTED_DIR / $( basename " $FILE " ) "
ENCRYPT += ( -o " $REENCRYPTED_FILE " )
@ageBin@ " ${ ENCRYPT [@] } " <" $CLEARTEXT_FILE " || exit 1
2023-11-04 00:57:48 +03:00
mkdir -p " $( dirname " $FILE " ) "
2023-11-04 00:53:33 +03:00
mv -f " $REENCRYPTED_FILE " " $FILE "
2023-01-29 22:58:38 +03:00
}
2024-11-24 14:13:31 +03:00
function encrypt {
FILE = $1
OUTPUT = $2
KEYS = $( keys " $OUTPUT " ) || exit 1
if [ ! -f " $FILE " ]
then
warn " $FILE does not exist. "
return
fi
ENCRYPT = ( )
while IFS = read -r key
do
if [ -n " $key " ] ; then
ENCRYPT += ( --recipient " $key " )
fi
done <<< " $KEYS "
2024-11-24 20:26:22 +03:00
ENCRYPT += ( -o " $OUTPUT " )
2024-11-24 14:13:31 +03:00
@ageBin@ " ${ ENCRYPT [@] } " <" $FILE " || exit 1
}
2023-01-29 22:58:38 +03:00
function rekey {
2023-10-08 17:31:54 +03:00
FILES = $( ( @nixInstantiate@ --json --eval -E " (let rules = import $RULES ; in builtins.attrNames rules) " | @jqBin@ -r .[ ] ) || exit 1)
2023-01-29 22:58:38 +03:00
for FILE in $FILES
do
2023-02-21 22:46:44 +03:00
warn " rekeying $FILE ... "
2023-01-29 22:58:38 +03:00
EDITOR = : edit " $FILE "
cleanup
done
}
[ $REKEY -eq 1 ] && rekey && exit 0
2023-03-14 20:53:32 +03:00
[ $DECRYPT_ONLY -eq 1 ] && DEFAULT_DECRYPT += ( "-o" "-" ) && decrypt " ${ FILE } " " $( keys " $FILE " ) " && exit 0
2024-11-24 14:13:31 +03:00
[ $ENCRYPT_FILE -eq 1 ] && encrypt " $FILE " " $OUTPUT " && exit 0
2023-01-29 22:58:38 +03:00
edit " $FILE " && cleanup && exit 0