forked from rarias/jungle
		
	Avoids adding an extra flake input only to fetch a single module and package. Reviewed-by: Aleix Boné <abonerib@bsc.es> Tested-by: Rodrigo Arias Mallo <rodrigo.arias@bsc.es>
		
			
				
	
	
		
			213 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			Bash
		
	
	
	
	
	
			
		
		
	
	
			213 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			Bash
		
	
	
	
	
	
| #!/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'
 | |
|   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 '-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 ' '
 | |
|   echo 'If STDIN is not interactive, EDITOR will be set to "cp /dev/stdin"'
 | |
|   echo ' '
 | |
|   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)"
 | |
| }
 | |
| 
 | |
| function warn() {
 | |
|   printf '%s\n' "$*" >&2
 | |
| }
 | |
| 
 | |
| function err() {
 | |
|   warn "$*"
 | |
|   exit 1
 | |
| }
 | |
| 
 | |
| test $# -eq 0 && (show_help && exit 1)
 | |
| 
 | |
| REKEY=0
 | |
| DECRYPT_ONLY=0
 | |
| 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
 | |
|       ;;
 | |
|     -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
 | |
|       ;;
 | |
|     -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)
 | |
|       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
 | |
| 
 | |
| function keys {
 | |
|     (@nixInstantiate@ --json --eval --strict -E "(let rules = import $RULES; in rules.\"$1\".publicKeys)" | @jqBin@ -r .[]) || exit 1
 | |
| }
 | |
| 
 | |
| function armor {
 | |
|     (@nixInstantiate@ --json --eval --strict -E "(let rules = import $RULES; in (builtins.hasAttr \"armor\" rules.\"$1\" && rules.\"$1\".armor))") || exit 1
 | |
| }
 | |
| 
 | |
| function decrypt {
 | |
|     FILE=$1
 | |
|     KEYS=$2
 | |
|     if [ -z "$KEYS" ]
 | |
|     then
 | |
|         err "There is no rule for $FILE in $RULES."
 | |
|     fi
 | |
| 
 | |
|     if [ -f "$FILE" ]
 | |
|     then
 | |
|         DECRYPT=("${DEFAULT_DECRYPT[@]}")
 | |
|         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
 | |
|         fi
 | |
|         if [[ "${DECRYPT[*]}" != *"--identity"* ]]; then
 | |
|           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."
 | |
|         fi
 | |
| 
 | |
|         @ageBin@ "${DECRYPT[@]}" -- "$FILE" || exit 1
 | |
|     fi
 | |
| }
 | |
| 
 | |
| function edit {
 | |
|     FILE=$1
 | |
|     KEYS=$(keys "$FILE") || exit 1
 | |
|     ARMOR=$(armor "$FILE") || exit 1
 | |
| 
 | |
|     CLEARTEXT_DIR=$(@mktempBin@ -d)
 | |
|     CLEARTEXT_FILE="$CLEARTEXT_DIR/$(basename -- "$FILE")"
 | |
|     DEFAULT_DECRYPT+=(-o "$CLEARTEXT_FILE")
 | |
| 
 | |
|     decrypt "$FILE" "$KEYS" || exit 1
 | |
| 
 | |
|     [ ! -f "$CLEARTEXT_FILE" ] || cp -- "$CLEARTEXT_FILE" "$CLEARTEXT_FILE.before"
 | |
| 
 | |
|     [ -t 0 ] || EDITOR='cp -- /dev/stdin'
 | |
| 
 | |
|     $EDITOR "$CLEARTEXT_FILE"
 | |
| 
 | |
|     if [ ! -f "$CLEARTEXT_FILE" ]
 | |
|     then
 | |
|       warn "$FILE wasn't created."
 | |
|       return
 | |
|     fi
 | |
|     [ -f "$FILE" ] && [ "$EDITOR" != ":" ] && @diffBin@ -q -- "$CLEARTEXT_FILE.before" "$CLEARTEXT_FILE" && warn "$FILE wasn't changed, skipping re-encryption." && return
 | |
| 
 | |
|     ENCRYPT=()
 | |
|     if [[ "$ARMOR" == "true" ]]; then
 | |
|         ENCRYPT+=(--armor)
 | |
|     fi
 | |
|     while IFS= read -r key
 | |
|     do
 | |
|         if [ -n "$key" ]; then
 | |
|             ENCRYPT+=(--recipient "$key")
 | |
|         fi
 | |
|     done <<< "$KEYS"
 | |
| 
 | |
|     REENCRYPTED_DIR=$(@mktempBin@ -d)
 | |
|     REENCRYPTED_FILE="$REENCRYPTED_DIR/$(basename -- "$FILE")"
 | |
| 
 | |
|     ENCRYPT+=(-o "$REENCRYPTED_FILE")
 | |
| 
 | |
|     @ageBin@ "${ENCRYPT[@]}" <"$CLEARTEXT_FILE" || exit 1
 | |
| 
 | |
|     mkdir -p -- "$(dirname -- "$FILE")"
 | |
| 
 | |
|     mv -f -- "$REENCRYPTED_FILE" "$FILE"
 | |
| }
 | |
| 
 | |
| function rekey {
 | |
|     FILES=$( (@nixInstantiate@ --json --eval -E "(let rules = import $RULES; in builtins.attrNames rules)"  | @jqBin@ -r .[]) || exit 1)
 | |
| 
 | |
|     for FILE in $FILES
 | |
|     do
 | |
|         warn "rekeying $FILE..."
 | |
|         EDITOR=: edit "$FILE"
 | |
|         cleanup
 | |
|     done
 | |
| }
 | |
| 
 | |
| [ $REKEY -eq 1 ] && rekey && exit 0
 | |
| [ $DECRYPT_ONLY -eq 1 ] && DEFAULT_DECRYPT+=("-o" "-") && decrypt "${FILE}" "$(keys "$FILE")" && exit 0
 | |
| edit "$FILE" && cleanup && exit 0
 |