contrib: format Nix code with Alejandra

This commit is contained in:
Ryan Mulligan 2023-01-29 08:36:01 -08:00
parent 99e0963743
commit 16bef569f4
6 changed files with 245 additions and 226 deletions

View file

@ -1,14 +1,17 @@
{ config, options, lib, pkgs, ... }: {
config,
with lib; options,
lib,
let pkgs,
...
}:
with lib; let
cfg = config.age; cfg = config.age;
# we need at least rage 0.5.0 to support ssh keys # we need at least rage 0.5.0 to support ssh keys
rage = rage =
if lib.versionOlder pkgs.rage.version "0.5.0" if lib.versionOlder pkgs.rage.version "0.5.0"
then pkgs.callPackage ../pkgs/rage.nix { } then pkgs.callPackage ../pkgs/rage.nix {}
else pkgs.rage; else pkgs.rage;
ageBin = config.age.ageBin; ageBin = config.age.ageBin;
@ -28,11 +31,15 @@ let
identities = builtins.concatStringsSep " " (map (path: "-i ${path}") cfg.identityPaths); identities = builtins.concatStringsSep " " (map (path: "-i ${path}") cfg.identityPaths);
setTruePath = secretType: '' setTruePath = secretType: ''
${if secretType.symlink then '' ${
_truePath="${cfg.secretsMountPoint}/$_agenix_generation/${secretType.name}" if secretType.symlink
'' else '' then ''
_truePath="${secretType.path}" _truePath="${cfg.secretsMountPoint}/$_agenix_generation/${secretType.name}"
''} ''
else ''
_truePath="${secretType.path}"
''
}
''; '';
installSecret = secretType: '' installSecret = secretType: ''
@ -55,9 +62,11 @@ let
''} ''}
''; '';
testIdentities = map (path: '' testIdentities =
test -f ${path} || echo '[agenix] WARNING: config.age.identityPaths entry ${path} not present!' map (path: ''
'') cfg.identityPaths; test -f ${path} || echo '[agenix] WARNING: config.age.identityPaths entry ${path} not present!'
'')
cfg.identityPaths;
cleanupAndLink = '' cleanupAndLink = ''
_agenix_generation="$(basename "$(readlink ${cfg.secretsDir})" || echo 0)" _agenix_generation="$(basename "$(readlink ${cfg.secretsDir})" || echo 0)"
@ -72,10 +81,10 @@ let
''; '';
installSecrets = builtins.concatStringsSep "\n" ( installSecrets = builtins.concatStringsSep "\n" (
[ "echo '[agenix] decrypting secrets...'" ] ["echo '[agenix] decrypting secrets...'"]
++ testIdentities ++ testIdentities
++ (map installSecret (builtins.attrValues cfg.secrets)) ++ (map installSecret (builtins.attrValues cfg.secrets))
++ [ cleanupAndLink ] ++ [cleanupAndLink]
); );
chownSecret = secretType: '' chownSecret = secretType: ''
@ -90,11 +99,12 @@ let
''; '';
chownSecrets = builtins.concatStringsSep "\n" ( chownSecrets = builtins.concatStringsSep "\n" (
[ "echo '[agenix] chowning...'" ] ["echo '[agenix] chowning...'"]
++ [ chownMountPoint ] ++ [chownMountPoint]
++ (map chownSecret (builtins.attrValues cfg.secrets))); ++ (map chownSecret (builtins.attrValues cfg.secrets))
);
secretType = types.submodule ({ config, ... }: { secretType = types.submodule ({config, ...}: {
options = { options = {
name = mkOption { name = mkOption {
type = types.str; type = types.str;
@ -137,14 +147,12 @@ let
Group of the decrypted secret. Group of the decrypted secret.
''; '';
}; };
symlink = mkEnableOption "symlinking secrets to their destination" // { default = true; }; symlink = mkEnableOption "symlinking secrets to their destination" // {default = true;};
}; };
}); });
in in {
{
imports = [ imports = [
(mkRenamedOptionModule [ "age" "sshKeyPaths" ] [ "age" "identityPaths" ]) (mkRenamedOptionModule ["age" "sshKeyPaths"] ["age" "identityPaths"])
]; ];
options.age = { options.age = {
@ -157,7 +165,7 @@ in
}; };
secrets = mkOption { secrets = mkOption {
type = types.attrsOf secretType; type = types.attrsOf secretType;
default = { }; default = {};
description = '' description = ''
Attrset of secrets. Attrset of secrets.
''; '';
@ -170,11 +178,13 @@ in
''; '';
}; };
secretsMountPoint = mkOption { secretsMountPoint = mkOption {
type = types.addCheck types.str type =
types.addCheck types.str
(s: (s:
(builtins.match "[ \t\n]*" s) == null # non-empty (builtins.match "[ \t\n]*" s)
&& (builtins.match ".+/" s) == null) # without trailing slash == null # non-empty
// { description = "${types.str.description} (with check: non-empty without trailing slash)"; }; && (builtins.match ".+/" s) == null) # without trailing slash
// {description = "${types.str.description} (with check: non-empty without trailing slash)";};
default = "/run/agenix.d"; default = "/run/agenix.d";
defaultText = "/run/agenix.d"; defaultText = "/run/agenix.d";
description = '' description = ''
@ -184,20 +194,22 @@ in
identityPaths = mkOption { identityPaths = mkOption {
type = types.listOf types.path; type = types.listOf types.path;
default = default =
if config.services.openssh.enable then if config.services.openssh.enable
map (e: e.path) (lib.filter (e: e.type == "rsa" || e.type == "ed25519") config.services.openssh.hostKeys) then map (e: e.path) (lib.filter (e: e.type == "rsa" || e.type == "ed25519") config.services.openssh.hostKeys)
else [ ]; else [];
description = '' description = ''
Path to SSH keys to be used as identities in age decryption. Path to SSH keys to be used as identities in age decryption.
''; '';
}; };
}; };
config = mkIf (cfg.secrets != { }) { config = mkIf (cfg.secrets != {}) {
assertions = [{ assertions = [
assertion = cfg.identityPaths != [ ]; {
message = "age.identityPaths must be set."; assertion = cfg.identityPaths != [];
}]; message = "age.identityPaths must be set.";
}
];
# Create a new directory full of secrets for symlinking (this helps # Create a new directory full of secrets for symlinking (this helps
# ensure removed secrets are actually removed, or at least become # ensure removed secrets are actually removed, or at least become
@ -218,7 +230,7 @@ in
}; };
# So user passwords can be encrypted. # So user passwords can be encrypted.
system.activationScripts.users.deps = [ "agenixInstall" ]; system.activationScripts.users.deps = ["agenixInstall"];
# Change ownership and group after users and groups are made. # Change ownership and group after users and groups are made.
system.activationScripts.agenixChown = { system.activationScripts.agenixChown = {
@ -232,8 +244,7 @@ in
# So other activation scripts can depend on agenix being done. # So other activation scripts can depend on agenix being done.
system.activationScripts.agenix = { system.activationScripts.agenix = {
text = ""; text = "";
deps = [ "agenixChown"]; deps = ["agenixChown"];
}; };
}; };
} }

View file

@ -1,4 +1,3 @@
final: prev: final: prev: {
{ agenix = prev.callPackage ./pkgs/agenix.nix {};
agenix = prev.callPackage ./pkgs/agenix.nix { };
} }

View file

@ -13,177 +13,175 @@
if rage.version < "0.5.0" if rage.version < "0.5.0"
then callPackage ./rage.nix {} then callPackage ./rage.nix {}
else rage else rage
}/bin/rage" }/bin/rage",
} : }: let
let
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 in
lib.recursiveUpdate (writeShellScriptBin "agenix" '' lib.recursiveUpdate (writeShellScriptBin "agenix" ''
set -Eeuo pipefail set -Eeuo pipefail
PACKAGE="agenix" PACKAGE="agenix"
function show_help () { function show_help () {
echo "$PACKAGE - edit and rekey age secret files" echo "$PACKAGE - edit and rekey age secret files"
echo " " echo " "
echo "$PACKAGE -e FILE [-i PRIVATE_KEY]" echo "$PACKAGE -e FILE [-i PRIVATE_KEY]"
echo "$PACKAGE -r [-i PRIVATE_KEY]" echo "$PACKAGE -r [-i PRIVATE_KEY]"
echo ' ' echo ' '
echo 'options:' echo 'options:'
echo '-h, --help show help' echo '-h, --help show help'
echo '-e, --edit FILE edits FILE using $EDITOR' echo '-e, --edit FILE edits FILE using $EDITOR'
echo '-r, --rekey re-encrypts all secrets with specified recipients' echo '-r, --rekey re-encrypts all secrets with specified recipients'
echo '-i, --identity identity to use when decrypting' echo '-i, --identity identity to use when decrypting'
echo '-v, --verbose verbose output' echo '-v, --verbose verbose output'
echo ' ' echo ' '
echo 'FILE an age-encrypted file' echo 'FILE an age-encrypted file'
echo ' ' echo ' '
echo 'PRIVATE_KEY a path to a private SSH key used to decrypt file' echo 'PRIVATE_KEY a path to a private SSH key used to decrypt file'
echo ' ' echo ' '
echo 'EDITOR environment variable of editor to use when editing FILE' echo 'EDITOR environment variable of editor to use when editing FILE'
echo ' ' echo ' '
echo 'RULES environment variable with path to Nix file specifying recipient public keys.' echo 'RULES environment variable with path to Nix file specifying recipient public keys.'
echo "Defaults to './secrets.nix'" echo "Defaults to './secrets.nix'"
echo ' ' echo ' '
echo "agenix version: 0.13.0" echo "agenix version: 0.13.0"
echo "age binary path: ${ageBin}" echo "age binary path: ${ageBin}"
echo "age version: $(${ageBin} --version)" echo "age version: $(${ageBin} --version)"
} }
test $# -eq 0 && (show_help && exit 1) test $# -eq 0 && (show_help && exit 1)
REKEY=0 REKEY=0
DEFAULT_DECRYPT=(--decrypt) DEFAULT_DECRYPT=(--decrypt)
while test $# -gt 0; do while test $# -gt 0; do
case "$1" in case "$1" in
-h|--help) -h|--help)
show_help show_help
exit 0 exit 0
;; ;;
-e|--edit) -e|--edit)
shift shift
if test $# -gt 0; then if test $# -gt 0; then
export FILE=$1 export FILE=$1
else else
echo "no FILE specified" echo "no FILE specified"
exit 1 exit 1
fi fi
shift shift
;; ;;
-i|--identity) -i|--identity)
shift shift
if test $# -gt 0; then if test $# -gt 0; then
DEFAULT_DECRYPT+=(--identity "$1") DEFAULT_DECRYPT+=(--identity "$1")
else else
echo "no PRIVATE_KEY specified" echo "no PRIVATE_KEY specified"
exit 1 exit 1
fi fi
shift shift
;; ;;
-r|--rekey) -r|--rekey)
shift shift
REKEY=1 REKEY=1
;; ;;
-v|--verbose) -v|--verbose)
shift shift
set -x set -x
;; ;;
*) *)
show_help 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 exit 1
fi ;;
DECRYPT+=(-o "$CLEARTEXT_FILE" "$FILE") esac
${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 done
}
[ $REKEY -eq 1 ] && rekey && exit 0 RULES=''${RULES:-./secrets.nix}
edit "$FILE" && cleanup && exit 0
'')
{ function cleanup {
meta.description = "age-encrypted secrets for NixOS"; 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";
}

View file

@ -1,5 +1,10 @@
{ stdenv, rustPlatform, fetchFromGitHub, installShellFiles, darwin }: {
stdenv,
rustPlatform,
fetchFromGitHub,
installShellFiles,
darwin,
}:
rustPlatform.buildRustPackage rec { rustPlatform.buildRustPackage rec {
pname = "rage"; pname = "rage";
version = "0.5.0"; version = "0.5.0";
@ -13,12 +18,13 @@ rustPlatform.buildRustPackage rec {
cargoSha256 = "sha256-GPr5zxeODAjD+ynp/nned9gZUiReYcdzosuEbLIKZSs="; cargoSha256 = "sha256-GPr5zxeODAjD+ynp/nned9gZUiReYcdzosuEbLIKZSs=";
nativeBuildInputs = [ installShellFiles ]; nativeBuildInputs = [installShellFiles];
buildInputs = with darwin.apple_sdk.frameworks; stdenv.lib.optionals stdenv.isDarwin [ buildInputs = with darwin.apple_sdk.frameworks;
Security stdenv.lib.optionals stdenv.isDarwin [
Foundation Security
]; Foundation
];
# cargo test has an x86-only dependency # cargo test has an x86-only dependency
doCheck = stdenv.hostPlatform.isx86; doCheck = stdenv.hostPlatform.isx86;
@ -37,7 +43,7 @@ rustPlatform.buildRustPackage rec {
description = "A simple, secure and modern encryption tool with small explicit keys, no config options, and UNIX-style composability"; description = "A simple, secure and modern encryption tool with small explicit keys, no config options, and UNIX-style composability";
homepage = "https://github.com/str4d/rage"; homepage = "https://github.com/str4d/rage";
changelog = "https://github.com/str4d/rage/releases/tag/v${version}"; changelog = "https://github.com/str4d/rage/releases/tag/v${version}";
license = with licenses; [ asl20 mit ]; # either at your option license = with licenses; [asl20 mit]; # either at your option
maintainers = with maintainers; [ marsam ryantm ]; maintainers = with maintainers; [marsam ryantm];
}; };
} }

View file

@ -1,6 +1,6 @@
# Do not copy this! It is insecure. This is only okay because we are testing. # Do not copy this! It is insecure. This is only okay because we are testing.
{ {
system.activationScripts.agenixInstall.deps = [ "installSSHHostKeys" ]; system.activationScripts.agenixInstall.deps = ["installSSHHostKeys"];
system.activationScripts.installSSHHostKeys.text = '' system.activationScripts.installSSHHostKeys.text = ''
mkdir -p /etc/ssh mkdir -p /etc/ssh

View file

@ -1,14 +1,20 @@
{ {
nixpkgs ? <nixpkgs>, nixpkgs ? <nixpkgs>,
pkgs ? import <nixpkgs> { inherit system; config = {}; }, pkgs ?
system ? builtins.currentSystem import <nixpkgs> {
} @args: inherit system;
config = {};
import "${nixpkgs}/nixos/tests/make-test-python.nix" ({ pkgs, ...}: { },
system ? builtins.currentSystem,
} @ args:
import "${nixpkgs}/nixos/tests/make-test-python.nix" ({pkgs, ...}: {
name = "agenix-integration"; name = "agenix-integration";
nodes.system1 = { config, lib, ... }: { nodes.system1 = {
config,
lib,
...
}: {
imports = [ imports = [
../modules/age.nix ../modules/age.nix
./install_ssh_host_keys.nix ./install_ssh_host_keys.nix
@ -30,11 +36,9 @@ import "${nixpkgs}/nixos/tests/make-test-python.nix" ({ pkgs, ...}: {
}; };
}; };
}; };
}; };
testScript = testScript = let
let
user = "user1"; user = "user1";
password = "password1234"; password = "password1234";
in '' in ''
@ -55,4 +59,5 @@ import "${nixpkgs}/nixos/tests/make-test-python.nix" ({ pkgs, ...}: {
system1.wait_for_file("/tmp/1") system1.wait_for_file("/tmp/1")
assert "${user}" in system1.succeed("cat /tmp/1") assert "${user}" in system1.succeed("cat /tmp/1")
''; '';
}) args })
args