add flake and default .nix files; add agenix command
This commit is contained in:
parent
4c2fd23693
commit
79244b4fc3
6 changed files with 205 additions and 20 deletions
|
@ -1,6 +1,4 @@
|
|||
{ pkgs ? import <nixpkgs> {} }:
|
||||
rec {
|
||||
age-nix = pkgs.writeScriptBin "age-nix" ''
|
||||
exit 0
|
||||
'';
|
||||
{
|
||||
agenix = pkgs.callPackage ./pkgs/agenix.nix {};
|
||||
}
|
||||
|
|
25
flake.lock
Normal file
25
flake.lock
Normal file
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1599148892,
|
||||
"narHash": "sha256-V76c6DlI0ZZffvbBpxGlpVSpXxZ14QpFHwAvEEujIsY=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "7ff50a7f7b9a701228f870813fe58f01950f870b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"id": "nixpkgs",
|
||||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
|
@ -12,7 +12,7 @@
|
|||
forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f system);
|
||||
in {
|
||||
nixosModules.age = import ./modules/age.nix;
|
||||
# packages = forAllSystems (system: nixpkgs.legacyPackages.${system}.callPackage ./default.nix {});
|
||||
# defaultPackage = forAllSystems (system: self.packages.${system}.age-nix); #
|
||||
packages = forAllSystems (system: nixpkgs.legacyPackages.${system}.callPackage ./default.nix {});
|
||||
defaultPackage = forAllSystems (system: self.packages.${system}.agenix);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@ let
|
|||
chmod ${secretType.mode} "$TMP_FILE"
|
||||
chown ${secretType.owner}:${secretType.group} "$TMP_FILE"
|
||||
mv -f "$TMP_FILE" '${secretType.path}'
|
||||
|
||||
'';
|
||||
installAllSecrets = builtins.concatStringsSep "\n" (map installSecret (builtins.attrValues cfg.secrets));
|
||||
|
||||
|
@ -27,15 +26,12 @@ let
|
|||
'';
|
||||
};
|
||||
file = mkOption {
|
||||
type = types.either types.str types.path;
|
||||
type = types.path;
|
||||
description = ''
|
||||
Age file the secret is loaded from.
|
||||
'';
|
||||
};
|
||||
path = assert assertMsg (builtins.pathExists config.file) ''
|
||||
Cannot find path '${config.file}' set in 'age.secrets."${config._module.args.name}".file'
|
||||
'';
|
||||
mkOption {
|
||||
path = mkOption {
|
||||
type = types.str;
|
||||
default = "/run/secrets/${config.name}";
|
||||
description = ''
|
||||
|
@ -81,20 +77,15 @@ in {
|
|||
map (e: e.path) (lib.filter (e: e.type == "rsa" || e.type == "ed25519") config.services.openssh.hostKeys)
|
||||
else [];
|
||||
description = ''
|
||||
Path to SSH keys to be used as identities in age file decryption.
|
||||
Path to SSH keys to be used as identities in age decryption.
|
||||
'';
|
||||
};
|
||||
};
|
||||
config = mkIf (cfg.secrets != {}) {
|
||||
assertions = [{
|
||||
assertion = cfg.sshKeyPaths != [];
|
||||
message = "Either age.sshKeyPaths must be set.";
|
||||
}] ++ map (name: let
|
||||
inherit (cfg.secrets.${name}) file;
|
||||
in {
|
||||
assertion = builtins.isPath file;
|
||||
message = "${file} is not in the nix store. Either add it to the nix store.";
|
||||
}) (builtins.attrNames cfg.secrets);
|
||||
message = "age.sshKeyPaths must be set.";
|
||||
}];
|
||||
|
||||
system.activationScripts.setup-secrets = stringAfter [ "users" "groups" ] installAllSecrets;
|
||||
};
|
||||
|
|
122
pkgs/agenix.nix
Normal file
122
pkgs/agenix.nix
Normal file
|
@ -0,0 +1,122 @@
|
|||
{writeShellScriptBin, runtimeShell, age, yq-go} :
|
||||
writeShellScriptBin "agenix" ''
|
||||
set -euo pipefail
|
||||
PACKAGE="agenix"
|
||||
|
||||
function show_help () {
|
||||
echo "$PACKAGE - edit and rekey age secret files"
|
||||
echo " "
|
||||
echo "$PACKAGE -e FILE"
|
||||
echo "$PACKAGE -r"
|
||||
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 ' '
|
||||
echo 'FILE an age-encrypted file'
|
||||
echo ' '
|
||||
echo 'EDITOR environment variable of editor to use when editing FILE'
|
||||
echo ' '
|
||||
echo 'RULES environment variable with path to YAML file specifying recipient public keys.'
|
||||
echo "Defaults to 'secrets.yaml'"
|
||||
}
|
||||
|
||||
test $# -eq 0 && (show_help && exit 1)
|
||||
|
||||
REKEY=0
|
||||
|
||||
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
|
||||
;;
|
||||
-r|--rekey)
|
||||
shift
|
||||
REKEY=1
|
||||
;;
|
||||
*)
|
||||
show_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
RULES=''${RULES:-secrets.yaml}
|
||||
|
||||
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=$(${yq-go}/bin/yq r "$RULES" "secrets.(name==$FILE).public_keys.**")
|
||||
if [ -z "$KEYS" ]
|
||||
then
|
||||
>&2 echo "There is no rule for $FILE in $RULES."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
CLEARTEXT_DIR=$(mktemp -d)
|
||||
CLEARTEXT_FILE="$CLEARTEXT_DIR/$(basename "$FILE")"
|
||||
|
||||
if [ -f "$FILE" ]
|
||||
then
|
||||
DECRYPT=(--decrypt)
|
||||
while IFS= read -r key
|
||||
do
|
||||
DECRYPT+=(--identity "$key")
|
||||
done <<<$(find ~/.ssh -maxdepth 1 -type f -not -name "*pub" -not -name "config" -not -name "authorized_keys" -not -name "known_hosts")
|
||||
DECRYPT+=(-o "$CLEARTEXT_FILE" "$FILE")
|
||||
${age}/bin/age "''${DECRYPT[@]}"
|
||||
fi
|
||||
|
||||
$EDITOR "$CLEARTEXT_FILE"
|
||||
|
||||
ENCRYPT=()
|
||||
while IFS= read -r key
|
||||
do
|
||||
ENCRYPT+=(--recipient "$key")
|
||||
done <<< "$KEYS"
|
||||
|
||||
REENCRYPTED_DIR=$(mktemp -d)
|
||||
REENCRYPTED_FILE="$REENCRYPTED_DIR/$(basename "$FILE")"
|
||||
|
||||
ENCRYPT+=(-o "$REENCRYPTED_FILE")
|
||||
|
||||
cat "$CLEARTEXT_FILE" | ${age}/bin/age "''${ENCRYPT[@]}"
|
||||
|
||||
mv -f "$REENCRYPTED_FILE" "$1"
|
||||
}
|
||||
|
||||
function rekey {
|
||||
echo "rekeying..."
|
||||
FILES=$(${yq-go}/bin/yq r "$RULES" "secrets.*.name")
|
||||
for FILE in $FILES
|
||||
do
|
||||
EDITOR=: edit $FILE
|
||||
done
|
||||
}
|
||||
|
||||
[ $REKEY -eq 1 ] && rekey && exit 0
|
||||
edit $FILE && exit 0
|
||||
''
|
49
pkgs/agenix.sh
Normal file
49
pkgs/agenix.sh
Normal file
|
@ -0,0 +1,49 @@
|
|||
#! /usr/bin/env nix-shell
|
||||
#! nix-shell -i bash -p age yq-go moreutils
|
||||
|
||||
while test $# -gt 0; do
|
||||
case "$1" in
|
||||
-h|--help)
|
||||
echo "$package - attempt to capture frames"
|
||||
echo " "
|
||||
echo "$package [options] application [arguments]"
|
||||
echo " "
|
||||
echo "options:"
|
||||
echo "-h, --help show brief help"
|
||||
echo "-a, --action=ACTION specify an action to use"
|
||||
echo "-o, --output-dir=DIR specify a directory to store output in"
|
||||
exit 0
|
||||
;;
|
||||
-a)
|
||||
shift
|
||||
if test $# -gt 0; then
|
||||
export PROCESS=$1
|
||||
else
|
||||
echo "no process specified"
|
||||
exit 1
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
--action*)
|
||||
export PROCESS=`echo $1 | sed -e 's/^[^=]*=//g'`
|
||||
shift
|
||||
;;
|
||||
-o)
|
||||
shift
|
||||
if test $# -gt 0; then
|
||||
export OUTPUT=$1
|
||||
else
|
||||
echo "no output dir specified"
|
||||
exit 1
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
--output-dir*)
|
||||
export OUTPUT=`echo $1 | sed -e 's/^[^=]*=//g'`
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
Loading…
Reference in a new issue