mirror of
https://github.com/ryantm/agenix.git
synced 2024-11-25 11:08:30 +03:00
contrib: use mkDerivation for agenix cli
* use mkDerivation * separate shell code in own file * use shellcheck to lint shell code * remove rage version check since rage is greater than 0.5.0 on all maintained nixpkgs
This commit is contained in:
parent
6053c559c5
commit
d0b75ddf9a
2 changed files with 189 additions and 177 deletions
194
pkgs/agenix.nix
194
pkgs/agenix.nix
|
@ -1,187 +1,37 @@
|
||||||
{
|
{
|
||||||
lib,
|
lib,
|
||||||
writeShellScriptBin,
|
stdenv,
|
||||||
runtimeShell,
|
|
||||||
callPackage,
|
|
||||||
rage,
|
rage,
|
||||||
gnused,
|
gnused,
|
||||||
nix,
|
nix,
|
||||||
mktemp,
|
mktemp,
|
||||||
diffutils,
|
diffutils,
|
||||||
ageBin ? "${
|
substituteAll,
|
||||||
# we need at least rage 0.5.0 to support ssh keys
|
ageBin ? "${rage}/bin/rage",
|
||||||
if rage.version < "0.5.0"
|
shellcheck,
|
||||||
then callPackage ./rage.nix {}
|
}:
|
||||||
else rage
|
stdenv.mkDerivation rec {
|
||||||
}/bin/rage",
|
pname = "agenix";
|
||||||
}: let
|
version = "0.13.0";
|
||||||
|
src = substituteAll {
|
||||||
|
inherit ageBin version;
|
||||||
sedBin = "${gnused}/bin/sed";
|
sedBin = "${gnused}/bin/sed";
|
||||||
nixInstantiate = "${nix}/bin/nix-instantiate";
|
nixInstantiate = "${nix}/bin/nix-instantiate";
|
||||||
mktempBin = "${mktemp}/bin/mktemp";
|
mktempBin = "${mktemp}/bin/mktemp";
|
||||||
diffBin = "${diffutils}/bin/diff";
|
diffBin = "${diffutils}/bin/diff";
|
||||||
in
|
src = ./agenix.sh;
|
||||||
lib.recursiveUpdate (writeShellScriptBin "agenix" ''
|
};
|
||||||
set -Eeuo pipefail
|
dontUnpack = true;
|
||||||
|
|
||||||
PACKAGE="agenix"
|
doCheck = true;
|
||||||
|
checkInputs = [shellcheck];
|
||||||
|
postCheck = ''
|
||||||
|
shellcheck $src
|
||||||
|
'';
|
||||||
|
|
||||||
function show_help () {
|
installPhase = ''
|
||||||
echo "$PACKAGE - edit and rekey age secret files"
|
install -D $src ${placeholder "out"}/bin/agenix
|
||||||
echo " "
|
'';
|
||||||
echo "$PACKAGE -e FILE [-i PRIVATE_KEY]"
|
|
||||||
echo "$PACKAGE -r [-i PRIVATE_KEY]"
|
|
||||||
echo ' '
|
|
||||||
echo 'options:'
|
|
||||||
echo '-h, --help show help'
|
|
||||||
echo '-e, --edit FILE edits FILE using $EDITOR'
|
|
||||||
echo '-r, --rekey re-encrypts all secrets with specified recipients'
|
|
||||||
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 'RULES environment variable with path to Nix file specifying recipient public keys.'
|
|
||||||
echo "Defaults to './secrets.nix'"
|
|
||||||
echo ' '
|
|
||||||
echo "agenix version: 0.13.0"
|
|
||||||
echo "age binary path: ${ageBin}"
|
|
||||||
echo "age version: $(${ageBin} --version)"
|
|
||||||
}
|
|
||||||
|
|
||||||
test $# -eq 0 && (show_help && exit 1)
|
|
||||||
|
|
||||||
REKEY=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
|
|
||||||
;;
|
|
||||||
-v|--verbose)
|
|
||||||
shift
|
|
||||||
set -x
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
show_help
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
RULES=''${RULES:-./secrets.nix}
|
|
||||||
|
|
||||||
function cleanup {
|
|
||||||
if [ ! -z ''${CLEARTEXT_DIR+x} ]
|
|
||||||
then
|
|
||||||
rm -rf "$CLEARTEXT_DIR"
|
|
||||||
fi
|
|
||||||
if [ ! -z ''${REENCRYPTED_DIR+x} ]
|
|
||||||
then
|
|
||||||
rm -rf "$REENCRYPTED_DIR"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
trap "cleanup" 0 2 3 15
|
|
||||||
|
|
||||||
function edit {
|
|
||||||
FILE=$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)
|
|
||||||
|
|
||||||
if [ -z "$KEYS" ]
|
|
||||||
then
|
|
||||||
>&2 echo "There is no rule for $FILE in $RULES."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
CLEARTEXT_DIR=$(${mktempBin} -d)
|
|
||||||
CLEARTEXT_FILE="$CLEARTEXT_DIR/$(basename "$FILE")"
|
|
||||||
|
|
||||||
if [ -f "$FILE" ]
|
|
||||||
then
|
|
||||||
DECRYPT=("''${DEFAULT_DECRYPT[@]}")
|
|
||||||
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
|
|
||||||
if [[ "''${DECRYPT[*]}" != *"--identity"* ]]; then
|
|
||||||
echo "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."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
DECRYPT+=(-o "$CLEARTEXT_FILE" "$FILE")
|
|
||||||
${ageBin} "''${DECRYPT[@]}" || exit 1
|
|
||||||
cp "$CLEARTEXT_FILE" "$CLEARTEXT_FILE.before"
|
|
||||||
fi
|
|
||||||
|
|
||||||
$EDITOR "$CLEARTEXT_FILE"
|
|
||||||
|
|
||||||
if [ ! -f "$CLEARTEXT_FILE" ]
|
|
||||||
then
|
|
||||||
echo "$FILE wasn't created."
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
[ -f "$FILE" ] && [ "$EDITOR" != ":" ] && ${diffBin} "$CLEARTEXT_FILE.before" "$CLEARTEXT_FILE" 1>/dev/null && echo "$FILE wasn't changed, skipping re-encryption." && return
|
|
||||||
|
|
||||||
ENCRYPT=()
|
|
||||||
while IFS= read -r key
|
|
||||||
do
|
|
||||||
ENCRYPT+=(--recipient "$key")
|
|
||||||
done <<< "$KEYS"
|
|
||||||
|
|
||||||
REENCRYPTED_DIR=$(${mktempBin} -d)
|
|
||||||
REENCRYPTED_FILE="$REENCRYPTED_DIR/$(basename "$FILE")"
|
|
||||||
|
|
||||||
ENCRYPT+=(-o "$REENCRYPTED_FILE")
|
|
||||||
|
|
||||||
${ageBin} "''${ENCRYPT[@]}" <"$CLEARTEXT_FILE" || exit 1
|
|
||||||
|
|
||||||
mv -f "$REENCRYPTED_FILE" "$1"
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
for FILE in $FILES
|
|
||||||
do
|
|
||||||
echo "rekeying $FILE..."
|
|
||||||
EDITOR=: edit "$FILE"
|
|
||||||
cleanup
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
[ $REKEY -eq 1 ] && rekey && exit 0
|
|
||||||
edit "$FILE" && cleanup && exit 0
|
|
||||||
'')
|
|
||||||
{
|
|
||||||
meta.description = "age-encrypted secrets for NixOS";
|
meta.description = "age-encrypted secrets for NixOS";
|
||||||
}
|
}
|
||||||
|
|
162
pkgs/agenix.sh
Normal file
162
pkgs/agenix.sh
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
#!/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 '-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 '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)"
|
||||||
|
}
|
||||||
|
|
||||||
|
test $# -eq 0 && (show_help && exit 1)
|
||||||
|
|
||||||
|
REKEY=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
|
||||||
|
;;
|
||||||
|
-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 edit {
|
||||||
|
FILE=$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)
|
||||||
|
|
||||||
|
if [ -z "$KEYS" ]
|
||||||
|
then
|
||||||
|
>&2 echo "There is no rule for $FILE in $RULES."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
CLEARTEXT_DIR=$(@mktempBin@ -d)
|
||||||
|
CLEARTEXT_FILE="$CLEARTEXT_DIR/$(basename "$FILE")"
|
||||||
|
|
||||||
|
if [ -f "$FILE" ]
|
||||||
|
then
|
||||||
|
DECRYPT=("${DEFAULT_DECRYPT[@]}")
|
||||||
|
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
|
||||||
|
if [[ "${DECRYPT[*]}" != *"--identity"* ]]; then
|
||||||
|
echo "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."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
DECRYPT+=(-o "$CLEARTEXT_FILE" "$FILE")
|
||||||
|
@ageBin@ "${DECRYPT[@]}" || exit 1
|
||||||
|
cp "$CLEARTEXT_FILE" "$CLEARTEXT_FILE.before"
|
||||||
|
fi
|
||||||
|
|
||||||
|
$EDITOR "$CLEARTEXT_FILE"
|
||||||
|
|
||||||
|
if [ ! -f "$CLEARTEXT_FILE" ]
|
||||||
|
then
|
||||||
|
echo "$FILE wasn't created."
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
[ -f "$FILE" ] && [ "$EDITOR" != ":" ] && @diffBin@ "$CLEARTEXT_FILE.before" "$CLEARTEXT_FILE" 1>/dev/null && echo "$FILE wasn't changed, skipping re-encryption." && return
|
||||||
|
|
||||||
|
ENCRYPT=()
|
||||||
|
while IFS= read -r key
|
||||||
|
do
|
||||||
|
ENCRYPT+=(--recipient "$key")
|
||||||
|
done <<< "$KEYS"
|
||||||
|
|
||||||
|
REENCRYPTED_DIR=$(@mktempBin@ -d)
|
||||||
|
REENCRYPTED_FILE="$REENCRYPTED_DIR/$(basename "$FILE")"
|
||||||
|
|
||||||
|
ENCRYPT+=(-o "$REENCRYPTED_FILE")
|
||||||
|
|
||||||
|
@ageBin@ "${ENCRYPT[@]}" <"$CLEARTEXT_FILE" || exit 1
|
||||||
|
|
||||||
|
mv -f "$REENCRYPTED_FILE" "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
for FILE in $FILES
|
||||||
|
do
|
||||||
|
echo "rekeying $FILE..."
|
||||||
|
EDITOR=: edit "$FILE"
|
||||||
|
cleanup
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
[ $REKEY -eq 1 ] && rekey && exit 0
|
||||||
|
edit "$FILE" && cleanup && exit 0
|
Loading…
Reference in a new issue