From b0721be0c6745e773738d49bf87e38afb3a075fd Mon Sep 17 00:00:00 2001 From: Ryan Mulligan Date: Sun, 19 Feb 2023 15:12:18 -0800 Subject: [PATCH 01/73] doc: how to skip the Darwin input --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 3381e7e..692b4cf 100644 --- a/README.md +++ b/README.md @@ -172,6 +172,8 @@ To install the `agenix` binary: inputs.agenix.url = "github:ryantm/agenix"; # optional, not necessary for the module #inputs.agenix.inputs.nixpkgs.follows = "nixpkgs"; + # optionally choose not to download darwin deps (saves some resources on Linux) + #inputs.agenix.inputs.darwin.follows = ""; outputs = { self, nixpkgs, agenix }: { # change `yourhostname` to your actual hostname From 8db34521db3f2ca6b9a9b824138c12c4a67aca5b Mon Sep 17 00:00:00 2001 From: Ryan Mulligan Date: Mon, 20 Feb 2023 12:02:58 -0800 Subject: [PATCH 02/73] doc: update agenix help text in docs --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 3381e7e..6413f87 100644 --- a/README.md +++ b/README.md @@ -499,6 +499,8 @@ PRIVATE_KEY a path to a private SSH key used to decrypt file EDITOR environment variable of editor to use when editing FILE +If STDIN is not interactive, EDITOR will be set to "cp /dev/stdin" + RULES environment variable with path to Nix file specifying recipient public keys. Defaults to './secrets.nix' ``` From 63e9da1737d97ec236d86dd7e7339bc42f42ecac Mon Sep 17 00:00:00 2001 From: Ryan Mulligan Date: Mon, 20 Feb 2023 12:03:14 -0800 Subject: [PATCH 03/73] test: add tests for one-way encrypted secrets --- test/fixtures/one-way/secrets.nix | 5 +++++ test/install_ssh_host_keys.nix | 4 ++++ test/integration.nix | 13 +++++++++++-- 3 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 test/fixtures/one-way/secrets.nix diff --git a/test/fixtures/one-way/secrets.nix b/test/fixtures/one-way/secrets.nix new file mode 100644 index 0000000..4e81ad5 --- /dev/null +++ b/test/fixtures/one-way/secrets.nix @@ -0,0 +1,5 @@ +let + system1 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPJDyIr/FSz1cJdcoW69R+NrWzwGK/+3gJpqD1t8L2zE"; +in { + "one-way.age".publicKeys = [system1]; +} diff --git a/test/install_ssh_host_keys.nix b/test/install_ssh_host_keys.nix index 72d82ee..fe6ad37 100644 --- a/test/install_ssh_host_keys.nix +++ b/test/install_ssh_host_keys.nix @@ -24,5 +24,9 @@ cp -r "${../example}" /tmp/secrets chmod -R u+rw /tmp/secrets chown -R $USER1_UID:$USERS_GID /tmp/secrets + + cp -r "${./fixtures/one-way}" /tmp/secrets-one-way + chmod -R u+rw /tmp/secrets-one-way + chown -R $USER1_UID:$USERS_GID /tmp/secrets-one-way ''; } diff --git a/test/integration.nix b/test/integration.nix index ff1bbac..0dbe5d0 100644 --- a/test/integration.nix +++ b/test/integration.nix @@ -66,7 +66,8 @@ pkgs.nixosTest { system1.wait_for_file("/tmp/1") assert "${user}" in system1.succeed("cat /tmp/1") - userDo = lambda input : f"sudo -u user1 -- bash -c 'set -eou pipefail; cd /tmp/secrets; {input}'" + userDir = "/tmp/secrets" + userDo = lambda input : f"sudo -u user1 -- bash -c 'set -eou pipefail; cd {userDir}; {input}'" before_hash = system1.succeed(userDo('sha256sum passwordfile-user1.age')).split() print(system1.succeed(userDo('agenix -r -i /home/user1/.ssh/id_ed25519'))) @@ -89,7 +90,15 @@ pkgs.nixosTest { system1.succeed(userDo("rm ~/.ssh/id_rsa")) # user1 can edit a secret by piping in contents - system1.succeed(userDo("echo 'secret1234' | agenix -e passwordfile-user1.age")) + system1.succeed(userDo("echo secret1234 | agenix -e passwordfile-user1.age")) assert "secret1234" in system1.succeed(userDo("EDITOR=cat agenix -e passwordfile-user1.age")) + + # user1 can make a one-way secret, but cannot see the contents, and host can decrypt + userDir = "/tmp/secrets-one-way" + system1.succeed(userDo("echo eye1234 | agenix -e one-way.age")) + system1.fail(userDo("EDITOR=cat agenix -e one-way.age")) + assert "eye1234" in system1.succeed(f"cd {userDir};EDITOR=cat agenix -e one-way.age -i /etc/ssh/ssh_host_ed25519_key") + system1.succeed(userDo("echo nose1234 | agenix -e one-way.age")) + assert "nose1234" in system1.succeed(f"cd {userDir};EDITOR=cat agenix -e one-way.age -i /etc/ssh/ssh_host_ed25519_key") ''; } From b720b35bd72b339b7766bcdec8e3912bfe1c9ef9 Mon Sep 17 00:00:00 2001 From: Ryan Mulligan Date: Mon, 20 Feb 2023 12:04:09 -0800 Subject: [PATCH 04/73] feature: enable one-way encrypted secrets fixes #9 --- pkgs/agenix.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/agenix.sh b/pkgs/agenix.sh index c1e89c3..ab959ff 100644 --- a/pkgs/agenix.sh +++ b/pkgs/agenix.sh @@ -114,7 +114,7 @@ function edit { CLEARTEXT_DIR=$(@mktempBin@ -d) CLEARTEXT_FILE="$CLEARTEXT_DIR/$(basename "$FILE")" - if [ -f "$FILE" ] + if [ -f "$FILE" ] && [ -t 0 ] then DECRYPT=("${DEFAULT_DECRYPT[@]}") if [[ "${DECRYPT[*]}" != *"--identity"* ]]; then @@ -142,7 +142,7 @@ function edit { 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 + [ -f "$FILE" ] && [ "$EDITOR" != ":" ] && [ -f "$CLEARTEXT_FILE.before" ] && @diffBin@ -q "$CLEARTEXT_FILE.before" "$CLEARTEXT_FILE" && warn "$FILE wasn't changed, skipping re-encryption." && return ENCRYPT=() while IFS= read -r key From 2c0ae7d44f0761100b03b02004cf141266094f22 Mon Sep 17 00:00:00 2001 From: Ryan Mulligan Date: Tue, 21 Feb 2023 20:33:19 -0800 Subject: [PATCH 05/73] contrib: stop packaging rage We don't need to package rage anymore, since all the latest maintained versions of Nix have versions higher than what we need. --- modules/age.nix | 7 +------ pkgs/rage.nix | 49 ------------------------------------------------- 2 files changed, 1 insertion(+), 55 deletions(-) delete mode 100644 pkgs/rage.nix diff --git a/modules/age.nix b/modules/age.nix index d38e8de..8c99214 100644 --- a/modules/age.nix +++ b/modules/age.nix @@ -10,11 +10,6 @@ with lib; let isDarwin = lib.attrsets.hasAttrByPath ["environment" "darwinConfig"] options; - # we need at least rage 0.5.0 to support ssh keys - rage = - if lib.versionOlder pkgs.rage.version "0.5.0" - then pkgs.callPackage ../pkgs/rage.nix {} - else pkgs.rage; ageBin = config.age.ageBin; users = config.users.users; @@ -187,7 +182,7 @@ in { options.age = { ageBin = mkOption { type = types.str; - default = "${rage}/bin/rage"; + default = "${pkgs.rage}/bin/rage"; description = '' The age executable to use. ''; diff --git a/pkgs/rage.nix b/pkgs/rage.nix deleted file mode 100644 index 4db11e9..0000000 --- a/pkgs/rage.nix +++ /dev/null @@ -1,49 +0,0 @@ -{ - stdenv, - rustPlatform, - fetchFromGitHub, - installShellFiles, - darwin, -}: -rustPlatform.buildRustPackage rec { - pname = "rage"; - version = "0.5.0"; - - src = fetchFromGitHub { - owner = "str4d"; - repo = pname; - rev = "v${version}"; - sha256 = "sha256-XSDfAsXfwSoe5JMdJtZlC324Sra+4fVJhE3/k2TthEc="; - }; - - cargoSha256 = "sha256-GPr5zxeODAjD+ynp/nned9gZUiReYcdzosuEbLIKZSs="; - - nativeBuildInputs = [installShellFiles]; - - buildInputs = with darwin.apple_sdk.frameworks; - stdenv.lib.optionals stdenv.isDarwin [ - Security - Foundation - ]; - - # cargo test has an x86-only dependency - doCheck = stdenv.hostPlatform.isx86; - - postBuild = '' - cargo run --example generate-docs - cargo run --example generate-completions - ''; - - postInstall = '' - installManPage target/manpages/* - installShellCompletion target/completions/*.{bash,fish,zsh} - ''; - - meta = with stdenv.lib; { - 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"; - changelog = "https://github.com/str4d/rage/releases/tag/v${version}"; - license = with licenses; [asl20 mit]; # either at your option - maintainers = with maintainers; [marsam ryantm]; - }; -} From 9cf19670393e4f67f20904683da92bef4996932b Mon Sep 17 00:00:00 2001 From: muvlon Date: Tue, 21 Feb 2023 02:15:37 +0100 Subject: [PATCH 06/73] feature: add -d/--decrypt option to decrypt a file to stdout --- README.md | 5 +++++ pkgs/agenix.sh | 38 ++++++++++++++++++++++++++++++++++---- test/integration.nix | 7 ++++++- 3 files changed, 45 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 3381e7e..976b304 100644 --- a/README.md +++ b/README.md @@ -483,6 +483,8 @@ Overriding `age.secretsMountPoint` example: ### agenix CLI reference ``` +agenix - edit and rekey age secret files + agenix -e FILE [-i PRIVATE_KEY] agenix -r [-i PRIVATE_KEY] @@ -490,6 +492,7 @@ options: -h, --help show help -e, --edit FILE edits FILE using $EDITOR -r, --rekey re-encrypts all secrets with specified recipients +-d, --decrypt FILE decrypts FILE to STDOUT -i, --identity identity to use when decrypting -v, --verbose verbose output @@ -499,6 +502,8 @@ PRIVATE_KEY a path to a private SSH key used to decrypt file EDITOR environment variable of editor to use when editing FILE +If STDIN is not interactive, EDITOR will be set to "cp /dev/stdin" + RULES environment variable with path to Nix file specifying recipient public keys. Defaults to './secrets.nix' ``` diff --git a/pkgs/agenix.sh b/pkgs/agenix.sh index c1e89c3..5839a7f 100644 --- a/pkgs/agenix.sh +++ b/pkgs/agenix.sh @@ -14,6 +14,7 @@ function 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 ' ' @@ -45,6 +46,7 @@ function err() { test $# -eq 0 && (show_help && exit 1) REKEY=0 +DECRYPT_ONLY=0 DEFAULT_DECRYPT=(--decrypt) while test $# -gt 0; do @@ -77,6 +79,17 @@ while test $# -gt 0; do 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 @@ -89,7 +102,6 @@ while test $# -gt 0; do done RULES=${RULES:-./secrets.nix} - function cleanup { if [ -n "${CLEARTEXT_DIR+x}" ] then @@ -102,10 +114,13 @@ function cleanup { } 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) +function keys { + (@nixInstantiate@ --eval -E "(let rules = import $RULES; in builtins.concatStringsSep \"\n\" rules.\"$1\".publicKeys)" | @sedBin@ 's/"//g' | @sedBin@ 's/\\n/\n/g') | @sedBin@ '/^$/d' || exit 1 +} +function decrypt { + FILE=$1 + KEYS=$2 if [ -z "$KEYS" ] then err "There is no rule for $FILE in $RULES." @@ -132,6 +147,12 @@ function edit { @ageBin@ "${DECRYPT[@]}" || exit 1 cp "$CLEARTEXT_FILE" "$CLEARTEXT_FILE.before" fi +} + +function edit { + FILE=$1 + KEYS=$(keys "$FILE") || exit 1 + decrypt "$FILE" "$KEYS" || exit 1 [ -t 0 ] || EDITOR='cp /dev/stdin' @@ -160,6 +181,14 @@ function edit { mv -f "$REENCRYPTED_FILE" "$1" } +function decrypt_only { + FILE=$1 + KEYS=$(keys "$FILE") || exit 1 + decrypt "$FILE" "$KEYS" + printf "%s" "$(<"${CLEARTEXT_FILE}")" + cleanup +} + 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) @@ -172,4 +201,5 @@ function rekey { } [ $REKEY -eq 1 ] && rekey && exit 0 +[ $DECRYPT_ONLY -eq 1 ] && decrypt_only "${FILE}" && exit 0 edit "$FILE" && cleanup && exit 0 diff --git a/test/integration.nix b/test/integration.nix index ff1bbac..64f5c50 100644 --- a/test/integration.nix +++ b/test/integration.nix @@ -90,6 +90,11 @@ pkgs.nixosTest { # user1 can edit a secret by piping in contents system1.succeed(userDo("echo 'secret1234' | agenix -e passwordfile-user1.age")) - assert "secret1234" in system1.succeed(userDo("EDITOR=cat agenix -e passwordfile-user1.age")) + + # and get it back out via --decrypt + assert "secret1234" in system1.succeed(userDo("agenix -d passwordfile-user1.age")) + + # finally, the plain text should not linger around anywhere in the filesystem. + system1.fail("grep -r secret1234 /tmp") ''; } From 7dae15b7bc858ebc7cbba51bd3aa8971001d5880 Mon Sep 17 00:00:00 2001 From: Wanja Hentze Date: Fri, 24 Feb 2023 09:00:48 +0100 Subject: [PATCH 07/73] fix -d/--decrypt-only not working correctly for binary data I had first used `printf` for outputting the data, but that breaks if the secret itself contains null bytes. One could fix this by using e.g. `cat`, but looking a bit more at the code I realized that in the -d case we never need to `mktemp` at all and can just ask `age` to write directly to stdout by not setting -o. --- pkgs/agenix.sh | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/pkgs/agenix.sh b/pkgs/agenix.sh index 5839a7f..970ca30 100644 --- a/pkgs/agenix.sh +++ b/pkgs/agenix.sh @@ -126,9 +126,6 @@ function decrypt { err "There is no rule for $FILE in $RULES." fi - CLEARTEXT_DIR=$(@mktempBin@ -d) - CLEARTEXT_FILE="$CLEARTEXT_DIR/$(basename "$FILE")" - if [ -f "$FILE" ] then DECRYPT=("${DEFAULT_DECRYPT[@]}") @@ -143,17 +140,23 @@ function decrypt { 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 - DECRYPT+=(-o "$CLEARTEXT_FILE" "$FILE") - @ageBin@ "${DECRYPT[@]}" || exit 1 - cp "$CLEARTEXT_FILE" "$CLEARTEXT_FILE.before" + + @ageBin@ "${DECRYPT[@]}" "$FILE" || exit 1 fi } function edit { FILE=$1 KEYS=$(keys "$FILE") || exit 1 + + CLEARTEXT_DIR=$(@mktempBin@ -d) + CLEARTEXT_FILE="$CLEARTEXT_DIR/$(basename "$FILE")" + DEFAULT_DECRYPT+=(-o "$CLEARTEXT_FILE") + decrypt "$FILE" "$KEYS" || exit 1 + cp "$CLEARTEXT_FILE" "$CLEARTEXT_FILE.before" + [ -t 0 ] || EDITOR='cp /dev/stdin' $EDITOR "$CLEARTEXT_FILE" @@ -181,14 +184,6 @@ function edit { mv -f "$REENCRYPTED_FILE" "$1" } -function decrypt_only { - FILE=$1 - KEYS=$(keys "$FILE") || exit 1 - decrypt "$FILE" "$KEYS" - printf "%s" "$(<"${CLEARTEXT_FILE}")" - cleanup -} - 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) @@ -201,5 +196,5 @@ function rekey { } [ $REKEY -eq 1 ] && rekey && exit 0 -[ $DECRYPT_ONLY -eq 1 ] && decrypt_only "${FILE}" && exit 0 +[ $DECRYPT_ONLY -eq 1 ] && decrypt "${FILE}" "$(keys "$FILE")" && exit 0 edit "$FILE" && cleanup && exit 0 From 37dcc5f5e7803f74386e2260d108c351979f3558 Mon Sep 17 00:00:00 2001 From: Nathan Henrie Date: Fri, 24 Feb 2023 11:17:12 -0700 Subject: [PATCH 08/73] Expand explanation that identityPaths should be strings --- README.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 976b304..34e51b4 100644 --- a/README.md +++ b/README.md @@ -437,13 +437,14 @@ Overriding `age.ageBin` example: #### `age.identityPaths` -`age.identityPaths` is a list of paths to recipient keys to try to use -to decrypt the secrets. All of the file paths must be present, but -only one needs to be able to decrypt the secret. Usually, you don't -need to change this. By default, this is the `rsa` and `ed25519` keys -in `config.services.openssh.hostKeys`. - -Overriding `age.identityPaths` example: +`age.identityPaths` is a list of paths to recipient keys to try to use to +decrypt the secrets. By default, it is the `rsa` and `ed25519` keys in +`config.services.openssh.hostKeys`, and on NixOS you usually don't need to +change this. The list items should be strings (`"/path/to/id_rsa"`), not +nix paths (`../path/to/id_rsa`), as the latter would copy your private key to +the nix store, which is the exact situation `agenix` is designed to avoid. At +least one of the file paths must be present at runtime and able to decrypt the +secret in question. Overriding `age.identityPaths` example: ```nix { From 1141c36c2642438e5b9a05bb1a2859ca6afccfc4 Mon Sep 17 00:00:00 2001 From: Ryan Mulligan Date: Sun, 26 Feb 2023 08:37:02 -0800 Subject: [PATCH 09/73] fix: disallow Nix store paths in age.identityPaths option --- modules/age.nix | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/modules/age.nix b/modules/age.nix index 8c99214..2a1bb29 100644 --- a/modules/age.nix +++ b/modules/age.nix @@ -174,6 +174,15 @@ with lib; let symlink = mkEnableOption "symlinking secrets to their destination" // {default = true;}; }; }); + + identity = with types; + mkOptionType { + name = "identity"; + description = "Path to the identity for age decryption. Usually a path to an SSH key. Must not be a store path, because we do not want private keys to end up in the nix store."; + descriptionClass = "noun"; + check = x: isStringLike x && !isStorePath x; + merge = mergeEqualOption; + }; in { imports = [ (mkRenamedOptionModule ["age" "sshKeyPaths"] ["age" "identityPaths"]) @@ -216,7 +225,7 @@ in { ''; }; identityPaths = mkOption { - type = types.listOf types.path; + type = types.listOf identity; default = if (config.services.openssh.enable or false) then map (e: e.path) (lib.filter (e: e.type == "rsa" || e.type == "ed25519") config.services.openssh.hostKeys) @@ -226,9 +235,7 @@ in { "/etc/ssh/ssh_host_rsa_key" ] else []; - description = '' - Path to SSH keys to be used as identities in age decryption. - ''; + description = "List of identities: ${identity.description}"; }; }; From b67873854d7be994105add2bd66bfcaabf01f83a Mon Sep 17 00:00:00 2001 From: Ryan Mulligan Date: Sun, 26 Feb 2023 15:11:56 -0800 Subject: [PATCH 10/73] Revert "fix: disallow Nix store paths in age.identityPaths option" --- modules/age.nix | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/modules/age.nix b/modules/age.nix index 2a1bb29..8c99214 100644 --- a/modules/age.nix +++ b/modules/age.nix @@ -174,15 +174,6 @@ with lib; let symlink = mkEnableOption "symlinking secrets to their destination" // {default = true;}; }; }); - - identity = with types; - mkOptionType { - name = "identity"; - description = "Path to the identity for age decryption. Usually a path to an SSH key. Must not be a store path, because we do not want private keys to end up in the nix store."; - descriptionClass = "noun"; - check = x: isStringLike x && !isStorePath x; - merge = mergeEqualOption; - }; in { imports = [ (mkRenamedOptionModule ["age" "sshKeyPaths"] ["age" "identityPaths"]) @@ -225,7 +216,7 @@ in { ''; }; identityPaths = mkOption { - type = types.listOf identity; + type = types.listOf types.path; default = if (config.services.openssh.enable or false) then map (e: e.path) (lib.filter (e: e.type == "rsa" || e.type == "ed25519") config.services.openssh.hostKeys) @@ -235,7 +226,9 @@ in { "/etc/ssh/ssh_host_rsa_key" ] else []; - description = "List of identities: ${identity.description}"; + description = '' + Path to SSH keys to be used as identities in age decryption. + ''; }; }; From 657789137cc504ba6a6bed7b72254f2626550967 Mon Sep 17 00:00:00 2001 From: Ryan Mulligan Date: Sun, 26 Feb 2023 14:17:37 -0800 Subject: [PATCH 11/73] doc: add new doc website * use mmdoc * add github pages action to auto publish * do not edit README for now, will follow up with a commit directs people to the doc site --- .github/workflows/ci.yaml | 2 + .github/workflows/doc.yml | 36 +++++ doc/acknowledgements.md | 3 + doc/community-and-support.md | 4 + doc/contributing.md | 28 ++++ doc/features.md | 8 + doc/install-via-fetchtarball.md | 38 +++++ doc/install-via-flakes.md | 39 +++++ doc/install-via-niv.md | 27 ++++ doc/install-via-nix-channel.md | 28 ++++ doc/introduction.md | 3 + doc/notices.md | 3 + doc/overriding-age-binary.md | 12 ++ doc/problem-and-solution.md | 5 + doc/reference.md | 250 ++++++++++++++++++++++++++++++++ doc/rekeying.md | 13 ++ doc/threat-model-warnings.md | 14 ++ doc/toc.md | 18 +++ doc/tutorial.md | 51 +++++++ flake.lock | 6 +- flake.nix | 6 + pkgs/doc.nix | 11 ++ 22 files changed, 602 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/doc.yml create mode 100644 doc/acknowledgements.md create mode 100644 doc/community-and-support.md create mode 100644 doc/contributing.md create mode 100644 doc/features.md create mode 100644 doc/install-via-fetchtarball.md create mode 100644 doc/install-via-flakes.md create mode 100644 doc/install-via-niv.md create mode 100644 doc/install-via-nix-channel.md create mode 100644 doc/introduction.md create mode 100644 doc/notices.md create mode 100644 doc/overriding-age-binary.md create mode 100644 doc/problem-and-solution.md create mode 100644 doc/reference.md create mode 100644 doc/rekeying.md create mode 100644 doc/threat-model-warnings.md create mode 100644 doc/toc.md create mode 100644 doc/tutorial.md create mode 100644 pkgs/doc.nix diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index a54ed78..7a765e3 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -11,6 +11,7 @@ jobs: with: extra_nix_config: "system-features = nixos-test benchmark big-parallel kvm" - run: nix build + - run: nix build .#doc - run: nix fmt . -- --check - run: nix flake check tests-darwin: @@ -21,6 +22,7 @@ jobs: with: extra_nix_config: "system-features = nixos-test benchmark big-parallel kvm" - run: nix build + - run: nix build .#doc - run: nix fmt . -- --check - run: nix flake check - run: | diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml new file mode 100644 index 0000000..c8569c3 --- /dev/null +++ b/.github/workflows/doc.yml @@ -0,0 +1,36 @@ +name: doc + +on: + push: + branches: + - main + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: "pages" + cancel-in-progress: true + +jobs: + publish: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: cachix/install-nix-action@v20 + - run: nix build .#doc + - name: Setup Pages + uses: actions/configure-pages@v3 + - name: Upload artifact + uses: actions/upload-pages-artifact@v1 + with: + path: 'result/multi' + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v1 diff --git a/doc/acknowledgements.md b/doc/acknowledgements.md new file mode 100644 index 0000000..cd36955 --- /dev/null +++ b/doc/acknowledgements.md @@ -0,0 +1,3 @@ +# Acknowledgements {#acknowledgements} + +This project is based off of [sops-nix](https://github.com/Mic92/sops-nix) created Mic92. Thank you to Mic92 for inspiration and advice. diff --git a/doc/community-and-support.md b/doc/community-and-support.md new file mode 100644 index 0000000..831b046 --- /dev/null +++ b/doc/community-and-support.md @@ -0,0 +1,4 @@ +# Community and Support {#community-and-support} + +Support and development discussion is available here on GitHub and +also through [Matrix](https://matrix.to/#/#agenix:nixos.org). diff --git a/doc/contributing.md b/doc/contributing.md new file mode 100644 index 0000000..4954726 --- /dev/null +++ b/doc/contributing.md @@ -0,0 +1,28 @@ +# Contributing {#contributing} + +* The main branch is protected against direct pushes +* All changes must go through GitHub PR review and get at least one approval +* PR titles and commit messages should be prefixed with at least one of these categories: + * contrib - things that make the project development better + * doc - documentation + * feature - new features + * fix - bug fixes +* Please update or make integration tests for new features +* Use `nix fmt` to format nix code + + +## Tests + +You can run the tests with + +```ShellSession +nix flake check +``` + +You can run the integration tests in interactive mode like this: + +```ShellSession +nix run .#checks.x86_64-linux.integration.driverInteractive +``` + +After it starts, enter `run_tests()` to run the tests. diff --git a/doc/features.md b/doc/features.md new file mode 100644 index 0000000..fc74bc3 --- /dev/null +++ b/doc/features.md @@ -0,0 +1,8 @@ +# Features {#features} + +* Secrets are encrypted with SSH keys + * system public keys via `ssh-keyscan` + * can use public keys available on GitHub for users (for example, https://github.com/ryantm.keys) +* No GPG +* Very little code, so it should be easy for you to audit +* Encrypted secrets are stored in the Nix store, so a separate distribution mechanism is not necessary diff --git a/doc/install-via-fetchtarball.md b/doc/install-via-fetchtarball.md new file mode 100644 index 0000000..d6f5b08 --- /dev/null +++ b/doc/install-via-fetchtarball.md @@ -0,0 +1,38 @@ +# Install via fetchTarball {#install-via-fetchtarball} + +#### Install module via fetchTarball + +Add the following to your configuration.nix: + +```nix +{ + imports = [ "${builtins.fetchTarball "https://github.com/ryantm/agenix/archive/main.tar.gz"}/modules/age.nix" ]; +} +``` + + or with pinning: + +```nix +{ + imports = let + # replace this with an actual commit id or tag + commit = "298b235f664f925b433614dc33380f0662adfc3f"; + in [ + "${builtins.fetchTarball { + url = "https://github.com/ryantm/agenix/archive/${commit}.tar.gz"; + # update hash from nix build output + sha256 = ""; + }}/modules/age.nix" + ]; +} +``` + +#### Install CLI via fetchTarball + +To install the `agenix` binary: + +```nix +{ + environment.systemPackages = [ (pkgs.callPackage "${builtins.fetchTarball "https://github.com/ryantm/agenix/archive/main.tar.gz"}/pkgs/agenix.nix" {}) ]; +} +``` diff --git a/doc/install-via-flakes.md b/doc/install-via-flakes.md new file mode 100644 index 0000000..efb7e4b --- /dev/null +++ b/doc/install-via-flakes.md @@ -0,0 +1,39 @@ +# Install via Flakes {#install-via-flakes} + +## Install module via Flakes + +```nix +{ + inputs.agenix.url = "github:ryantm/agenix"; + # optional, not necessary for the module + #inputs.agenix.inputs.nixpkgs.follows = "nixpkgs"; + + outputs = { self, nixpkgs, agenix }: { + # change `yourhostname` to your actual hostname + nixosConfigurations.yourhostname = nixpkgs.lib.nixosSystem { + # change to your system: + system = "x86_64-linux"; + modules = [ + ./configuration.nix + agenix.nixosModules.default + ]; + }; + }; +} +``` + +## Install CLI via Flakes + +You don't need to install it, + +```ShellSession +nix run github:ryantm/agenix -- --help +``` + +but, if you want to (change the system based on your system): + +```nix +{ + environment.systemPackages = [ agenix.packages.x86_64-linux.default ]; +} +``` diff --git a/doc/install-via-niv.md b/doc/install-via-niv.md new file mode 100644 index 0000000..7aba6f4 --- /dev/null +++ b/doc/install-via-niv.md @@ -0,0 +1,27 @@ +# Install via [niv](https://github.com/nmattia/niv) {#install-via-niv} + +First add it to niv: + +```ShellSession +$ niv add ryantm/agenix +``` + +## Install module via niv + +Then add the following to your `configuration.nix` in the `imports` list: + +```nix +{ + imports = [ "${(import ./nix/sources.nix).agenix}/modules/age.nix" ]; +} +``` + +## Install CLI via niv + +To install the `agenix` binary: + +```nix +{ + environment.systemPackages = [ (pkgs.callPackage "${(import ./nix/sources.nix).agenix}/pkgs/agenix.nix" {}) ]; +} +``` diff --git a/doc/install-via-nix-channel.md b/doc/install-via-nix-channel.md new file mode 100644 index 0000000..75c8d86 --- /dev/null +++ b/doc/install-via-nix-channel.md @@ -0,0 +1,28 @@ +# Install via nix-channel {#install-via-nix-channel} + +As root run: + +```ShellSession +$ sudo nix-channel --add https://github.com/ryantm/agenix/archive/main.tar.gz agenix +$ sudo nix-channel --update +``` + +## Install module via nix-channel + +Then add the following to your `configuration.nix` in the `imports` list: + +```nix +{ + imports = [ ]; +} +``` + +## Install CLI via nix-channel + +To install the `agenix` binary: + +```nix +{ + environment.systemPackages = [ (pkgs.callPackage {}) ]; +} +``` diff --git a/doc/introduction.md b/doc/introduction.md new file mode 100644 index 0000000..58ff3a5 --- /dev/null +++ b/doc/introduction.md @@ -0,0 +1,3 @@ +# agenix - [age](https://github.com/FiloSottile/age)-encrypted secrets for NixOS {#introduction} + +`agenix` is a commandline tool for managing secrets encrypted with your existing SSH keys. This project also includes the NixOS module `age` for adding encrypted secrets into the Nix store and decrypting them. diff --git a/doc/notices.md b/doc/notices.md new file mode 100644 index 0000000..5dcc5a9 --- /dev/null +++ b/doc/notices.md @@ -0,0 +1,3 @@ +# Notices {#notices} + +* Password-protected ssh keys: since the underlying tool age/rage do not support ssh-agent, password-protected ssh keys do not work well. For example, if you need to rekey 20 secrets you will have to enter your password 20 times. diff --git a/doc/overriding-age-binary.md b/doc/overriding-age-binary.md new file mode 100644 index 0000000..9ee3a11 --- /dev/null +++ b/doc/overriding-age-binary.md @@ -0,0 +1,12 @@ +# Overriding age binary {#overriding-age-binary} + +The agenix CLI uses `rage` by default as its age implemenation, you +can use the reference implementation `age` with Flakes like this: + +```nix +{pkgs,agenix,...}:{ + environment.systemPackages = [ + (agenix.packages.x86_64-linux.default.override { ageBin = "${pkgs.age}/bin/age"; }) + ]; +} +``` diff --git a/doc/problem-and-solution.md b/doc/problem-and-solution.md new file mode 100644 index 0000000..fb2b758 --- /dev/null +++ b/doc/problem-and-solution.md @@ -0,0 +1,5 @@ +# Problem and solution {#problem-and-solution} + +All files in the Nix store are readable by any system user, so it is not a suitable place for including cleartext secrets. Many existing tools (like NixOps deployment.keys) deploy secrets separately from `nixos-rebuild`, making deployment, caching, and auditing more difficult. Out-of-band secret management is also less reproducible. + +`agenix` solves these issues by using your pre-existing SSH key infrastructure and `age` to encrypt secrets into the Nix store. Secrets are decrypted using an SSH host private key during NixOS system activation. diff --git a/doc/reference.md b/doc/reference.md new file mode 100644 index 0000000..614b0c9 --- /dev/null +++ b/doc/reference.md @@ -0,0 +1,250 @@ +# Reference {#reference} + +## `age` module reference {#age-module-reference} + +### `age.secrets` + +`age.secrets` attrset of secrets. You always need to use this +configuration option. Defaults to `{}`. + +### `age.secrets..file` + +`age.secrets..file` is the path to the encrypted `.age` for this +secret. This is the only required secret option. + +Example: + +```nix +{ + age.secrets.monitrc.file = ../secrets/monitrc.age; +} +``` + +### `age.secrets..path` + +`age.secrets..path` is the path where the secret is decrypted +to. Defaults to `/run/agenix/` (`config.age.secretsDir/`). + +Example defining a different path: + +```nix +{ + age.secrets.monitrc = { + file = ../secrets/monitrc.age; + path = "/etc/monitrc"; + }; +} +``` + +For many services, you do not need to set this. Instead, refer to the +decryption path in your configuration with +`config.age.secrets..path`. + +Example referring to path: + +```nix +{ + users.users.ryantm = { + isNormalUser = true; + passwordFile = config.age.secrets.passwordfile-ryantm.path; + }; +} +``` + +#### builtins.readFile anti-pattern + +```nix +{ + # Do not do this! + config.password = builtins.readFile config.age.secrets.secret1.path; +} +``` + +This can cause the cleartext to be placed into the world-readable Nix +store. Instead, have your services read the cleartext path at runtime. + +### `age.secrets..mode` + +`age.secrets..mode` is permissions mode of the decrypted secret +in a format understood by chmod. Usually, you only need to use this in +combination with `age.secrets..owner` and +`age.secrets..group` + +Example: + +```nix +{ + age.secrets.nginx-htpasswd = { + file = ../secrets/nginx.htpasswd.age; + mode = "770"; + owner = "nginx"; + group = "nginx"; + }; +} +``` + +### `age.secrets..owner` + +`age.secrets..owner` is the username of the decrypted file's +owner. Usually, you only need to use this in combination with +`age.secrets..mode` and `age.secrets..group` + +Example: + +```nix +{ + age.secrets.nginx-htpasswd = { + file = ../secrets/nginx.htpasswd.age; + mode = "770"; + owner = "nginx"; + group = "nginx"; + }; +} +``` + +### `age.secrets..group` + +`age.secrets..group` is the name of the decrypted file's +group. Usually, you only need to use this in combination with +`age.secrets..owner` and `age.secrets..mode` + +Example: + +```nix +{ + age.secrets.nginx-htpasswd = { + file = ../secrets/nginx.htpasswd.age; + mode = "770"; + owner = "nginx"; + group = "nginx"; + }; +} +``` + +### `age.secrets..symlink` + +`age.secrets..symlink` is a boolean. If true (the default), +secrets are symlinked to `age.secrets..path`. If false, secerts +are copied to `age.secrets..path`. Usually, you want to keep +this as true, because it secure cleanup of secrets no longer +used. (The symlink will still be there, but it will be broken.) If +false, you are responsible for cleaning up your own secrets after you +stop using them. + +Some programs do not like following symlinks (for example Java +programs like Elasticsearch). + +Example: + +```nix +{ + age.secrets."elasticsearch.conf" = { + file = ../secrets/elasticsearch.conf.age; + symlink = false; + }; +} +``` + +### `age.secrets..name` + +`age.secrets..name` is the string of the name of the file after +it is decrypted. Defaults to the `` in the attrpath, but can be +set separately if you want the file name to be different from the +attribute name part. + +Example of a secret with a name different from its attrpath: + +```nix +{ + age.secrets.monit = { + name = "monitrc"; + file = ../secrets/monitrc.age; + }; +} +``` + +### `age.ageBin` + +`age.ageBin` the string of the path to the `age` binary. Usually, you +don't need to change this. Defaults to `rage/bin/rage`. + +Overriding `age.ageBin` example: + +```nix +{pkgs, ...}:{ + age.ageBin = "${pkgs.age}/bin/age"; +} +``` + +### `age.identityPaths` + +`age.identityPaths` is a list of paths to recipient keys to try to use to +decrypt the secrets. By default, it is the `rsa` and `ed25519` keys in +`config.services.openssh.hostKeys`, and on NixOS you usually don't need to +change this. The list items should be strings (`"/path/to/id_rsa"`), not +nix paths (`../path/to/id_rsa`), as the latter would copy your private key to +the nix store, which is the exact situation `agenix` is designed to avoid. At +least one of the file paths must be present at runtime and able to decrypt the +secret in question. Overriding `age.identityPaths` example: + +```nix +{ + age.identityPaths = [ "/var/lib/persistent/ssh_host_ed25519_key" ]; +} +``` + +### `age.secretsDir` + +`age.secretsDir` is the directory where secrets are symlinked to by +default.Usually, you don't need to change this. Defaults to +`/run/agenix`. + +Overriding `age.secretsDir` example: + +```nix +{ + age.secretsDir = "/run/keys"; +} +``` + +### `age.secretsMountPoint` + +`age.secretsMountPoint` is the directory where the secret generations +are created before they are symlinked. Usually, you don't need to +change this. Defaults to `/run/agenix.d`. + + +Overriding `age.secretsMountPoint` example: + +```nix +{ + age.secretsMountPoint = "/run/secret-generations"; +} +``` + +## agenix CLI reference {#agenix-cli-reference} + +``` +agenix - edit and rekey age secret files + +agenix -e FILE [-i PRIVATE_KEY] +agenix -r [-i PRIVATE_KEY] + +options: +-h, --help show help +-e, --edit FILE edits FILE using $EDITOR +-r, --rekey re-encrypts all secrets with specified recipients +-d, --decrypt FILE decrypts FILE to STDOUT +-i, --identity identity to use when decrypting +-v, --verbose verbose output + +FILE an age-encrypted file + +PRIVATE_KEY a path to a private SSH key used to decrypt file + +EDITOR environment variable of editor to use when editing FILE + +If STDIN is not interactive, EDITOR will be set to "cp /dev/stdin" + +RULES environment variable with path to Nix file specifying recipient public keys. +Defaults to './secrets.nix' diff --git a/doc/rekeying.md b/doc/rekeying.md new file mode 100644 index 0000000..4d56ad3 --- /dev/null +++ b/doc/rekeying.md @@ -0,0 +1,13 @@ +# Rekeying {#rekeying} + +If you change the public keys in `secrets.nix`, you should rekey your +secrets: + +```ShellSession +$ agenix --rekey +``` + +To rekey a secret, you have to be able to decrypt it. Because of +randomness in `age`'s encryption algorithms, the files always change +when rekeyed, even if the identities do not. (This eventually could be +improved upon by reading the identities from the age file.) diff --git a/doc/threat-model-warnings.md b/doc/threat-model-warnings.md new file mode 100644 index 0000000..f9c652c --- /dev/null +++ b/doc/threat-model-warnings.md @@ -0,0 +1,14 @@ +# Threat model/Warnings {#threat-model-warnings} + +This project has not been audited by a security professional. + +People unfamiliar with `age` might be surprised that secrets are not +authenticated. This means that every attacker that has write access to +the secret files can modify secrets because public keys are exposed. +This seems like not a problem on the first glance because changing the +configuration itself could expose secrets easily. However, reviewing +configuration changes is easier than reviewing random secrets (for +example, 4096-bit rsa keys). This would be solved by having a message +authentication code (MAC) like other implementations like GPG or +[sops](https://github.com/Mic92/sops-nix) have, however this was left +out for simplicity in `age`. diff --git a/doc/toc.md b/doc/toc.md new file mode 100644 index 0000000..e6b7238 --- /dev/null +++ b/doc/toc.md @@ -0,0 +1,18 @@ +# agenix + +* [Introduction](#introduction) +* [Problem and solution](#problem-and-solution) +* [Features](#features) +* Installation + * [flakes](#install-via-flakes) + * [niv](#install-via-niv) + * [fetchTarball](#install-via-fetchtarball) + * [nix-channel](#install-via-nix-channel) +* [Tutorial](#tutorial) +* [Reference](#reference) + * [`age` module reference](#age-module-reference) + * [agenix CLI reference](#agenix-cli-reference) +* [Community and Support](#community-and-support) +* [Threat model/Warnings](#threat-model-warnings) +* [Contributing](#contributing) +* [Acknowledgements](#acknowledgements) diff --git a/doc/tutorial.md b/doc/tutorial.md new file mode 100644 index 0000000..8344121 --- /dev/null +++ b/doc/tutorial.md @@ -0,0 +1,51 @@ +# Tutorial {#tutorial} + +1. The system you want to deploy secrets to should already exist and + have `sshd` running on it so that it has generated SSH host keys in + `/etc/ssh/`. + +2. Make a directory to store secrets and `secrets.nix` file for listing secrets and their public keys (This file is **not** imported into your NixOS configuration. It is only used for the `agenix` CLI.): + + ```ShellSession + $ mkdir secrets + $ cd secrets + $ touch secrets.nix + ``` +3. Add public keys to `secrets.nix` file (hint: use `ssh-keyscan` or GitHub (for example, https://github.com/ryantm.keys)): + ```nix + let + user1 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIL0idNvgGiucWgup/mP78zyC23uFjYq0evcWdjGQUaBH"; + user2 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILI6jSq53F/3hEmSs+oq9L4TwOo1PrDMAgcA1uo1CCV/"; + users = [ user1 user2 ]; + + system1 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPJDyIr/FSz1cJdcoW69R+NrWzwGK/+3gJpqD1t8L2zE"; + system2 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKzxQgondgEYcLpcPdJLrTdNgZ2gznOHCAxMdaceTUT1"; + systems = [ system1 system2 ]; + in + { + "secret1.age".publicKeys = [ user1 system1 ]; + "secret2.age".publicKeys = users ++ systems; + } + ``` +4. Edit secret files (these instructions assume your SSH private key is in ~/.ssh/): + ```ShellSession + $ agenix -e secret1.age + ``` +5. Add secret to a NixOS module config: + ```nix + { + age.secrets.secret1.file = ../secrets/secret1.age; + } + ``` +6. Use the secret in your config: + ```nix + { + users.users.user1 = { + isNormalUser = true; + passwordFile = config.age.secrets.secret1.path; + }; + } + ``` +7. NixOS rebuild or use your deployment tool like usual. + + The secret will be decrypted to the value of `config.age.secrets.secret1.path` (`/run/agenix/secret1` by default). diff --git a/flake.lock b/flake.lock index df8a306..a73d62d 100644 --- a/flake.lock +++ b/flake.lock @@ -23,11 +23,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1674641431, - "narHash": "sha256-qfo19qVZBP4qn5M5gXc/h1MDgAtPA5VxJm9s8RUAkVk=", + "lastModified": 1677676435, + "narHash": "sha256-6FxdcmQr5JeZqsQvfinIMr0XcTyTuR7EXX0H3ANShpQ=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "9b97ad7b4330aacda9b2343396eb3df8a853b4fc", + "rev": "a08d6979dd7c82c4cef0dcc6ac45ab16051c1169", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 143dd5f..6269b44 100644 --- a/flake.nix +++ b/flake.nix @@ -15,6 +15,7 @@ darwin, }: let agenix = system: nixpkgs.legacyPackages.${system}.callPackage ./pkgs/agenix.nix {}; + doc = system: nixpkgs.legacyPackages.${system}.callPackage ./pkgs/doc.nix {}; in { nixosModules.age = import ./modules/age.nix; nixosModules.default = self.nixosModules.age; @@ -26,23 +27,28 @@ formatter.x86_64-darwin = nixpkgs.legacyPackages.x86_64-darwin.alejandra; packages.x86_64-darwin.agenix = agenix "x86_64-darwin"; + packages.x86_64-darwin.doc = doc "x86_64-darwin"; packages.x86_64-darwin.default = self.packages.x86_64-darwin.agenix; formatter.aarch64-darwin = nixpkgs.legacyPackages.aarch64-darwin.alejandra; packages.aarch64-darwin.agenix = agenix "aarch64-darwin"; + packages.aarch64-darwin.doc = doc "aarch64-darwin"; packages.aarch64-darwin.default = self.packages.aarch64-darwin.agenix; formatter.aarch64-linux = nixpkgs.legacyPackages.aarch64-linux.alejandra; packages.aarch64-linux.agenix = agenix "aarch64-linux"; + packages.aarch64-linux.doc = doc "aarch64-linux"; packages.aarch64-linux.default = self.packages.aarch64-linux.agenix; formatter.i686-linux = nixpkgs.legacyPackages.i686-linux.alejandra; packages.i686-linux.agenix = agenix "i686-linux"; + packages.i686-linux.doc = doc "i686-linux"; packages.i686-linux.default = self.packages.i686-linux.agenix; formatter.x86_64-linux = nixpkgs.legacyPackages.x86_64-linux.alejandra; packages.x86_64-linux.agenix = agenix "x86_64-linux"; packages.x86_64-linux.default = self.packages.x86_64-linux.agenix; + packages.x86_64-linux.doc = doc "x86_64-linux"; checks.x86_64-linux.integration = import ./test/integration.nix { inherit nixpkgs; pkgs = nixpkgs.legacyPackages.x86_64-linux; diff --git a/pkgs/doc.nix b/pkgs/doc.nix new file mode 100644 index 0000000..db441dc --- /dev/null +++ b/pkgs/doc.nix @@ -0,0 +1,11 @@ +{ + stdenvNoCC, + mmdoc, + self, +}: +stdenvNoCC.mkDerivation rec { + name = "agenix-doc"; + src = ../doc; + phases = ["mmdocPhase"]; + mmdocPhase = "${mmdoc}/bin/mmdoc agenix $src $out"; +} From 119fac65b48523cd2c17a1e09eb1878cf47e6fe2 Mon Sep 17 00:00:00 2001 From: Ryan Mulligan Date: Sat, 4 Mar 2023 12:04:58 -0800 Subject: [PATCH 12/73] doc: try to fix doc ci --- .github/workflows/doc.yml | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index c8569c3..aeee02c 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -6,17 +6,15 @@ on: - main workflow_dispatch: -permissions: - contents: read - pages: write - id-token: write - -concurrency: - group: "pages" - cancel-in-progress: true - jobs: publish: + permissions: + contents: read + pages: write + id-token: write + concurrency: + group: "pages" + cancel-in-progress: true environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} From 2fb0a74be305d03d076ba5602bbf76d5b8277d87 Mon Sep 17 00:00:00 2001 From: Ryan Mulligan Date: Sat, 4 Mar 2023 13:06:51 -0800 Subject: [PATCH 13/73] doc: try a slightly different format for github action --- .github/workflows/doc.yml | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index aeee02c..f1be4b3 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -1,34 +1,43 @@ -name: doc +# Simple workflow for deploying static content to GitHub Pages +name: Deploy static content to Pages on: + # Runs on pushes targeting the default branch push: - branches: - - main + branches: [$default-branch] + + # Allows you to run this workflow manually from the Actions tab workflow_dispatch: +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow one concurrent deployment +concurrency: + group: "pages" + cancel-in-progress: true + jobs: - publish: - permissions: - contents: read - pages: write - id-token: write - concurrency: - group: "pages" - cancel-in-progress: true + # Single deploy job since we're just deploying + deploy: environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: cachix/install-nix-action@v20 - - run: nix build .#doc + - name: Checkout + uses: actions/checkout@v3 - name: Setup Pages uses: actions/configure-pages@v3 + - uses: cachix/install-nix-action@v20 + - run: nix build .#doc - name: Upload artifact uses: actions/upload-pages-artifact@v1 with: - path: 'result/multi' + path: './result/multi' - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v1 From b1d6d764e111d3a529cea436055bd9b8c631eb64 Mon Sep 17 00:00:00 2001 From: Ryan Mulligan Date: Sat, 4 Mar 2023 14:41:59 -0800 Subject: [PATCH 14/73] doc: actually fix github pages deploy --- .github/workflows/doc.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index f1be4b3..9334ed4 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -33,11 +33,9 @@ jobs: - name: Setup Pages uses: actions/configure-pages@v3 - uses: cachix/install-nix-action@v20 - - run: nix build .#doc + - run: nix build .#doc && mkdir -p _site/ && cp -r ./result/multi/* _site/ - name: Upload artifact uses: actions/upload-pages-artifact@v1 - with: - path: './result/multi' - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v1 From 40550f0619ede64d49f0b531d356e271c875c6db Mon Sep 17 00:00:00 2001 From: Wanja Hentze Date: Tue, 14 Mar 2023 18:53:32 +0100 Subject: [PATCH 15/73] fix truncated output when decrypting a large file to stdout via -d rage intentionally truncates large output when writing to stdout: https://github.com/str4d/rage/blob/55e52c252b4036eb76582c84cbc5b9f6ef23c67f/age/src/cli_common/file_io.rs#L219 but if told to write to "-" instead, it will not truncate: https://github.com/str4d/rage/blob/55e52c252b4036eb76582c84cbc5b9f6ef23c67f/age/src/cli_common/file_io.rs#L312 --- pkgs/agenix.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/agenix.sh b/pkgs/agenix.sh index 970ca30..c83abeb 100644 --- a/pkgs/agenix.sh +++ b/pkgs/agenix.sh @@ -196,5 +196,5 @@ function rekey { } [ $REKEY -eq 1 ] && rekey && exit 0 -[ $DECRYPT_ONLY -eq 1 ] && decrypt "${FILE}" "$(keys "$FILE")" && exit 0 +[ $DECRYPT_ONLY -eq 1 ] && DEFAULT_DECRYPT+=("-o" "-") && decrypt "${FILE}" "$(keys "$FILE")" && exit 0 edit "$FILE" && cleanup && exit 0 From 8722cf94f15be0d907e52c595b548bc1052b803e Mon Sep 17 00:00:00 2001 From: "Winston (Winny) Weinert" Date: Thu, 20 Apr 2023 18:50:12 -0500 Subject: [PATCH 16/73] doc: missing space --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 62f1b6c..5f583e0 100644 --- a/README.md +++ b/README.md @@ -457,7 +457,7 @@ secret in question. Overriding `age.identityPaths` example: #### `age.secretsDir` `age.secretsDir` is the directory where secrets are symlinked to by -default.Usually, you don't need to change this. Defaults to +default. Usually, you don't need to change this. Defaults to `/run/agenix`. Overriding `age.secretsDir` example: From 9274b82816ae4450ff13f634113e99a83d1bd4b1 Mon Sep 17 00:00:00 2001 From: Bruno BELANYI Date: Sat, 22 Apr 2023 20:24:07 +0100 Subject: [PATCH 17/73] Add home-manager module This is to update and fix the issues I saw in [1] and [2]. Using a service definition instead of an activation script should resolve the issue about the secrets disappearing after rebooting. Removed the `user` and `group` option as they do not make sense to me for a home-manager module, which should target a single user. They can always be added back if somebody comes screaming. This is somewhat modeled after sops-nix's own module [3]. [1]: https://github.com/ryantm/agenix/pull/58/ [2]: https://github.com/ryantm/agenix/pull/109 [3]: https://github.com/Mic92/sops-nix/blob/master/modules/home-manager/sops.nix --- flake.nix | 3 + modules/age-home.nix | 234 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 237 insertions(+) create mode 100644 modules/age-home.nix diff --git a/flake.nix b/flake.nix index 6269b44..90783c9 100644 --- a/flake.nix +++ b/flake.nix @@ -23,6 +23,9 @@ darwinModules.age = import ./modules/age.nix; darwinModules.default = self.darwinModules.age; + homeManagerModules.age = import ./modules/age-home.nix; + homeManagerModules.default = self.homeManagerModules.age; + overlays.default = import ./overlay.nix; formatter.x86_64-darwin = nixpkgs.legacyPackages.x86_64-darwin.alejandra; diff --git a/modules/age-home.nix b/modules/age-home.nix new file mode 100644 index 0000000..86bfbe0 --- /dev/null +++ b/modules/age-home.nix @@ -0,0 +1,234 @@ +{ + config, + options, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.age; + + ageBin = lib.getExe config.age.package; + + newGeneration = '' + _agenix_generation="$(basename "$(readlink "${cfg.secretsDir}")" || echo 0)" + (( ++_agenix_generation )) + echo "[agenix] creating new generation in ${cfg.secretsMountPoint}/$_agenix_generation" + mkdir -p "${cfg.secretsMountPoint}" + chmod 0751 "${cfg.secretsMountPoint}" + mkdir -p "${cfg.secretsMountPoint}/$_agenix_generation" + chmod 0751 "${cfg.secretsMountPoint}/$_agenix_generation" + ''; + + setTruePath = secretType: '' + ${ + if secretType.symlink + then '' + _truePath="${cfg.secretsMountPoint}/$_agenix_generation/${secretType.name}" + '' + else '' + _truePath="${secretType.path}" + '' + } + ''; + + installSecret = secretType: '' + ${setTruePath secretType} + echo "decrypting '${secretType.file}' to '$_truePath'..." + TMP_FILE="$_truePath.tmp" + + IDENTITIES=() + # shellcheck disable=2043 + for identity in ${toString cfg.identityPaths}; do + test -r "$identity" || continue + IDENTITIES+=(-i) + IDENTITIES+=("$identity") + done + + test "''${#IDENTITIES[@]}" -eq 0 && echo "[agenix] WARNING: no readable identities found!" + + mkdir -p "$(dirname "$_truePath")" + [ "${secretType.path}" != "${cfg.secretsDir}/${secretType.name}" ] && mkdir -p "$(dirname "${secretType.path}")" + ( + umask u=r,g=,o= + test -f "${secretType.file}" || echo '[agenix] WARNING: encrypted file ${secretType.file} does not exist!' + test -d "$(dirname "$TMP_FILE")" || echo "[agenix] WARNING: $(dirname "$TMP_FILE") does not exist!" + LANG=${config.i18n.defaultLocale or "C"} ${ageBin} --decrypt "''${IDENTITIES[@]}" -o "$TMP_FILE" "${secretType.file}" + ) + chmod ${secretType.mode} "$TMP_FILE" + mv -f "$TMP_FILE" "$_truePath" + + ${optionalString secretType.symlink '' + [ "${secretType.path}" != "${cfg.secretsDir}/${secretType.name}" ] && ln -sfn "${cfg.secretsDir}/${secretType.name}" "${secretType.path}" + ''} + ''; + + testIdentities = + map + (path: '' + test -f ${path} || echo '[agenix] WARNING: config.age.identityPaths entry ${path} not present!' + '') + cfg.identityPaths; + + cleanupAndLink = '' + _agenix_generation="$(basename "$(readlink "${cfg.secretsDir}")" || echo 0)" + (( ++_agenix_generation )) + echo "[agenix] symlinking new secrets to ${cfg.secretsDir} (generation $_agenix_generation)..." + ln -sfn "${cfg.secretsMountPoint}/$_agenix_generation" "${cfg.secretsDir}" + + (( _agenix_generation > 1 )) && { + echo "[agenix] removing old secrets (generation $(( _agenix_generation - 1 )))..." + rm -rf "${cfg.secretsMountPoint}/$(( _agenix_generation - 1 ))" + } + ''; + + installSecrets = builtins.concatStringsSep "\n" ( + ["echo '[agenix] decrypting secrets...'"] + ++ testIdentities + ++ (map installSecret (builtins.attrValues cfg.secrets)) + ++ [cleanupAndLink] + ); + + secretType = types.submodule ({ + config, + name, + ... + }: { + options = { + name = mkOption { + type = types.str; + default = name; + description = '' + Name of the file used in ''${cfg.secretsDir} + ''; + }; + file = mkOption { + type = types.path; + description = '' + Age file the secret is loaded from. + ''; + }; + path = mkOption { + type = types.str; + default = "${cfg.secretsDir}/${config.name}"; + description = '' + Path where the decrypted secret is installed. + ''; + }; + mode = mkOption { + type = types.str; + default = "0400"; + description = '' + Permissions mode of the decrypted secret in a format understood by chmod. + ''; + }; + symlink = mkEnableOption "symlinking secrets to their destination" // {default = true;}; + }; + }); + + mountingScript = let + app = pkgs.writeShellApplication { + name = "agenix-home-manager-mount-secrets"; + runtimeInputs = with pkgs; [coreutils]; + text = '' + ${newGeneration} + ${installSecrets} + exit 0 + ''; + }; + in + lib.getExe app; + + userDirectory = dir: let + inherit (pkgs.stdenv.hostPlatform) isDarwin; + baseDir = + if isDarwin + then "$(getconf DARWIN_USER_TEMP_DIR)" + else "$XDG_RUNTIME_DIR"; + in "${baseDir}/${dir}"; + + userDirectoryDescription = dir: '' + "$XDG_RUNTIME_DIR"/${dir} on linux or "$(getconf DARWIN_USER_TEMP_DIR)"/${dir} on darwin. + ''; +in { + options.age = { + package = mkPackageOption pkgs "rage" {}; + + secrets = mkOption { + type = types.attrsOf secretType; + default = {}; + description = '' + Attrset of secrets. + ''; + }; + + identityPaths = mkOption { + type = types.listOf types.path; + default = [ + "${config.home.homeDirectory}/.ssh/id_ed25519" + "${config.home.homeDirectory}/.ssh/id_rsa" + ]; + defaultText = litteralExpression '' + [ + "''${config.home.homeDirectory}/.ssh/id_ed25519" + "''${config.home.homeDirectory}/.ssh/id_rsa" + ] + ''; + description = '' + Path to SSH keys to be used as identities in age decryption. + ''; + }; + + secretsDir = mkOption { + type = types.str; + default = userDirectory "agenix"; + defaultText = userDirectoryDescription "agenix"; + description = '' + Folder where secrets are symlinked to + ''; + }; + + secretsMountPoint = mkOption { + default = userDirectory "agenix.d"; + defaultText = userDirectoryDescription "agenix.d"; + description = '' + Where secrets are created before they are symlinked to ''${cfg.secretsDir} + ''; + }; + }; + + config = mkIf (cfg.secrets != {}) { + assertions = [ + { + assertion = cfg.identityPaths != []; + message = "age.identityPaths must be set."; + } + ]; + + systemd.user.services.agenix = lib.mkIf pkgs.stdenv.hostPlatform.isLinux { + Unit = { + Description = "agenix activation"; + }; + Service = { + Type = "oneshot"; + ExecStart = mountingScript; + }; + Install.WantedBy = ["default.target"]; + }; + + launchd.agents.activate-agenix = { + enable = true; + config = { + ProgramArguments = [mountingScript]; + KeepAlive = { + Crashed = false; + SuccessfulExit = false; + }; + RunAtLoad = true; + ProcessType = "Background"; + StandardOutPath = "${config.home.homeDirectory}/Library/Logs/agenix/stdout"; + StandardErrorPath = "${config.home.homeDirectory}/Library/Logs/agenix/stderr"; + }; + }; + }; +} From 1f43d94d52a750270202f22d50598e6234aecf86 Mon Sep 17 00:00:00 2001 From: Bruno BELANYI Date: Sun, 23 Apr 2023 11:22:03 +0100 Subject: [PATCH 18/73] Add home-manager input --- flake.lock | 21 +++++++++++++++++++++ flake.nix | 5 +++++ 2 files changed, 26 insertions(+) diff --git a/flake.lock b/flake.lock index a73d62d..3be370f 100644 --- a/flake.lock +++ b/flake.lock @@ -21,6 +21,26 @@ "type": "github" } }, + "home-manager": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1682203081, + "narHash": "sha256-kRL4ejWDhi0zph/FpebFYhzqlOBrk0Pl3dzGEKSAlEw=", + "owner": "nix-community", + "repo": "home-manager", + "rev": "32d3e39c491e2f91152c84f8ad8b003420eab0a1", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "home-manager", + "type": "github" + } + }, "nixpkgs": { "locked": { "lastModified": 1677676435, @@ -40,6 +60,7 @@ "root": { "inputs": { "darwin": "darwin", + "home-manager": "home-manager", "nixpkgs": "nixpkgs" } } diff --git a/flake.nix b/flake.nix index 90783c9..127ce83 100644 --- a/flake.nix +++ b/flake.nix @@ -7,12 +7,17 @@ url = "github:lnl7/nix-darwin/master"; inputs.nixpkgs.follows = "nixpkgs"; }; + home-manager = { + url = "github:nix-community/home-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; }; outputs = { self, nixpkgs, darwin, + home-manager, }: let agenix = system: nixpkgs.legacyPackages.${system}.callPackage ./pkgs/agenix.nix {}; doc = system: nixpkgs.legacyPackages.${system}.callPackage ./pkgs/doc.nix {}; From 0155c5710e8527f19ec61dbbd076495f3f920868 Mon Sep 17 00:00:00 2001 From: Bruno BELANYI Date: Sun, 23 Apr 2023 11:22:35 +0100 Subject: [PATCH 19/73] Test home-manager module --- flake.nix | 2 +- test/integration.nix | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index 127ce83..2df057b 100644 --- a/flake.nix +++ b/flake.nix @@ -58,7 +58,7 @@ packages.x86_64-linux.default = self.packages.x86_64-linux.agenix; packages.x86_64-linux.doc = doc "x86_64-linux"; checks.x86_64-linux.integration = import ./test/integration.nix { - inherit nixpkgs; + inherit nixpkgs home-manager; pkgs = nixpkgs.legacyPackages.x86_64-linux; system = "x86_64-linux"; }; diff --git a/test/integration.nix b/test/integration.nix index 64f5c50..f719356 100644 --- a/test/integration.nix +++ b/test/integration.nix @@ -6,6 +6,7 @@ config = {}; }, system ? builtins.currentSystem, + home-manager ? , }: pkgs.nixosTest { name = "agenix-integration"; @@ -18,6 +19,7 @@ pkgs.nixosTest { imports = [ ../modules/age.nix ./install_ssh_host_keys.nix + "${home-manager}/nixos" ]; services.openssh.enable = true; @@ -43,11 +45,28 @@ pkgs.nixosTest { }; }; }; + + home-manager.users.user1 = {options, ...}: { + imports = [ + ../modules/age-home.nix + ]; + + home.stateVersion = pkgs.lib.trivial.release; + + age = { + identityPaths = options.age.identityPaths.default ++ ["/home/user1/.ssh/this_key_wont_exist"]; + secrets.secret2 = { + # Only decryptable by user1's key + file = ../example/secret2.age; + }; + }; + }; }; testScript = let user = "user1"; password = "password1234"; + secret2 = "world!"; in '' system1.wait_for_unit("multi-user.target") system1.wait_until_succeeds("pgrep -f 'agetty.*tty1'") @@ -65,6 +84,9 @@ pkgs.nixosTest { system1.send_chars("whoami > /tmp/1\n") system1.wait_for_file("/tmp/1") assert "${user}" in system1.succeed("cat /tmp/1") + system1.send_chars("cat /run/user/$(id -u)/agenix/secret2 > /tmp/2\n") + system1.wait_for_file("/tmp/2") + assert "${secret2}" in system1.succeed("cat /tmp/2") userDo = lambda input : f"sudo -u user1 -- bash -c 'set -eou pipefail; cd /tmp/secrets; {input}'" From 3fbc22fe43673401c37f862057d045ec90ad5670 Mon Sep 17 00:00:00 2001 From: Nathan Henrie Date: Wed, 3 May 2023 17:25:54 -0600 Subject: [PATCH 20/73] Install user keys in Darwin tests --- test/install_ssh_host_keys_darwin.nix | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/test/install_ssh_host_keys_darwin.nix b/test/install_ssh_host_keys_darwin.nix index 78e1567..c826fa8 100644 --- a/test/install_ssh_host_keys_darwin.nix +++ b/test/install_ssh_host_keys_darwin.nix @@ -1,10 +1,17 @@ # Do not copy this! It is insecure. This is only okay because we are testing. { system.activationScripts.extraUserActivation.text = '' - echo "Installing SSH host key" + echo "Installing system SSH host key" sudo cp ${../example_keys/system1.pub} /etc/ssh/ssh_host_ed25519_key.pub sudo cp ${../example_keys/system1} /etc/ssh/ssh_host_ed25519_key sudo chmod 644 /etc/ssh/ssh_host_ed25519_key.pub sudo chmod 600 /etc/ssh/ssh_host_ed25519_key + + echo "Installing user SSH host key" + mkdir -p $HOME/.ssh + cp ${../example_keys/user1.pub} $HOME/.ssh/id_ed25519.pub + cp ${../example_keys/user1} $HOME/.ssh/id_ed25519 + chmod 644 $HOME/.ssh/id_ed25519.pub + chmod 600 $HOME/.ssh/id_ed25519 ''; } From 19bf5a20d835145e5f3fc8d61672eefee4c33450 Mon Sep 17 00:00:00 2001 From: Nathan Henrie Date: Wed, 3 May 2023 17:25:54 -0600 Subject: [PATCH 21/73] Clean-up Darwin integration test --- test/integration_darwin.nix | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/test/integration_darwin.nix b/test/integration_darwin.nix index 4894caa..f309667 100644 --- a/test/integration_darwin.nix +++ b/test/integration_darwin.nix @@ -8,7 +8,7 @@ testScript = pkgs.writeShellApplication { name = "agenix-integration"; text = '' - grep ${secret} ${config.age.secrets.secret1.path} + grep "${secret}" "${config.age.secrets.system-secret.path}" ''; }; in { @@ -19,9 +19,10 @@ in { services.nix-daemon.enable = true; - age.identityPaths = options.age.identityPaths.default ++ ["/etc/ssh/this_key_wont_exist"]; - - age.secrets.secret1.file = ../example/secret1.age; + age = { + identityPaths = options.age.identityPaths.default ++ ["/etc/ssh/this_key_wont_exist"]; + secrets.system-secret.file = ../example/secret1.age; + }; environment.systemPackages = [testScript]; } From 50743bd1178c1afc426ee914686d1437eec8c9d2 Mon Sep 17 00:00:00 2001 From: Nathan Henrie Date: Wed, 3 May 2023 17:25:54 -0600 Subject: [PATCH 22/73] Add darwin tests for home-manager module --- flake.nix | 58 +++++++++++++++++++++++----------- test/integration_hm_darwin.nix | 33 +++++++++++++++++++ 2 files changed, 73 insertions(+), 18 deletions(-) create mode 100644 test/integration_hm_darwin.nix diff --git a/flake.nix b/flake.nix index 2df057b..13d863f 100644 --- a/flake.nix +++ b/flake.nix @@ -57,24 +57,46 @@ packages.x86_64-linux.agenix = agenix "x86_64-linux"; packages.x86_64-linux.default = self.packages.x86_64-linux.agenix; packages.x86_64-linux.doc = doc "x86_64-linux"; - checks.x86_64-linux.integration = import ./test/integration.nix { - inherit nixpkgs home-manager; - pkgs = nixpkgs.legacyPackages.x86_64-linux; - system = "x86_64-linux"; - }; - checks."aarch64-darwin".integration = - (darwin.lib.darwinSystem { - system = "aarch64-darwin"; - modules = [./test/integration_darwin.nix "${darwin.outPath}/pkgs/darwin-installer/installer.nix"]; - }) - .system; - checks."x86_64-darwin".integration = - (darwin.lib.darwinSystem { - system = "x86_64-darwin"; - modules = [./test/integration_darwin.nix "${darwin.outPath}/pkgs/darwin-installer/installer.nix"]; - }) - .system; - darwinConfigurations.integration.system = self.checks."x86_64-darwin".integration; + checks = + nixpkgs.lib.genAttrs ["aarch64-darwin" "x86_64-darwin"] (system: { + integration = + (darwin.lib.darwinSystem { + inherit system; + modules = [ + ./test/integration_darwin.nix + "${darwin.outPath}/pkgs/darwin-installer/installer.nix" + home-manager.darwinModules.home-manager + { + home-manager = { + verbose = true; + useGlobalPkgs = true; + useUserPackages = true; + backupFileExtension = "hmbak"; + users.runner = ./test/integration_hm_darwin.nix; + }; + } + ]; + }) + .system; + }) + // { + x86_64-linux.integration = import ./test/integration.nix { + inherit nixpkgs home-manager; + pkgs = nixpkgs.legacyPackages.x86_64-linux; + system = "x86_64-linux"; + }; + }; + + darwinConfigurations.integration-x86_64.system = self.checks.x86_64-darwin.integration; + darwinConfigurations.integration-aarch64.system = self.checks.aarch64-darwin.integration; + + # Work-around for https://github.com/nix-community/home-manager/issues/3075 + legacyPackages = nixpkgs.lib.genAttrs ["aarch64-darwin" "x86_64-darwin"] (system: { + homeConfigurations.integration-darwin = home-manager.lib.homeManagerConfiguration { + pkgs = nixpkgs.legacyPackages.${system}; + modules = [./test/integration_hm_darwin.nix]; + }; + }); }; } diff --git a/test/integration_hm_darwin.nix b/test/integration_hm_darwin.nix new file mode 100644 index 0000000..d2e14b2 --- /dev/null +++ b/test/integration_hm_darwin.nix @@ -0,0 +1,33 @@ +{ + pkgs, + config, + options, + lib, + ... +}: { + imports = [../modules/age-home.nix]; + + age = { + identityPaths = options.age.identityPaths.default ++ ["/Users/user1/.ssh/this_key_wont_exist"]; + secrets.user-secret.file = ../example/secret2.age; + }; + + home = rec { + username = "runner"; + homeDirectory = lib.mkForce "/Users/${username}"; + stateVersion = lib.trivial.release; + }; + + home.file = let + name = "agenix-home-integration"; + in { + ${name}.source = pkgs.writeShellApplication { + inherit name; + text = let + secret = "world!"; + in '' + diff -q "${config.age.secrets.user-secret.path}" <(printf '${secret}\n') + ''; + }; + }; +} From 6b4ff3d19165b2bbf8f3447ceaa431fb22311037 Mon Sep 17 00:00:00 2001 From: Nathan Henrie Date: Wed, 3 May 2023 17:25:54 -0600 Subject: [PATCH 23/73] Check darwin home-manager test in CI --- .github/workflows/ci.yaml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 7a765e3..54089e9 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -25,8 +25,17 @@ jobs: - run: nix build .#doc - run: nix fmt . -- --check - run: nix flake check - - run: | + - name: "Install nix-darwin module" + run: | system=$(nix build --no-link --print-out-paths .#checks.x86_64-darwin.integration) ${system}/activate-user sudo ${system}/activate - - run: sudo /run/current-system/sw/bin/agenix-integration + - name: "Test nix-darwin module" + run: | + sudo /run/current-system/sw/bin/agenix-integration + - name: "Test home-manager module" + run: | + # Do the job of `home-manager switch` in-line to avoid rate limiting + nix build .#homeConfigurations.integration-darwin.activationPackage + ./result/activate + ~/agenix-home-integration/bin/agenix-home-integration From 758cdc98f49ba72f4e941933575db8a70f24cfe3 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Fri, 12 May 2023 11:01:22 +0200 Subject: [PATCH 24/73] Disable shellcheck warning about impossible comparison This shellcheck warning occurs when setting a path for a secret using the home-manager module. Signed-off-by: Sefa Eyeoglu --- modules/age-home.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/age-home.nix b/modules/age-home.nix index 86bfbe0..c3209ab 100644 --- a/modules/age-home.nix +++ b/modules/age-home.nix @@ -48,6 +48,7 @@ with lib; let test "''${#IDENTITIES[@]}" -eq 0 && echo "[agenix] WARNING: no readable identities found!" mkdir -p "$(dirname "$_truePath")" + # shellcheck disable=SC2193 [ "${secretType.path}" != "${cfg.secretsDir}/${secretType.name}" ] && mkdir -p "$(dirname "${secretType.path}")" ( umask u=r,g=,o= @@ -59,6 +60,7 @@ with lib; let mv -f "$TMP_FILE" "$_truePath" ${optionalString secretType.symlink '' + # shellcheck disable=SC2193 [ "${secretType.path}" != "${cfg.secretsDir}/${secretType.name}" ] && ln -sfn "${cfg.secretsDir}/${secretType.name}" "${secretType.path}" ''} ''; From 72205a86cafc06df6a11e1ae9dcef71374cd2cf5 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Fri, 12 May 2023 20:13:33 +0200 Subject: [PATCH 25/73] Add test for custom secret paths for HM Signed-off-by: Sefa Eyeoglu --- test/integration.nix | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/integration.nix b/test/integration.nix index f719356..e0ee85a 100644 --- a/test/integration.nix +++ b/test/integration.nix @@ -59,6 +59,10 @@ pkgs.nixosTest { # Only decryptable by user1's key file = ../example/secret2.age; }; + secrets.secret2Path = { + file = ../example/secret2.age; + path = "/home/user1/secret2"; + }; }; }; }; From 0d94960783efdcaaee800b22f03c861b8c575d3f Mon Sep 17 00:00:00 2001 From: Lin Jian Date: Mon, 26 Jun 2023 22:02:28 +0800 Subject: [PATCH 26/73] doc: fix defaultText by adding literalExpression I also remove an unnecessary defaultText and fix a typo. --- modules/age-home.nix | 9 +++++---- modules/age.nix | 21 ++++++++++++++++++++- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/modules/age-home.nix b/modules/age-home.nix index c3209ab..9458da9 100644 --- a/modules/age-home.nix +++ b/modules/age-home.nix @@ -149,9 +149,10 @@ with lib; let else "$XDG_RUNTIME_DIR"; in "${baseDir}/${dir}"; - userDirectoryDescription = dir: '' - "$XDG_RUNTIME_DIR"/${dir} on linux or "$(getconf DARWIN_USER_TEMP_DIR)"/${dir} on darwin. - ''; + userDirectoryDescription = dir: + literalExpression '' + "$XDG_RUNTIME_DIR"/${dir} on linux or "$(getconf DARWIN_USER_TEMP_DIR)"/${dir} on darwin. + ''; in { options.age = { package = mkPackageOption pkgs "rage" {}; @@ -170,7 +171,7 @@ in { "${config.home.homeDirectory}/.ssh/id_ed25519" "${config.home.homeDirectory}/.ssh/id_rsa" ]; - defaultText = litteralExpression '' + defaultText = literalExpression '' [ "''${config.home.homeDirectory}/.ssh/id_ed25519" "''${config.home.homeDirectory}/.ssh/id_rsa" diff --git a/modules/age.nix b/modules/age.nix index 8c99214..a5b160c 100644 --- a/modules/age.nix +++ b/modules/age.nix @@ -133,6 +133,7 @@ with lib; let name = mkOption { type = types.str; default = config._module.args.name; + defaultText = literalExpression "config._module.args.name"; description = '' Name of the file used in ''${cfg.secretsDir} ''; @@ -146,6 +147,9 @@ with lib; let path = mkOption { type = types.str; default = "${cfg.secretsDir}/${config.name}"; + defaultText = literalExpression '' + "''${cfg.secretsDir}/''${config.name}" + ''; description = '' Path where the decrypted secret is installed. ''; @@ -167,6 +171,9 @@ with lib; let group = mkOption { type = types.str; default = users.${config.owner}.group or "0"; + defaultText = literalExpression '' + users.''${config.owner}.group or "0" + ''; description = '' Group of the decrypted secret. ''; @@ -183,6 +190,9 @@ in { ageBin = mkOption { type = types.str; default = "${pkgs.rage}/bin/rage"; + defaultText = literalExpression '' + "''${pkgs.rage}/bin/rage" + ''; description = '' The age executable to use. ''; @@ -210,7 +220,6 @@ in { && (builtins.match ".+/" s) == null) # without trailing slash // {description = "${types.str.description} (with check: non-empty without trailing slash)";}; default = "/run/agenix.d"; - defaultText = "/run/agenix.d"; description = '' Where secrets are created before they are symlinked to ''${cfg.secretsDir} ''; @@ -226,6 +235,16 @@ in { "/etc/ssh/ssh_host_rsa_key" ] else []; + defaultText = literalExpression '' + if (config.services.openssh.enable or false) + then map (e: e.path) (lib.filter (e: e.type == "rsa" || e.type == "ed25519") config.services.openssh.hostKeys) + else if isDarwin + then [ + "/etc/ssh/ssh_host_ed25519_key" + "/etc/ssh/ssh_host_rsa_key" + ] + else []; + ''; description = '' Path to SSH keys to be used as identities in age decryption. ''; From 6e8a48c2dca4fd8bd6a002dda1da1645753ef006 Mon Sep 17 00:00:00 2001 From: Lin Jian Date: Mon, 26 Jun 2023 23:25:55 +0800 Subject: [PATCH 27/73] doc: fix nixos option format in descriptions --- modules/age.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/age.nix b/modules/age.nix index a5b160c..9468779 100644 --- a/modules/age.nix +++ b/modules/age.nix @@ -135,7 +135,7 @@ with lib; let default = config._module.args.name; defaultText = literalExpression "config._module.args.name"; description = '' - Name of the file used in ''${cfg.secretsDir} + Name of the file used in {option}`age.secretsDir` ''; }; file = mkOption { @@ -221,7 +221,7 @@ in { // {description = "${types.str.description} (with check: non-empty without trailing slash)";}; default = "/run/agenix.d"; description = '' - Where secrets are created before they are symlinked to ''${cfg.secretsDir} + Where secrets are created before they are symlinked to {option}`age.secretsDir` ''; }; identityPaths = mkOption { From 78733d6d09ffc49fc7bfcab29abd5d9676e1182a Mon Sep 17 00:00:00 2001 From: malteneuss Date: Sun, 16 Jul 2023 20:12:02 +0200 Subject: [PATCH 28/73] Make intro section more beginner friendly --- README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5f583e0..7ea4256 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,14 @@ # agenix - [age](https://github.com/FiloSottile/age)-encrypted secrets for NixOS -`agenix` is a commandline tool for managing secrets encrypted with your existing SSH keys. This project also includes the NixOS module `age` for adding encrypted secrets into the Nix store and decrypting them. +`agenix` is a small and convenient Nix library for securely managing and deploying secrets using common public-private SSH key pairs: +You can encrypt a secret (password, access-token, etc.) on a source machine using a number of public SSH keys, +and deploy that encrypted secret to any another target machine that has the corresponding private SSH key of one of those public keys. +This project contains two parts: +1. An `agenix` commandline app (CLI) to encrypt secrets into secured `.age` files that can be openly shared on Github, Nix store, etc. +2. An `agenix` NixOS module to conveniently + - add those encrypted secrets (`.age` files) into the Nix store so that they can be deployed like any other Nix package using `nixos-rebuild` or similar tools. + - automatically decrypt on a target machine using the private SSH keys on that machine + - automatically mount these decrypted secrets on a well known path like `/run/agenix/...` to be consumed. ## Contents From b91dfbaf769b51d69edbee9971234087eca10d3a Mon Sep 17 00:00:00 2001 From: malteneuss Date: Sun, 16 Jul 2023 20:17:20 +0200 Subject: [PATCH 29/73] Fix indentation --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7ea4256..3a8f21f 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,9 @@ and deploy that encrypted secret to any another target machine that has the corr This project contains two parts: 1. An `agenix` commandline app (CLI) to encrypt secrets into secured `.age` files that can be openly shared on Github, Nix store, etc. 2. An `agenix` NixOS module to conveniently - - add those encrypted secrets (`.age` files) into the Nix store so that they can be deployed like any other Nix package using `nixos-rebuild` or similar tools. - - automatically decrypt on a target machine using the private SSH keys on that machine - - automatically mount these decrypted secrets on a well known path like `/run/agenix/...` to be consumed. + * add those encrypted secrets (`.age` files) into the Nix store so that they can be deployed like any other Nix package using `nixos-rebuild` or similar tools. + * automatically decrypt on a target machine using the private SSH keys on that machine + * automatically mount these decrypted secrets on a well known path like `/run/agenix/...` to be consumed. ## Contents From 6d20bf81f8ed177281e72cc3f1cbb73637d3ad35 Mon Sep 17 00:00:00 2001 From: malteneuss Date: Sun, 16 Jul 2023 21:23:10 +0200 Subject: [PATCH 30/73] Fix intro indentation --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3a8f21f..204a974 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,9 @@ and deploy that encrypted secret to any another target machine that has the corr This project contains two parts: 1. An `agenix` commandline app (CLI) to encrypt secrets into secured `.age` files that can be openly shared on Github, Nix store, etc. 2. An `agenix` NixOS module to conveniently - * add those encrypted secrets (`.age` files) into the Nix store so that they can be deployed like any other Nix package using `nixos-rebuild` or similar tools. - * automatically decrypt on a target machine using the private SSH keys on that machine - * automatically mount these decrypted secrets on a well known path like `/run/agenix/...` to be consumed. + * add those encrypted secrets (`.age` files) into the Nix store so that they can be deployed like any other Nix package using `nixos-rebuild` or similar tools. + * automatically decrypt on a target machine using the private SSH keys on that machine + * automatically mount these decrypted secrets on a well known path like `/run/agenix/...` to be consumed. ## Contents From 1d7fd1569043356d9c64d81e8b40177e164f2017 Mon Sep 17 00:00:00 2001 From: malteneuss Date: Sun, 16 Jul 2023 21:34:50 +0200 Subject: [PATCH 31/73] Extend flake install section --- README.md | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 204a974..a238f82 100644 --- a/README.md +++ b/README.md @@ -199,13 +199,14 @@ To install the `agenix` binary: #### Install CLI via Flakes -You don't need to install it, +You can run the CLI tool ad-hoc without installing it: ```ShellSession nix run github:ryantm/agenix -- --help ``` -but, if you want to (change the system based on your system): +But you can also add it permanently into a [NixOS module](https://nixos.wiki/wiki/NixOS_modules) +(replace system "x86_64-linux" with your system): ```nix { @@ -213,6 +214,28 @@ but, if you want to (change the system based on your system): } ``` +e.g. inside your `flake.nix` file: + +```nix +{ + inputs.agenix.url = "github:ryantm/agenix"; + # ... + + outputs = { self, nixpkgs, agenix }: { + # change `yourhostname` to your actual hostname + nixosConfigurations.yourhostname = nixpkgs.lib.nixosSystem { + system = "x86_64-linux"; + modules = [ + # ... + { + environment.systemPackages = [ agenix.packages.${system}.default ]; + } + ]; + }; + }; +} +``` + ## Tutorial From 2bee5c988c41387ef61879927206e1bc98aae978 Mon Sep 17 00:00:00 2001 From: malteneuss Date: Sun, 16 Jul 2023 22:40:26 +0200 Subject: [PATCH 32/73] Extend tutorial section --- README.md | 43 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index a238f82..2ed9974 100644 --- a/README.md +++ b/README.md @@ -244,14 +244,15 @@ e.g. inside your `flake.nix` file: have `sshd` running on it so that it has generated SSH host keys in `/etc/ssh/`. -2. Make a directory to store secrets and `secrets.nix` file for listing secrets and their public keys (This file is **not** imported into your NixOS configuration. It is only used for the `agenix` CLI.): - +2. Make a directory to store secrets and `secrets.nix` file for listing secrets and their public keys: ```ShellSession $ mkdir secrets $ cd secrets $ touch secrets.nix ``` -3. Add public keys to `secrets.nix` file (hint: use `ssh-keyscan` or GitHub (for example, https://github.com/ryantm.keys)): + This `secrets.nix` file is **not** imported into your NixOS configuration. + It's only used for the `agenix` CLI tool (example below) to know which public keys to use for encryption. +3. Add public keys to your `secrets.nix` file: ```nix let user1 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIL0idNvgGiucWgup/mP78zyC23uFjYq0evcWdjGQUaBH"; @@ -267,17 +268,32 @@ e.g. inside your `flake.nix` file: "secret2.age".publicKeys = users ++ systems; } ``` -4. Edit secret files (these instructions assume your SSH private key is in ~/.ssh/): + These are the users and systems that will be able to decrypt the `.age` files later with their corresponding private keys. + You can obtain the public keys from + * your local computer usually in `~/.ssh`, e.g. `~/.ssh/id_ed25519.pub`. + * from a running target machine with `ssh-keyscan`: + ```ShellSession + $ ssh-keyscan @ + ... ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKzxQgondgEYcLpcPdJLrTdNgZ2gznOHCAxMdaceTUT1 + ... + ``` + * from GitHub like https://github.com/ryantm.keys. +4. Create a secret file: ```ShellSession $ agenix -e secret1.age ``` + It will open a temporary file in the app configured in your $EDITOR environment variable. + When you save that file its content will be encrypted with all the public keys mentioned in the `secrets.nix` file. 5. Add secret to a NixOS module config: ```nix { age.secrets.secret1.file = ../secrets/secret1.age; } ``` -6. Use the secret in your config: + When the `age.secrets` attribute set contains a secret, the `agenix` NixOS module will later automatically decrypt and mount that secret under the default path `/run/agenix/secret1`. + Here the `secret1.age` file becomes part of your NixOS deployment, i.e. moves into the Nix store. + +6. Reference the secrets' mount path in your config: ```nix { users.users.user1 = { @@ -286,9 +302,22 @@ e.g. inside your `flake.nix` file: }; } ``` -7. NixOS rebuild or use your deployment tool like usual. + You can reference the mount path to the (later) unencrypted secret already in your other configuration. + So `config.age.secrets.secret1.path` will contain the path `/run/agenix/secret1` by default. +7. Use `nixos-rebuild` or [another deployment tool](https://nixos.wiki/wiki/Applications#Deployment") of choice as usual. - The secret will be decrypted to the value of `config.age.secrets.secret1.path` (`/run/agenix/secret1` by default). + The `secret1.age` file will be copied over to the target machine like any other Nix package. + Then it will be decrypted and mounted as described before. +8. Edit secret files: + ```ShellSession + $ agenix -e secret1.age + ``` + It assumes your SSH private key is in `~/.ssh/`. + In order to decrypt and open a `.age` file for editing you need the private key of one of the public keys + it was encrypted with. You can pass the private key you want to use explicitly with `-i`, e.g. + ```ShellSession + $ agenix -e secret1.age -i ~/.ssh/id_ed25519 + ``` ## Reference From 91220a701d0bf8055547620baa765b36d885db7a Mon Sep 17 00:00:00 2001 From: malteneuss Date: Mon, 24 Jul 2023 21:51:25 +0200 Subject: [PATCH 33/73] Rephrase cli app summary --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2ed9974..998bfc2 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ You can encrypt a secret (password, access-token, etc.) on a source machine using a number of public SSH keys, and deploy that encrypted secret to any another target machine that has the corresponding private SSH key of one of those public keys. This project contains two parts: -1. An `agenix` commandline app (CLI) to encrypt secrets into secured `.age` files that can be openly shared on Github, Nix store, etc. +1. An `agenix` commandline app (CLI) to encrypt secrets into secured `.age` files that can be copied into the Nix store. 2. An `agenix` NixOS module to conveniently * add those encrypted secrets (`.age` files) into the Nix store so that they can be deployed like any other Nix package using `nixos-rebuild` or similar tools. * automatically decrypt on a target machine using the private SSH keys on that machine From fe4f564f13c6151df7983fc3449061dc71c37d8d Mon Sep 17 00:00:00 2001 From: Nicolas Lenz Date: Sat, 9 Sep 2023 16:46:53 +0200 Subject: [PATCH 34/73] fix(home): shellcheck failure for fixed secretsDir --- modules/age-home.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/age-home.nix b/modules/age-home.nix index 9458da9..99ccccb 100644 --- a/modules/age-home.nix +++ b/modules/age-home.nix @@ -48,7 +48,7 @@ with lib; let test "''${#IDENTITIES[@]}" -eq 0 && echo "[agenix] WARNING: no readable identities found!" mkdir -p "$(dirname "$_truePath")" - # shellcheck disable=SC2193 + # shellcheck disable=SC2193,SC2050 [ "${secretType.path}" != "${cfg.secretsDir}/${secretType.name}" ] && mkdir -p "$(dirname "${secretType.path}")" ( umask u=r,g=,o= @@ -60,7 +60,7 @@ with lib; let mv -f "$TMP_FILE" "$_truePath" ${optionalString secretType.symlink '' - # shellcheck disable=SC2193 + # shellcheck disable=SC2193,SC2050 [ "${secretType.path}" != "${cfg.secretsDir}/${secretType.name}" ] && ln -sfn "${cfg.secretsDir}/${secretType.name}" "${secretType.path}" ''} ''; From 1698ed385d3142a5ff927d5eafe4642207652dcd Mon Sep 17 00:00:00 2001 From: Ryan Mulligan Date: Thu, 14 Sep 2023 16:19:37 -0700 Subject: [PATCH 35/73] Create flakehub-publish-tagged.yml --- .github/workflows/flakehub-publish-tagged.yml | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .github/workflows/flakehub-publish-tagged.yml diff --git a/.github/workflows/flakehub-publish-tagged.yml b/.github/workflows/flakehub-publish-tagged.yml new file mode 100644 index 0000000..75fe0d2 --- /dev/null +++ b/.github/workflows/flakehub-publish-tagged.yml @@ -0,0 +1,27 @@ +name: "Publish tags to FlakeHub" +on: + push: + tags: + - "v?[0-9]+.[0-9]+.[0-9]+*" + workflow_dispatch: + inputs: + tag: + description: "The existing tag to publish to FlakeHub" + type: "string" + required: true +jobs: + flakehub-publish: + runs-on: "ubuntu-latest" + permissions: + id-token: "write" + contents: "read" + steps: + - uses: "actions/checkout@v3" + with: + ref: "${{ (inputs.tag != null) && format('refs/tags/{0}', inputs.tag) || '' }}" + - uses: "DeterminateSystems/nix-installer-action@main" + - uses: "DeterminateSystems/flakehub-push@main" + with: + visibility: "public" + name: "ryantm/agenix" + tag: "${{ inputs.tag }}" From 54693c91d923fecb4cf04c4535e3d84f8dec7919 Mon Sep 17 00:00:00 2001 From: Ryan Mulligan Date: Thu, 14 Sep 2023 16:20:33 -0700 Subject: [PATCH 36/73] version 0.14.0 --- pkgs/agenix.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/agenix.nix b/pkgs/agenix.nix index 648ccb9..3978cfa 100644 --- a/pkgs/agenix.nix +++ b/pkgs/agenix.nix @@ -12,7 +12,7 @@ }: stdenv.mkDerivation rec { pname = "agenix"; - version = "0.13.0"; + version = "0.14.0"; src = substituteAll { inherit ageBin version; sedBin = "${gnused}/bin/sed"; From 2ed2dc758234f0ccdae47f7f0a007450858001e1 Mon Sep 17 00:00:00 2001 From: Ryan Mulligan Date: Thu, 14 Sep 2023 16:37:58 -0700 Subject: [PATCH 37/73] dev: try switching to determinate systems installer action --- .github/workflows/ci.yaml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 54089e9..38f62cc 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -7,9 +7,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - uses: cachix/install-nix-action@v18 + - name: Install Nix + uses: DeterminateSystems/nix-installer-action@main with: - extra_nix_config: "system-features = nixos-test benchmark big-parallel kvm" + extra-conf: "system-features = nixos-test benchmark big-parallel kvm" - run: nix build - run: nix build .#doc - run: nix fmt . -- --check @@ -18,9 +19,10 @@ jobs: runs-on: macos-11 steps: - uses: actions/checkout@v3 - - uses: cachix/install-nix-action@v18 + - name: Install Nix + uses: DeterminateSystems/nix-installer-action@main with: - extra_nix_config: "system-features = nixos-test benchmark big-parallel kvm" + extra-conf: "system-features = nixos-test benchmark big-parallel kvm" - run: nix build - run: nix build .#doc - run: nix fmt . -- --check From 7f30f9b4b31219407fb0d7a536da3fdcbf151727 Mon Sep 17 00:00:00 2001 From: Nathan Henrie Date: Fri, 15 Sep 2023 07:53:36 -0600 Subject: [PATCH 38/73] Revert "dev: try switching to determinate systems installer action" This reverts commit 2ed2dc758234f0ccdae47f7f0a007450858001e1. --- .github/workflows/ci.yaml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 38f62cc..54089e9 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -7,10 +7,9 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: Install Nix - uses: DeterminateSystems/nix-installer-action@main + - uses: cachix/install-nix-action@v18 with: - extra-conf: "system-features = nixos-test benchmark big-parallel kvm" + extra_nix_config: "system-features = nixos-test benchmark big-parallel kvm" - run: nix build - run: nix build .#doc - run: nix fmt . -- --check @@ -19,10 +18,9 @@ jobs: runs-on: macos-11 steps: - uses: actions/checkout@v3 - - name: Install Nix - uses: DeterminateSystems/nix-installer-action@main + - uses: cachix/install-nix-action@v18 with: - extra-conf: "system-features = nixos-test benchmark big-parallel kvm" + extra_nix_config: "system-features = nixos-test benchmark big-parallel kvm" - run: nix build - run: nix build .#doc - run: nix fmt . -- --check From b76899f4c177fcf901e90704aa472f15d7c83cae Mon Sep 17 00:00:00 2001 From: Nathan Henrie Date: Fri, 15 Sep 2023 07:54:21 -0600 Subject: [PATCH 39/73] Update nix installer Fixes https://github.com/ryantm/agenix/issues/198 --- .github/workflows/ci.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 54089e9..b53a3f2 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - uses: cachix/install-nix-action@v18 + - uses: cachix/install-nix-action@v22 with: extra_nix_config: "system-features = nixos-test benchmark big-parallel kvm" - run: nix build @@ -18,7 +18,7 @@ jobs: runs-on: macos-11 steps: - uses: actions/checkout@v3 - - uses: cachix/install-nix-action@v18 + - uses: cachix/install-nix-action@v22 with: extra_nix_config: "system-features = nixos-test benchmark big-parallel kvm" - run: nix build From eb1386f3b246ae563c99ecadaaeff6b6d36fc9a5 Mon Sep 17 00:00:00 2001 From: Nathan Henrie Date: Mon, 13 Feb 2023 09:06:58 -0700 Subject: [PATCH 40/73] Use jq instead of sed --- pkgs/agenix.nix | 4 ++-- pkgs/agenix.sh | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pkgs/agenix.nix b/pkgs/agenix.nix index 3978cfa..e399dd0 100644 --- a/pkgs/agenix.nix +++ b/pkgs/agenix.nix @@ -2,7 +2,7 @@ lib, stdenv, rage, - gnused, + jq, nix, mktemp, diffutils, @@ -15,7 +15,7 @@ stdenv.mkDerivation rec { version = "0.14.0"; src = substituteAll { inherit ageBin version; - sedBin = "${gnused}/bin/sed"; + jqBin = "${jq}/bin/jq"; nixInstantiate = "${nix}/bin/nix-instantiate"; mktempBin = "${mktemp}/bin/mktemp"; diffBin = "${diffutils}/bin/diff"; diff --git a/pkgs/agenix.sh b/pkgs/agenix.sh index c83abeb..7b2045f 100644 --- a/pkgs/agenix.sh +++ b/pkgs/agenix.sh @@ -115,7 +115,7 @@ function cleanup { trap "cleanup" 0 2 3 15 function keys { - (@nixInstantiate@ --eval -E "(let rules = import $RULES; in builtins.concatStringsSep \"\n\" rules.\"$1\".publicKeys)" | @sedBin@ 's/"//g' | @sedBin@ 's/\\n/\n/g') | @sedBin@ '/^$/d' || exit 1 + (@nixInstantiate@ --json --eval -E "(let rules = import $RULES; in builtins.concatStringsSep \"\n\" rules.\"$FILE\".publicKeys)" | @jqBin@ -r .[]) || exit 1 } function decrypt { @@ -185,7 +185,7 @@ function edit { } 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) + FILES=$( (@nixInstantiate@ --json --eval -E "(let rules = import $RULES; in builtins.concatStringsSep \"\n\" (builtins.attrNames rules))" | @jqBin@ -r .[]) || exit 1) for FILE in $FILES do From da763b2c4bfac0310e8c1d972199e10967ad38b8 Mon Sep 17 00:00:00 2001 From: Nathan Henrie Date: Fri, 15 Sep 2023 16:22:30 -0600 Subject: [PATCH 41/73] Don't need concatStringSep if using jq to parse json arrays --- pkgs/agenix.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/agenix.sh b/pkgs/agenix.sh index 7b2045f..5980fe6 100644 --- a/pkgs/agenix.sh +++ b/pkgs/agenix.sh @@ -115,7 +115,7 @@ function cleanup { trap "cleanup" 0 2 3 15 function keys { - (@nixInstantiate@ --json --eval -E "(let rules = import $RULES; in builtins.concatStringsSep \"\n\" rules.\"$FILE\".publicKeys)" | @jqBin@ -r .[]) || exit 1 + (@nixInstantiate@ --json --eval -E "(let rules = import $RULES; in rules.\"$FILE\".publicKeys)" | @jqBin@ -r .[]) || exit 1 } function decrypt { @@ -185,7 +185,7 @@ function edit { } function rekey { - FILES=$( (@nixInstantiate@ --json --eval -E "(let rules = import $RULES; in builtins.concatStringsSep \"\n\" (builtins.attrNames rules))" | @jqBin@ -r .[]) || exit 1) + FILES=$( (@nixInstantiate@ --json --eval -E "(let rules = import $RULES; in builtins.attrNames rules)" | @jqBin@ -r .[]) || exit 1) for FILE in $FILES do From 115e561054afd80d22ac6225772ca89333112632 Mon Sep 17 00:00:00 2001 From: William McKinnon Date: Fri, 22 Sep 2023 01:32:46 -0400 Subject: [PATCH 42/73] fix: add --strict nix-instantiate to support builtins.readFile --- pkgs/agenix.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/agenix.sh b/pkgs/agenix.sh index 5980fe6..f638b10 100644 --- a/pkgs/agenix.sh +++ b/pkgs/agenix.sh @@ -115,7 +115,7 @@ function cleanup { trap "cleanup" 0 2 3 15 function keys { - (@nixInstantiate@ --json --eval -E "(let rules = import $RULES; in rules.\"$FILE\".publicKeys)" | @jqBin@ -r .[]) || exit 1 + (@nixInstantiate@ --json --eval --strict -E "(let rules = import $RULES; in rules.\"$FILE\".publicKeys)" | @jqBin@ -r .[]) || exit 1 } function decrypt { From b5fa96a90e07cc099cc5247540449263caeedb16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20H=C3=A4ring?= Date: Sun, 8 Oct 2023 14:13:59 +0200 Subject: [PATCH 43/73] feat: remove empty newlines from jq query When you do not have your pubkeys in your `secrets.nix` verbatim as string but read them from file like this: `desktop1 = builtins.readFile ./desktop1.pub;`, you will end up with empty newlines in the resulting list of keys, which will add `--recipient=''` arguments to your age call, failing the call. --- pkgs/agenix.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/agenix.sh b/pkgs/agenix.sh index f638b10..14b3a25 100644 --- a/pkgs/agenix.sh +++ b/pkgs/agenix.sh @@ -115,7 +115,7 @@ function cleanup { trap "cleanup" 0 2 3 15 function keys { - (@nixInstantiate@ --json --eval --strict -E "(let rules = import $RULES; in rules.\"$FILE\".publicKeys)" | @jqBin@ -r .[]) || exit 1 + (@nixInstantiate@ --json --eval --strict -E "(let rules = import $RULES; in rules.\"$FILE\".publicKeys)" | @jqBin@ -r 'map(rtrimstr("\n")) .[]') || exit 1 } function decrypt { @@ -185,7 +185,7 @@ function edit { } function rekey { - FILES=$( (@nixInstantiate@ --json --eval -E "(let rules = import $RULES; in builtins.attrNames rules)" | @jqBin@ -r .[]) || exit 1) + FILES=$( (@nixInstantiate@ --json --eval -E "(let rules = import $RULES; in builtins.attrNames rules)" | @jqBin@ -r 'map(rtrimstr("\n")) .[]') || exit 1) for FILE in $FILES do From dbc533ddc2568d5eaac1a39b9e638275aff3841c Mon Sep 17 00:00:00 2001 From: Ryan Mulligan Date: Sun, 8 Oct 2023 07:31:54 -0700 Subject: [PATCH 44/73] Revert "feat: remove empty newlines from jq query" --- pkgs/agenix.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/agenix.sh b/pkgs/agenix.sh index 14b3a25..f638b10 100644 --- a/pkgs/agenix.sh +++ b/pkgs/agenix.sh @@ -115,7 +115,7 @@ function cleanup { trap "cleanup" 0 2 3 15 function keys { - (@nixInstantiate@ --json --eval --strict -E "(let rules = import $RULES; in rules.\"$FILE\".publicKeys)" | @jqBin@ -r 'map(rtrimstr("\n")) .[]') || exit 1 + (@nixInstantiate@ --json --eval --strict -E "(let rules = import $RULES; in rules.\"$FILE\".publicKeys)" | @jqBin@ -r .[]) || exit 1 } function decrypt { @@ -185,7 +185,7 @@ function edit { } function rekey { - FILES=$( (@nixInstantiate@ --json --eval -E "(let rules = import $RULES; in builtins.attrNames rules)" | @jqBin@ -r 'map(rtrimstr("\n")) .[]') || exit 1) + FILES=$( (@nixInstantiate@ --json --eval -E "(let rules = import $RULES; in builtins.attrNames rules)" | @jqBin@ -r .[]) || exit 1) for FILE in $FILES do From 05591973d7a546f84c60f4992715f8a5e3d74378 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Fri, 3 Nov 2023 14:53:33 -0700 Subject: [PATCH 45/73] use named variable instead of numbered one --- pkgs/agenix.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/agenix.sh b/pkgs/agenix.sh index f638b10..27a53c9 100644 --- a/pkgs/agenix.sh +++ b/pkgs/agenix.sh @@ -181,7 +181,7 @@ function edit { @ageBin@ "${ENCRYPT[@]}" <"$CLEARTEXT_FILE" || exit 1 - mv -f "$REENCRYPTED_FILE" "$1" + mv -f "$REENCRYPTED_FILE" "$FILE" } function rekey { From 65fe5959c3014be90cbc046f30288b6e14beff48 Mon Sep 17 00:00:00 2001 From: Charles Hall Date: Fri, 3 Nov 2023 14:57:48 -0700 Subject: [PATCH 46/73] create leading directories if they don't exist This works for files without directories too because `dirname` prints `.` in that case. --- pkgs/agenix.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkgs/agenix.sh b/pkgs/agenix.sh index 27a53c9..5212977 100644 --- a/pkgs/agenix.sh +++ b/pkgs/agenix.sh @@ -181,6 +181,8 @@ function edit { @ageBin@ "${ENCRYPT[@]}" <"$CLEARTEXT_FILE" || exit 1 + mkdir -p "$(dirname "$FILE")" + mv -f "$REENCRYPTED_FILE" "$FILE" } From 4c4860609491a98c1e4a6f03e0ba27c0a23982c0 Mon Sep 17 00:00:00 2001 From: Shiva Kaul Date: Sun, 19 Mar 2023 00:17:27 -0400 Subject: [PATCH 47/73] only backup cleartext file if it exists Avoids complaints from `cp` about nonexistent files. --- pkgs/agenix.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/agenix.sh b/pkgs/agenix.sh index f638b10..b82d942 100644 --- a/pkgs/agenix.sh +++ b/pkgs/agenix.sh @@ -155,7 +155,7 @@ function edit { decrypt "$FILE" "$KEYS" || exit 1 - cp "$CLEARTEXT_FILE" "$CLEARTEXT_FILE.before" + [ ! -f "$CLEARTEXT_FILE" ] || cp "$CLEARTEXT_FILE" "$CLEARTEXT_FILE.before" [ -t 0 ] || EDITOR='cp /dev/stdin' From 221a1f22e5333882419023c698d21fbfc3017642 Mon Sep 17 00:00:00 2001 From: Ryan Mulligan Date: Wed, 20 Dec 2023 07:51:36 -0800 Subject: [PATCH 48/73] dev: add release-drafter --- .github/release-drafter.yml | 33 +++++++++++++++++++++++++++ .github/workflows/release-drafter.yml | 33 +++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 .github/release-drafter.yml create mode 100644 .github/workflows/release-drafter.yml diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml new file mode 100644 index 0000000..a6d2e95 --- /dev/null +++ b/.github/release-drafter.yml @@ -0,0 +1,33 @@ + +name-template: '$RESOLVED_VERSION' +tag-template: '$RESOLVED_VERSION' +categories: + - title: '🚀 Features' + labels: + - 'feature' + - 'enhancement' + - title: '🐛 Bug Fixes' + labels: + - 'fix' + - 'bugfix' + - 'bug' + - title: '🧰 Development' + label: 'dev' + - title: '🤖 Dependencies' + label: 'dependencies' +change-template: '- $TITLE @$AUTHOR (#$NUMBER)' +change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks. +version-resolver: + major: + labels: + - 'major' + minor: + labels: + - 'minor' + patch: + labels: + - 'patch' + default: patch +template: | + ## Changes + $CHANGES diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml new file mode 100644 index 0000000..dd646d2 --- /dev/null +++ b/.github/workflows/release-drafter.yml @@ -0,0 +1,33 @@ +name: Release Drafter + +on: + push: + # branches to consider in the event; optional, defaults to all + branches: + - main + # pull_request event is required only for autolabeler + pull_request: + # Only following types are handled by the action, but one can default to all as well + types: [opened, reopened, synchronize] + # pull_request_target event is required for autolabeler to support PRs from forks + pull_request_target: + types: [opened, reopened, synchronize] + +permissions: + contents: read + +jobs: + update_release_draft: + permissions: + # write permission is required to create a github release + contents: write + # write permission is required for autolabeler + # otherwise, read permission is required at least + pull-requests: write + runs-on: ubuntu-latest + steps: + # Drafts your next Release notes as Pull Requests are merged into "main" + - uses: release-drafter/release-drafter@v5 + continue-on-error: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 93cec0ce6ed1061bf0973c5fef76b2d8e76d4810 Mon Sep 17 00:00:00 2001 From: Ryan Mulligan Date: Wed, 20 Dec 2023 08:03:06 -0800 Subject: [PATCH 49/73] dev: add security label category --- .github/release-drafter.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index a6d2e95..125ac0c 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -15,6 +15,8 @@ categories: label: 'dev' - title: '🤖 Dependencies' label: 'dependencies' + - title: '🔒 Security' + label: 'security' change-template: '- $TITLE @$AUTHOR (#$NUMBER)' change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks. version-resolver: From 9d3b37a1177fedcce0a6c72ff4d337283a393fe2 Mon Sep 17 00:00:00 2001 From: Samuele Facenda Date: Wed, 15 Nov 2023 18:29:09 +0100 Subject: [PATCH 50/73] fix: update keys functions in agenix.sh The functions was always called with `$FILE` as first argument, but inside the argument is ignored. This change doesn' have any impact, but can solve problems caused by the keys function called with an argument different from `$FILE` --- pkgs/agenix.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/agenix.sh b/pkgs/agenix.sh index 098802e..b78b441 100644 --- a/pkgs/agenix.sh +++ b/pkgs/agenix.sh @@ -115,7 +115,7 @@ function cleanup { trap "cleanup" 0 2 3 15 function keys { - (@nixInstantiate@ --json --eval --strict -E "(let rules = import $RULES; in rules.\"$FILE\".publicKeys)" | @jqBin@ -r .[]) || exit 1 + (@nixInstantiate@ --json --eval --strict -E "(let rules = import $RULES; in rules.\"$1\".publicKeys)" | @jqBin@ -r .[]) || exit 1 } function decrypt { From 564595d0ad4be7277e07fa63b5a991b3c645655d Mon Sep 17 00:00:00 2001 From: Ryan Mulligan Date: Wed, 20 Dec 2023 08:33:16 -0800 Subject: [PATCH 51/73] version 0.15.0 --- pkgs/agenix.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/agenix.nix b/pkgs/agenix.nix index e399dd0..0d35bf4 100644 --- a/pkgs/agenix.nix +++ b/pkgs/agenix.nix @@ -12,7 +12,7 @@ }: stdenv.mkDerivation rec { pname = "agenix"; - version = "0.14.0"; + version = "0.15.0"; src = substituteAll { inherit ageBin version; jqBin = "${jq}/bin/jq"; From 344f98552660520eb2b5f354d16cf3534f0849bf Mon Sep 17 00:00:00 2001 From: Ryan Mulligan Date: Wed, 20 Dec 2023 08:56:05 -0800 Subject: [PATCH 52/73] dev: remove i686 support; simplify flake Why === * flake.nix had a lot of almost redundant lines in it. * i686 support is probably being dropped soon in nixpkgs https://github.com/NixOS/nixpkgs/pull/266164 What changed == * Add new input nix-systems/default which represents the default systems typically used in the Nix community * Define and use an eachSystem function that simplifies the flake.nix file --- flake.lock | 18 +++++++++++++++++- flake.nix | 34 +++++++++------------------------- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/flake.lock b/flake.lock index 3be370f..8ec68d4 100644 --- a/flake.lock +++ b/flake.lock @@ -61,7 +61,23 @@ "inputs": { "darwin": "darwin", "home-manager": "home-manager", - "nixpkgs": "nixpkgs" + "nixpkgs": "nixpkgs", + "systems": "systems" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" } } }, diff --git a/flake.nix b/flake.nix index 13d863f..1df2443 100644 --- a/flake.nix +++ b/flake.nix @@ -11,6 +11,7 @@ url = "github:nix-community/home-manager"; inputs.nixpkgs.follows = "nixpkgs"; }; + systems.url = "github:nix-systems/default"; }; outputs = { @@ -18,9 +19,9 @@ nixpkgs, darwin, home-manager, + systems, }: let - agenix = system: nixpkgs.legacyPackages.${system}.callPackage ./pkgs/agenix.nix {}; - doc = system: nixpkgs.legacyPackages.${system}.callPackage ./pkgs/doc.nix {}; + eachSystem = nixpkgs.lib.genAttrs (import systems); in { nixosModules.age = import ./modules/age.nix; nixosModules.default = self.nixosModules.age; @@ -33,30 +34,13 @@ overlays.default = import ./overlay.nix; - formatter.x86_64-darwin = nixpkgs.legacyPackages.x86_64-darwin.alejandra; - packages.x86_64-darwin.agenix = agenix "x86_64-darwin"; - packages.x86_64-darwin.doc = doc "x86_64-darwin"; - packages.x86_64-darwin.default = self.packages.x86_64-darwin.agenix; + formatter = eachSystem (system: nixpkgs.legacyPackages.${system}.alejandra); - formatter.aarch64-darwin = nixpkgs.legacyPackages.aarch64-darwin.alejandra; - packages.aarch64-darwin.agenix = agenix "aarch64-darwin"; - packages.aarch64-darwin.doc = doc "aarch64-darwin"; - packages.aarch64-darwin.default = self.packages.aarch64-darwin.agenix; - - formatter.aarch64-linux = nixpkgs.legacyPackages.aarch64-linux.alejandra; - packages.aarch64-linux.agenix = agenix "aarch64-linux"; - packages.aarch64-linux.doc = doc "aarch64-linux"; - packages.aarch64-linux.default = self.packages.aarch64-linux.agenix; - - formatter.i686-linux = nixpkgs.legacyPackages.i686-linux.alejandra; - packages.i686-linux.agenix = agenix "i686-linux"; - packages.i686-linux.doc = doc "i686-linux"; - packages.i686-linux.default = self.packages.i686-linux.agenix; - - formatter.x86_64-linux = nixpkgs.legacyPackages.x86_64-linux.alejandra; - packages.x86_64-linux.agenix = agenix "x86_64-linux"; - packages.x86_64-linux.default = self.packages.x86_64-linux.agenix; - packages.x86_64-linux.doc = doc "x86_64-linux"; + packages = eachSystem (system: { + agenix = nixpkgs.legacyPackages.${system}.callPackage ./pkgs/agenix.nix {}; + doc = nixpkgs.legacyPackages.${system}.callPackage ./pkgs/doc.nix {}; + default = self.packages.${system}.agenix; + }); checks = nixpkgs.lib.genAttrs ["aarch64-darwin" "x86_64-darwin"] (system: { From 097aa18b593a9f8215ce743a0796ae76b19b28d5 Mon Sep 17 00:00:00 2001 From: Ryan Mulligan Date: Tue, 21 Feb 2023 20:35:56 -0800 Subject: [PATCH 53/73] contrib: add direct tests for agenix These tests are MUCH faster than the NixOS tests. --- .github/workflows/ci.yaml | 8 +++-- pkgs/agenix.nix | 66 +++++++++++++++++++++++++-------------- 2 files changed, 49 insertions(+), 25 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index b53a3f2..29be3c5 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -9,7 +9,9 @@ jobs: - uses: actions/checkout@v3 - uses: cachix/install-nix-action@v22 with: - extra_nix_config: "system-features = nixos-test benchmark big-parallel kvm" + extra_nix_config: | + system-features = nixos-test recursive-nix benchmark big-parallel kvm + extra-experimental-features = recursive-nix nix-command flakes - run: nix build - run: nix build .#doc - run: nix fmt . -- --check @@ -20,7 +22,9 @@ jobs: - uses: actions/checkout@v3 - uses: cachix/install-nix-action@v22 with: - extra_nix_config: "system-features = nixos-test benchmark big-parallel kvm" + extra_nix_config: | + system-features = nixos-test recursive-nix benchmark big-parallel kvm + extra-experimental-features = recursive-nix nix-command flakes - run: nix build - run: nix build .#doc - run: nix fmt . -- --check diff --git a/pkgs/agenix.nix b/pkgs/agenix.nix index 0d35bf4..d2fcbce 100644 --- a/pkgs/agenix.nix +++ b/pkgs/agenix.nix @@ -9,29 +9,49 @@ substituteAll, ageBin ? "${rage}/bin/rage", shellcheck, -}: -stdenv.mkDerivation rec { - pname = "agenix"; - version = "0.15.0"; - src = substituteAll { - inherit ageBin version; - jqBin = "${jq}/bin/jq"; - nixInstantiate = "${nix}/bin/nix-instantiate"; - mktempBin = "${mktemp}/bin/mktemp"; - diffBin = "${diffutils}/bin/diff"; - src = ./agenix.sh; - }; - dontUnpack = true; +}: let + bin = "${placeholder "out"}/bin/agenix"; +in + stdenv.mkDerivation rec { + pname = "agenix"; + version = "0.15.0"; + src = substituteAll { + inherit ageBin version; + jqBin = "${jq}/bin/jq"; + nixInstantiate = "${nix}/bin/nix-instantiate"; + mktempBin = "${mktemp}/bin/mktemp"; + diffBin = "${diffutils}/bin/diff"; + src = ./agenix.sh; + }; + dontUnpack = true; + doInstallCheck = true; + installCheckInputs = [shellcheck]; + postInstallCheck = '' + shellcheck ${bin} + ${bin} -h | grep ${version} - doCheck = true; - checkInputs = [shellcheck]; - postCheck = '' - shellcheck $src - ''; + mkdir -p /tmp/home/.ssh + cp -r "${../example}" /tmp/home/secrets + chmod -R u+rw /tmp/home/secrets + export HOME=/tmp/home + ( + umask u=rw,g=r,o=r + cp ${../example_keys/user1.pub} $HOME/.ssh/id_ed25519.pub + chown $UID $HOME/.ssh/id_ed25519.pub + ) + ( + umask u=rw,g=,o= + cp ${../example_keys/user1} $HOME/.ssh/id_ed25519 + chown $UID $HOME/.ssh/id_ed25519 + ) - installPhase = '' - install -D $src ${placeholder "out"}/bin/agenix - ''; + cd /tmp/home/secrets + test $(${bin} -d secret1.age) = "hello" + ''; - meta.description = "age-encrypted secrets for NixOS"; -} + installPhase = '' + install -D $src ${bin} + ''; + + meta.description = "age-encrypted secrets for NixOS"; + } From 08dc5068e6b5f8c985dba6490c219ea439f48ac1 Mon Sep 17 00:00:00 2001 From: Ryan Mulligan Date: Fri, 22 Dec 2023 07:48:06 -0800 Subject: [PATCH 54/73] Revert "contrib: add direct tests for agenix " --- .github/workflows/ci.yaml | 8 ++--- pkgs/agenix.nix | 66 ++++++++++++++------------------------- 2 files changed, 25 insertions(+), 49 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 29be3c5..b53a3f2 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -9,9 +9,7 @@ jobs: - uses: actions/checkout@v3 - uses: cachix/install-nix-action@v22 with: - extra_nix_config: | - system-features = nixos-test recursive-nix benchmark big-parallel kvm - extra-experimental-features = recursive-nix nix-command flakes + extra_nix_config: "system-features = nixos-test benchmark big-parallel kvm" - run: nix build - run: nix build .#doc - run: nix fmt . -- --check @@ -22,9 +20,7 @@ jobs: - uses: actions/checkout@v3 - uses: cachix/install-nix-action@v22 with: - extra_nix_config: | - system-features = nixos-test recursive-nix benchmark big-parallel kvm - extra-experimental-features = recursive-nix nix-command flakes + extra_nix_config: "system-features = nixos-test benchmark big-parallel kvm" - run: nix build - run: nix build .#doc - run: nix fmt . -- --check diff --git a/pkgs/agenix.nix b/pkgs/agenix.nix index d2fcbce..0d35bf4 100644 --- a/pkgs/agenix.nix +++ b/pkgs/agenix.nix @@ -9,49 +9,29 @@ substituteAll, ageBin ? "${rage}/bin/rage", shellcheck, -}: let - bin = "${placeholder "out"}/bin/agenix"; -in - stdenv.mkDerivation rec { - pname = "agenix"; - version = "0.15.0"; - src = substituteAll { - inherit ageBin version; - jqBin = "${jq}/bin/jq"; - nixInstantiate = "${nix}/bin/nix-instantiate"; - mktempBin = "${mktemp}/bin/mktemp"; - diffBin = "${diffutils}/bin/diff"; - src = ./agenix.sh; - }; - dontUnpack = true; - doInstallCheck = true; - installCheckInputs = [shellcheck]; - postInstallCheck = '' - shellcheck ${bin} - ${bin} -h | grep ${version} +}: +stdenv.mkDerivation rec { + pname = "agenix"; + version = "0.15.0"; + src = substituteAll { + inherit ageBin version; + jqBin = "${jq}/bin/jq"; + nixInstantiate = "${nix}/bin/nix-instantiate"; + mktempBin = "${mktemp}/bin/mktemp"; + diffBin = "${diffutils}/bin/diff"; + src = ./agenix.sh; + }; + dontUnpack = true; - mkdir -p /tmp/home/.ssh - cp -r "${../example}" /tmp/home/secrets - chmod -R u+rw /tmp/home/secrets - export HOME=/tmp/home - ( - umask u=rw,g=r,o=r - cp ${../example_keys/user1.pub} $HOME/.ssh/id_ed25519.pub - chown $UID $HOME/.ssh/id_ed25519.pub - ) - ( - umask u=rw,g=,o= - cp ${../example_keys/user1} $HOME/.ssh/id_ed25519 - chown $UID $HOME/.ssh/id_ed25519 - ) + doCheck = true; + checkInputs = [shellcheck]; + postCheck = '' + shellcheck $src + ''; - cd /tmp/home/secrets - test $(${bin} -d secret1.age) = "hello" - ''; + installPhase = '' + install -D $src ${placeholder "out"}/bin/agenix + ''; - installPhase = '' - install -D $src ${bin} - ''; - - meta.description = "age-encrypted secrets for NixOS"; - } + meta.description = "age-encrypted secrets for NixOS"; +} From 5c1198a352b5fac579be4aff9cd9cbfe2920c282 Mon Sep 17 00:00:00 2001 From: Ryan Mulligan Date: Wed, 20 Dec 2023 13:13:47 -0800 Subject: [PATCH 55/73] feat: switch from rage to age Why === * Someone said age works better with password protected keys, requiring entering the password less often. * We switched to rage from age in https://github.com/ryantm/agenix/commit/07ce686870f52a7397164b755d721bc85f110aac because it was limiting recipients to 20. This was fixed https://github.com/FiloSottile/age/issues/139 What changed === * Switch from rage back to age (the reference implementation) in all the spots * Update the docs to show how to switch back to Rage * Skip keys that are empty files, which fixes the integration test. --- README.md | 10 +++++----- doc/notices.md | 2 +- doc/reference.md | 2 +- modules/age-home.nix | 2 +- modules/age.nix | 5 +++-- pkgs/agenix.nix | 4 ++-- 6 files changed, 13 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 998bfc2..b66df95 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ All files in the Nix store are readable by any system user, so it is not a suita ## Notices -* Password-protected ssh keys: since the underlying tool age/rage do not support ssh-agent, password-protected ssh keys do not work well. For example, if you need to rekey 20 secrets you will have to enter your password 20 times. +* Password-protected ssh keys: since age does not support ssh-agent, password-protected ssh keys do not work well. For example, if you need to rekey 20 secrets you will have to enter your password 20 times. ## Installation @@ -487,7 +487,7 @@ Example of a secret with a name different from its attrpath: #### `age.ageBin` `age.ageBin` the string of the path to the `age` binary. Usually, you -don't need to change this. Defaults to `rage/bin/rage`. +don't need to change this. Defaults to `age/bin/age`. Overriding `age.ageBin` example: @@ -587,13 +587,13 @@ improved upon by reading the identities from the age file.) #### Overriding age binary -The agenix CLI uses `rage` by default as its age implemenation, you -can use the reference implementation `age` with Flakes like this: +The agenix CLI uses `age` by default as its age implemenation, you +can use the `rage` implementation with Flakes like this: ```nix {pkgs,agenix,...}:{ environment.systemPackages = [ - (agenix.packages.x86_64-linux.default.override { ageBin = "${pkgs.age}/bin/age"; }) + (agenix.packages.x86_64-linux.default.override { ageBin = "${pkgs.rage}/bin/rage"; }) ]; } ``` diff --git a/doc/notices.md b/doc/notices.md index 5dcc5a9..a186507 100644 --- a/doc/notices.md +++ b/doc/notices.md @@ -1,3 +1,3 @@ # Notices {#notices} -* Password-protected ssh keys: since the underlying tool age/rage do not support ssh-agent, password-protected ssh keys do not work well. For example, if you need to rekey 20 secrets you will have to enter your password 20 times. +* Password-protected ssh keys: since age does not support ssh-agent, password-protected ssh keys do not work well. For example, if you need to rekey 20 secrets you will have to enter your password 20 times. diff --git a/doc/reference.md b/doc/reference.md index 614b0c9..2419559 100644 --- a/doc/reference.md +++ b/doc/reference.md @@ -166,7 +166,7 @@ Example of a secret with a name different from its attrpath: ### `age.ageBin` `age.ageBin` the string of the path to the `age` binary. Usually, you -don't need to change this. Defaults to `rage/bin/rage`. +don't need to change this. Defaults to `age/bin/age`. Overriding `age.ageBin` example: diff --git a/modules/age-home.nix b/modules/age-home.nix index 99ccccb..9577eb8 100644 --- a/modules/age-home.nix +++ b/modules/age-home.nix @@ -155,7 +155,7 @@ with lib; let ''; in { options.age = { - package = mkPackageOption pkgs "rage" {}; + package = mkPackageOption pkgs "age" {}; secrets = mkOption { type = types.attrsOf secretType; diff --git a/modules/age.nix b/modules/age.nix index 9468779..86c4447 100644 --- a/modules/age.nix +++ b/modules/age.nix @@ -69,6 +69,7 @@ with lib; let IDENTITIES=() for identity in ${toString cfg.identityPaths}; do test -r "$identity" || continue + test -s "$identity" || continue IDENTITIES+=(-i) IDENTITIES+=("$identity") done @@ -189,9 +190,9 @@ in { options.age = { ageBin = mkOption { type = types.str; - default = "${pkgs.rage}/bin/rage"; + default = "${pkgs.age}/bin/age"; defaultText = literalExpression '' - "''${pkgs.rage}/bin/rage" + "''${pkgs.age}/bin/age" ''; description = '' The age executable to use. diff --git a/pkgs/agenix.nix b/pkgs/agenix.nix index 0d35bf4..7ce6de2 100644 --- a/pkgs/agenix.nix +++ b/pkgs/agenix.nix @@ -1,13 +1,13 @@ { lib, stdenv, - rage, + age, jq, nix, mktemp, diffutils, substituteAll, - ageBin ? "${rage}/bin/rage", + ageBin ? "${age}/bin/age", shellcheck, }: stdenv.mkDerivation rec { From eb3b5cf4fd2e177a7dd020b1f62eb1219ad78f68 Mon Sep 17 00:00:00 2001 From: Ryan Mulligan Date: Wed, 20 Dec 2023 21:16:28 -0800 Subject: [PATCH 56/73] update nixpkgs --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 8ec68d4..881ab8e 100644 --- a/flake.lock +++ b/flake.lock @@ -43,11 +43,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1677676435, - "narHash": "sha256-6FxdcmQr5JeZqsQvfinIMr0XcTyTuR7EXX0H3ANShpQ=", + "lastModified": 1703013332, + "narHash": "sha256-+tFNwMvlXLbJZXiMHqYq77z/RfmpfpiI3yjL6o/Zo9M=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "a08d6979dd7c82c4cef0dcc6ac45ab16051c1169", + "rev": "54aac082a4d9bb5bbc5c4e899603abfb76a3f6d6", "type": "github" }, "original": { From bd86c0696163645c78fcecc2724a94d74840e2f7 Mon Sep 17 00:00:00 2001 From: Ryan Mulligan Date: Wed, 20 Dec 2023 21:24:28 -0800 Subject: [PATCH 57/73] fix doc build --- flake.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index 1df2443..fc900d8 100644 --- a/flake.nix +++ b/flake.nix @@ -38,7 +38,7 @@ packages = eachSystem (system: { agenix = nixpkgs.legacyPackages.${system}.callPackage ./pkgs/agenix.nix {}; - doc = nixpkgs.legacyPackages.${system}.callPackage ./pkgs/doc.nix {}; + doc = nixpkgs.legacyPackages.${system}.callPackage ./pkgs/doc.nix {inherit self;}; default = self.packages.${system}.agenix; }); From 58017c0c932f24787d60c93c54df2b0f7bb9a766 Mon Sep 17 00:00:00 2001 From: Ryan Mulligan Date: Wed, 20 Dec 2023 21:29:26 -0800 Subject: [PATCH 58/73] update inputs --- flake.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/flake.lock b/flake.lock index 881ab8e..5d6236c 100644 --- a/flake.lock +++ b/flake.lock @@ -7,11 +7,11 @@ ] }, "locked": { - "lastModified": 1673295039, - "narHash": "sha256-AsdYgE8/GPwcelGgrntlijMg4t3hLFJFCRF3tL5WVjA=", + "lastModified": 1700795494, + "narHash": "sha256-gzGLZSiOhf155FW7262kdHo2YDeugp3VuIFb4/GGng0=", "owner": "lnl7", "repo": "nix-darwin", - "rev": "87b9d090ad39b25b2400029c64825fc2a8868943", + "rev": "4b9b83d5a92e8c1fbfd8eb27eda375908c11ec4d", "type": "github" }, "original": { @@ -28,11 +28,11 @@ ] }, "locked": { - "lastModified": 1682203081, - "narHash": "sha256-kRL4ejWDhi0zph/FpebFYhzqlOBrk0Pl3dzGEKSAlEw=", + "lastModified": 1703113217, + "narHash": "sha256-7ulcXOk63TIT2lVDSExj7XzFx09LpdSAPtvgtM7yQPE=", "owner": "nix-community", "repo": "home-manager", - "rev": "32d3e39c491e2f91152c84f8ad8b003420eab0a1", + "rev": "3bfaacf46133c037bb356193bd2f1765d9dc82c1", "type": "github" }, "original": { From b6aa6180dbfc8a5df9296db264c01b49eb173f4c Mon Sep 17 00:00:00 2001 From: Ryan Mulligan Date: Wed, 20 Dec 2023 21:38:19 -0800 Subject: [PATCH 59/73] test removing installer --- flake.nix | 1 - 1 file changed, 1 deletion(-) diff --git a/flake.nix b/flake.nix index fc900d8..8a44679 100644 --- a/flake.nix +++ b/flake.nix @@ -49,7 +49,6 @@ inherit system; modules = [ ./test/integration_darwin.nix - "${darwin.outPath}/pkgs/darwin-installer/installer.nix" home-manager.darwinModules.home-manager { home-manager = { From 23d4d5d29193a5ab1b1514375d578209a6119392 Mon Sep 17 00:00:00 2001 From: Ryan Mulligan Date: Wed, 20 Dec 2023 21:56:50 -0800 Subject: [PATCH 60/73] maybe this fixes darwin checks? --- flake.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/flake.nix b/flake.nix index 8a44679..4f4ccc7 100644 --- a/flake.nix +++ b/flake.nix @@ -49,6 +49,8 @@ inherit system; modules = [ ./test/integration_darwin.nix + darwin.darwinModules.simple + home-manager.darwinModules.home-manager { home-manager = { From 6ce42cc7684cb59b3a9390256f68909d7dfb9af0 Mon Sep 17 00:00:00 2001 From: Nathan Henrie Date: Thu, 21 Dec 2023 13:15:07 -0700 Subject: [PATCH 61/73] Fix CI for darwin nix-darwin is detecting `/etc/nix/nix.conf` from the cachix/install-nix-action and refusing to overwrite it, failing the nix-darwin activation and therefore the rest of CI. This commit `mv`s the existing `nix.conf` to avoid the above, and then adds support for new-style nix commands and flakes to the nix-darwin configuration to allow their subsequent use in CI. It also removes the nix config from the `cachix/install-nix-action` step, which was duplicated effort since we are blowing it away with nix-darwin anyway. Relevant: - https://github.com/LnL7/nix-darwin/issues/149 --- .github/workflows/ci.yaml | 20 ++++++++++++++------ flake.nix | 4 +++- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index b53a3f2..6345634 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -15,21 +15,29 @@ jobs: - run: nix fmt . -- --check - run: nix flake check tests-darwin: - runs-on: macos-11 + runs-on: macos-12 steps: - uses: actions/checkout@v3 - - uses: cachix/install-nix-action@v22 + - uses: cachix/install-nix-action@v24 with: - extra_nix_config: "system-features = nixos-test benchmark big-parallel kvm" + extra_nix_config: | + system-features = nixos-test recursive-nix benchmark big-parallel kvm + extra-experimental-features = recursive-nix nix-command flakes - run: nix build - run: nix build .#doc - run: nix fmt . -- --check - run: nix flake check - name: "Install nix-darwin module" run: | - system=$(nix build --no-link --print-out-paths .#checks.x86_64-darwin.integration) - ${system}/activate-user - sudo ${system}/activate + # https://github.com/ryantm/agenix/pull/230#issuecomment-1867025385 + + sudo mv /etc/nix/nix.conf{,.bak} + nix \ + --extra-experimental-features 'nix-command flakes' \ + build .#checks.x86_64-darwin.integration + + ./result/activate-user + sudo ./result/activate - name: "Test nix-darwin module" run: | sudo /run/current-system/sw/bin/agenix-integration diff --git a/flake.nix b/flake.nix index 4f4ccc7..587138e 100644 --- a/flake.nix +++ b/flake.nix @@ -49,7 +49,9 @@ inherit system; modules = [ ./test/integration_darwin.nix - darwin.darwinModules.simple + + # Allow new-style nix commands in CI + {nix.extraOptions = "experimental-features = nix-command flakes";} home-manager.darwinModules.home-manager { From bc24f2e510e045c70203cff80d34a8a024c38dec Mon Sep 17 00:00:00 2001 From: Ryan Mulligan Date: Sat, 23 Dec 2023 14:43:03 -0800 Subject: [PATCH 62/73] Revert "Revert "contrib: add direct tests for agenix "" This reverts commit 08dc5068e6b5f8c985dba6490c219ea439f48ac1. --- .github/workflows/ci.yaml | 4 ++- pkgs/agenix.nix | 66 +++++++++++++++++++++++++-------------- 2 files changed, 46 insertions(+), 24 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 6345634..e48411b 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -9,7 +9,9 @@ jobs: - uses: actions/checkout@v3 - uses: cachix/install-nix-action@v22 with: - extra_nix_config: "system-features = nixos-test benchmark big-parallel kvm" + extra_nix_config: | + system-features = nixos-test recursive-nix benchmark big-parallel kvm + extra-experimental-features = recursive-nix nix-command flakes - run: nix build - run: nix build .#doc - run: nix fmt . -- --check diff --git a/pkgs/agenix.nix b/pkgs/agenix.nix index 7ce6de2..e8f59ae 100644 --- a/pkgs/agenix.nix +++ b/pkgs/agenix.nix @@ -9,29 +9,49 @@ substituteAll, ageBin ? "${age}/bin/age", shellcheck, -}: -stdenv.mkDerivation rec { - pname = "agenix"; - version = "0.15.0"; - src = substituteAll { - inherit ageBin version; - jqBin = "${jq}/bin/jq"; - nixInstantiate = "${nix}/bin/nix-instantiate"; - mktempBin = "${mktemp}/bin/mktemp"; - diffBin = "${diffutils}/bin/diff"; - src = ./agenix.sh; - }; - dontUnpack = true; +}: let + bin = "${placeholder "out"}/bin/agenix"; +in + stdenv.mkDerivation rec { + pname = "agenix"; + version = "0.15.0"; + src = substituteAll { + inherit ageBin version; + jqBin = "${jq}/bin/jq"; + nixInstantiate = "${nix}/bin/nix-instantiate"; + mktempBin = "${mktemp}/bin/mktemp"; + diffBin = "${diffutils}/bin/diff"; + src = ./agenix.sh; + }; + dontUnpack = true; + doInstallCheck = true; + installCheckInputs = [shellcheck]; + postInstallCheck = '' + shellcheck ${bin} + ${bin} -h | grep ${version} - doCheck = true; - checkInputs = [shellcheck]; - postCheck = '' - shellcheck $src - ''; + mkdir -p /tmp/home/.ssh + cp -r "${../example}" /tmp/home/secrets + chmod -R u+rw /tmp/home/secrets + export HOME=/tmp/home + ( + umask u=rw,g=r,o=r + cp ${../example_keys/user1.pub} $HOME/.ssh/id_ed25519.pub + chown $UID $HOME/.ssh/id_ed25519.pub + ) + ( + umask u=rw,g=,o= + cp ${../example_keys/user1} $HOME/.ssh/id_ed25519 + chown $UID $HOME/.ssh/id_ed25519 + ) - installPhase = '' - install -D $src ${placeholder "out"}/bin/agenix - ''; + cd /tmp/home/secrets + test $(${bin} -d secret1.age) = "hello" + ''; - meta.description = "age-encrypted secrets for NixOS"; -} + installPhase = '' + install -D $src ${bin} + ''; + + meta.description = "age-encrypted secrets for NixOS"; + } From a23aa271bec82d3e962bafb994595c1c4a62b133 Mon Sep 17 00:00:00 2001 From: Ryan Mulligan Date: Sat, 23 Dec 2023 14:47:15 -0800 Subject: [PATCH 63/73] dev: reland add direct tests for agenix Why === * We'd like some tests for the CLI * Last time we tried it failed on macos What changed === * This time, we try to create the temp diretory in a way that works with macos too --- pkgs/agenix.nix | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/pkgs/agenix.nix b/pkgs/agenix.nix index e8f59ae..987d679 100644 --- a/pkgs/agenix.nix +++ b/pkgs/agenix.nix @@ -30,10 +30,15 @@ in shellcheck ${bin} ${bin} -h | grep ${version} - mkdir -p /tmp/home/.ssh - cp -r "${../example}" /tmp/home/secrets - chmod -R u+rw /tmp/home/secrets - export HOME=/tmp/home + HOME=$(mktemp -d 2>/dev/null || mktemp -d -t 'mytmpdir') + function cleanup { + rm -rf $HOME + } + trap "cleanup" 0 2 3 15 + + mkdir -p $HOME/.ssh + cp -r "${../example}" $HOME/secrets + chmod -R u+rw $HOME/secrets ( umask u=rw,g=r,o=r cp ${../example_keys/user1.pub} $HOME/.ssh/id_ed25519.pub @@ -45,7 +50,7 @@ in chown $UID $HOME/.ssh/id_ed25519 ) - cd /tmp/home/secrets + cd $HOME/secrets test $(${bin} -d secret1.age) = "hello" ''; From 1746e4f5ec0849a50c2b7b634b4b84e3e09c6f87 Mon Sep 17 00:00:00 2001 From: sternenseemann Date: Thu, 1 Feb 2024 13:30:22 +0100 Subject: [PATCH 64/73] agenix: fix installCheckPhase with Nix 2.3 As opposed to e.g. Nix 2.18, Nix 2.3 doesn't try to create a fallback store in $HOME if $NIX_STORE_DIR and $NIX_STATE_DIR aren't writable. --- pkgs/agenix.nix | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pkgs/agenix.nix b/pkgs/agenix.nix index 987d679..e51a5c6 100644 --- a/pkgs/agenix.nix +++ b/pkgs/agenix.nix @@ -30,9 +30,13 @@ in shellcheck ${bin} ${bin} -h | grep ${version} - HOME=$(mktemp -d 2>/dev/null || mktemp -d -t 'mytmpdir') + test_tmp=$(mktemp -d 2>/dev/null || mktemp -d -t 'mytmpdir') + export HOME="$test_tmp/home" + export NIX_STORE_DIR="$test_tmp/nix/store" + export NIX_STATE_DIR="$test_tmp/nix/var" + mkdir -p "$HOME" "$NIX_STORE_DIR" "$NIX_STATE_DIR" function cleanup { - rm -rf $HOME + rm -rf "$test_tmp" } trap "cleanup" 0 2 3 15 From 1f62cef426d11795bdd588e507c2845df8434e32 Mon Sep 17 00:00:00 2001 From: kraem Date: Wed, 7 Feb 2024 08:48:49 +0100 Subject: [PATCH 65/73] fix: update docs for 5c1198a --- doc/overriding-age-binary.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/overriding-age-binary.md b/doc/overriding-age-binary.md index 9ee3a11..34ae232 100644 --- a/doc/overriding-age-binary.md +++ b/doc/overriding-age-binary.md @@ -1,12 +1,12 @@ # Overriding age binary {#overriding-age-binary} -The agenix CLI uses `rage` by default as its age implemenation, you -can use the reference implementation `age` with Flakes like this: +The agenix CLI uses `age` by default as its age implemenation, you +can use the `rage` implementation with Flakes like this: ```nix {pkgs,agenix,...}:{ environment.systemPackages = [ - (agenix.packages.x86_64-linux.default.override { ageBin = "${pkgs.age}/bin/age"; }) + (agenix.packages.x86_64-linux.default.override { ageBin = "${pkgs.rage}/bin/rage"; }) ]; } ``` From 3fd98a2c3b1a9a46341a975ee0a4b488194ecb56 Mon Sep 17 00:00:00 2001 From: oluceps Date: Wed, 3 Apr 2024 00:59:47 +0800 Subject: [PATCH 66/73] doc: fix wrong ssh-keyscan usage --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b66df95..c6d8f8d 100644 --- a/README.md +++ b/README.md @@ -273,7 +273,7 @@ e.g. inside your `flake.nix` file: * your local computer usually in `~/.ssh`, e.g. `~/.ssh/id_ed25519.pub`. * from a running target machine with `ssh-keyscan`: ```ShellSession - $ ssh-keyscan @ + $ ssh-keyscan ... ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKzxQgondgEYcLpcPdJLrTdNgZ2gznOHCAxMdaceTUT1 ... ``` From 2c1d1fb13467184256e352b00ed2d89046c16cd6 Mon Sep 17 00:00:00 2001 From: Ellis Gibbons Date: Fri, 12 Apr 2024 17:50:07 -0400 Subject: [PATCH 67/73] fix: allow for newlines in keys --- pkgs/agenix.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkgs/agenix.sh b/pkgs/agenix.sh index b78b441..3d0415e 100644 --- a/pkgs/agenix.sh +++ b/pkgs/agenix.sh @@ -171,7 +171,9 @@ function edit { ENCRYPT=() while IFS= read -r key do - ENCRYPT+=(--recipient "$key") + if [ -n "$key" ]; then + ENCRYPT+=(--recipient "$key") + fi done <<< "$KEYS" REENCRYPTED_DIR=$(@mktempBin@ -d) From 07479c2e7396acaaaac5925483498154034ea80a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Tue, 7 May 2024 19:12:37 +0200 Subject: [PATCH 68/73] update link to nixos wiki (#258) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c6d8f8d..0f5b4f2 100644 --- a/README.md +++ b/README.md @@ -205,7 +205,7 @@ You can run the CLI tool ad-hoc without installing it: nix run github:ryantm/agenix -- --help ``` -But you can also add it permanently into a [NixOS module](https://nixos.wiki/wiki/NixOS_modules) +But you can also add it permanently into a [NixOS module](https://wiki.nixos.org/wiki/NixOS_modules) (replace system "x86_64-linux" with your system): ```nix From 63a57d8dfb16daca46721fdc505fee7cc2eb4cbf Mon Sep 17 00:00:00 2001 From: hansemschnokeloch Date: Thu, 9 May 2024 22:25:29 +0200 Subject: [PATCH 69/73] Fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0f5b4f2..f65144e 100644 --- a/README.md +++ b/README.md @@ -445,7 +445,7 @@ Example: #### `age.secrets..symlink` `age.secrets..symlink` is a boolean. If true (the default), -secrets are symlinked to `age.secrets..path`. If false, secerts +secrets are symlinked to `age.secrets..path`. If false, secrets are copied to `age.secrets..path`. Usually, you want to keep this as true, because it secure cleanup of secrets no longer used. (The symlink will still be there, but it will be broken.) If From 08ed896eb60cf738d5a1d12cb713663d6e83db9b Mon Sep 17 00:00:00 2001 From: oddlama Date: Sat, 20 May 2023 01:51:05 +0200 Subject: [PATCH 70/73] fix: always treat link destinations as files to ensure error when destination is a directory. This can happen if for example a secret is used in the initrd, which materializes it as a directory, which then causes agenix to silently create an incorrect link when switching to stage2. This ensures that agenix will abort with an error. --- modules/age-home.nix | 4 ++-- modules/age.nix | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/age-home.nix b/modules/age-home.nix index 9577eb8..7c1051f 100644 --- a/modules/age-home.nix +++ b/modules/age-home.nix @@ -61,7 +61,7 @@ with lib; let ${optionalString secretType.symlink '' # shellcheck disable=SC2193,SC2050 - [ "${secretType.path}" != "${cfg.secretsDir}/${secretType.name}" ] && ln -sfn "${cfg.secretsDir}/${secretType.name}" "${secretType.path}" + [ "${secretType.path}" != "${cfg.secretsDir}/${secretType.name}" ] && ln -sfT "${cfg.secretsDir}/${secretType.name}" "${secretType.path}" ''} ''; @@ -76,7 +76,7 @@ with lib; let _agenix_generation="$(basename "$(readlink "${cfg.secretsDir}")" || echo 0)" (( ++_agenix_generation )) echo "[agenix] symlinking new secrets to ${cfg.secretsDir} (generation $_agenix_generation)..." - ln -sfn "${cfg.secretsMountPoint}/$_agenix_generation" "${cfg.secretsDir}" + ln -sfT "${cfg.secretsMountPoint}/$_agenix_generation" "${cfg.secretsDir}" (( _agenix_generation > 1 )) && { echo "[agenix] removing old secrets (generation $(( _agenix_generation - 1 )))..." diff --git a/modules/age.nix b/modules/age.nix index 86c4447..e49d9d8 100644 --- a/modules/age.nix +++ b/modules/age.nix @@ -88,7 +88,7 @@ with lib; let mv -f "$TMP_FILE" "$_truePath" ${optionalString secretType.symlink '' - [ "${secretType.path}" != "${cfg.secretsDir}/${secretType.name}" ] && ln -sfn "${cfg.secretsDir}/${secretType.name}" "${secretType.path}" + [ "${secretType.path}" != "${cfg.secretsDir}/${secretType.name}" ] && ln -sfT "${cfg.secretsDir}/${secretType.name}" "${secretType.path}" ''} ''; @@ -103,7 +103,7 @@ with lib; let _agenix_generation="$(basename "$(readlink ${cfg.secretsDir})" || echo 0)" (( ++_agenix_generation )) echo "[agenix] symlinking new secrets to ${cfg.secretsDir} (generation $_agenix_generation)..." - ln -sfn "${cfg.secretsMountPoint}/$_agenix_generation" ${cfg.secretsDir} + ln -sfT "${cfg.secretsMountPoint}/$_agenix_generation" ${cfg.secretsDir} (( _agenix_generation > 1 )) && { echo "[agenix] removing old secrets (generation $(( _agenix_generation - 1 )))..." From 760751b6d1d07bebad4b836c4014f6f20bc6db68 Mon Sep 17 00:00:00 2001 From: KREYREN Date: Wed, 19 Jun 2024 15:37:53 +0000 Subject: [PATCH 71/73] README: Add warning about HNDL and PQS in theat model --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index f65144e..67718f6 100644 --- a/README.md +++ b/README.md @@ -618,6 +618,8 @@ authentication code (MAC) like other implementations like GPG or [sops](https://github.com/Mic92/sops-nix) have, however this was left out for simplicity in `age`. +Additionally you should only encrypt secrets that you are able to make useless in the event that they are decrypted in the future and be ready to rotate them periodically as [age](https://github.com/FiloSottile/age) is [as of 19th June 2024 NOT Post-Quantum Safe](https://github.com/FiloSottile/age/discussions/231#discussioncomment-3092773) and so in case the threat actor can access your encrypted keys e.g. via their use in a public repository then they can utilize the strategy of [Harvest Now, Decrypt Later](https://en.wikipedia.org/wiki/Harvest_now,_decrypt_later) to store your keys now for later decryption including the case where a major vulnerability is found that would expose the secrets. See https://github.com/FiloSottile/age/issues/578 for details. + ## Contributing * The main branch is protected against direct pushes From 40012e5ed4affbb5af7ea59770eb8d8fd1a5991b Mon Sep 17 00:00:00 2001 From: Farid Zakaria Date: Mon, 29 Jul 2024 08:09:40 -0700 Subject: [PATCH 72/73] Remove import for NixOS/HM modules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When using `files` on a NixOS option in the `nix repl` it fails to follow the attribute of agenix module. Discussing with @roberth has explained that this is a "common bug" on account of mis-using the `import` for modules. From what I understand, the `import` statement brings it into the current context so you lose the attribute of where it's defined. Here is what I currently see: ``` nix-repl> options.age.ageBin.files [ "/nix/store/8kpmdb63f5i9mwdyirqki7hvvglgy1va-source/machines/nyx/configuration.nix" ] ``` After this change, the value in agenix is reported instead. ``` ❯ nix repl --extra-experimental-features 'flakes repl-flake' \ --override-input agenix /home/fmzakari/code/github.com/ryantm/agenix . nix-repl> options.age.ageBin.files [ "/nix/store/99gc8rhgw43k201k34pshcsygdvbhmpy-source/modules/age.nix" ] ``` --- flake.nix | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.nix b/flake.nix index 587138e..3a68940 100644 --- a/flake.nix +++ b/flake.nix @@ -23,13 +23,13 @@ }: let eachSystem = nixpkgs.lib.genAttrs (import systems); in { - nixosModules.age = import ./modules/age.nix; + nixosModules.age = ./modules/age.nix; nixosModules.default = self.nixosModules.age; - darwinModules.age = import ./modules/age.nix; + darwinModules.age = ./modules/age.nix; darwinModules.default = self.darwinModules.age; - homeManagerModules.age = import ./modules/age-home.nix; + homeManagerModules.age = ./modules/age-home.nix; homeManagerModules.default = self.homeManagerModules.age; overlays.default = import ./overlay.nix; From e3413992fbc77176f5827949fd3d645288db1223 Mon Sep 17 00:00:00 2001 From: Jacob Hrbek Date: Sat, 10 Aug 2024 05:05:53 +0200 Subject: [PATCH 73/73] age-home: Use curly-brackets for XDG_RUNTIME_DIR To avoid having to do https://github.com/colonelpanic8/dotfiles/blob/4fd99eae63ef0ea8e6f23ac5e152352cb0fc1bf0/nixos/secrets.nix#L25C9-L29C116 while using agenix in user services. --- modules/age-home.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/age-home.nix b/modules/age-home.nix index 7c1051f..c8135b2 100644 --- a/modules/age-home.nix +++ b/modules/age-home.nix @@ -146,12 +146,12 @@ with lib; let baseDir = if isDarwin then "$(getconf DARWIN_USER_TEMP_DIR)" - else "$XDG_RUNTIME_DIR"; + else "\${XDG_RUNTIME_DIR}"; in "${baseDir}/${dir}"; userDirectoryDescription = dir: literalExpression '' - "$XDG_RUNTIME_DIR"/${dir} on linux or "$(getconf DARWIN_USER_TEMP_DIR)"/${dir} on darwin. + "${XDG_RUNTIME_DIR}"/${dir} on linux or "$(getconf DARWIN_USER_TEMP_DIR)"/${dir} on darwin. ''; in { options.age = {