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
 |