oddlama / agenix-rekey

An agenix extension adding secret generation and automatic rekeying using a YubiKey or master-identity
MIT License
197 stars 16 forks source link

Unset option `age.rekey.generatedSecretsDir` results in error message "Cannot generate as it isn't a direct subpath of the flake directory <dir>, meaning this script cannot determine its true origin!" when running `agenix generate` #23

Closed Lukas-C closed 3 months ago

Lukas-C commented 3 months ago

Hello,

first of all, thank you for your work on this project, I have been happily using it for the past year in several personal deployments where managing secrets would have been a lot less convenient otherwise! Throughout my time working with the project I have collected some minor issues, which I thought I should share with you.

Recently I was restructuring a project and used that as an opportunity to regenerate some secrets. However, upon running

nix run ".#agenix-rekey.apps.x86_64-linux.generate" --show-trace

I was met with the following output:

error:
       … while calling the 'derivationStrict' builtin
         at <nix/derivation-internal.nix>:9:12:
            8|
            9|   strict = derivationStrict drvAttrs;
             |            ^
           10|

       … while evaluating derivation 'agenix-generate'
         whose name attribute is located at /nix/store/52yawfmb2rz0sm07px5zcrgv3y78v27v-source/pkgs/stdenv/generic/make-derivation.nix:348:7

       … while evaluating attribute 'text' of derivation 'agenix-generate'
         at /nix/store/52yawfmb2rz0sm07px5zcrgv3y78v27v-source/pkgs/build-support/trivial-builders/default.nix:148:17:
          147|     runCommand name
          148|       { inherit text executable checkPhase allowSubstitutes preferLocalBuild;
             |                 ^
          149|         passAsFile = [ "text" ];

       … from call site
         at /nix/store/3951347g3cqyyd3g9ijp4w59wvz4bk0s-source/apps/generate.nix:237:18:
          236|       shopt -s nullglob
          237|       for f in ${pkgs.lib.concatMapStrings (
             |                  ^
          238|       x:

       … while calling 'concatMapStrings'
         at /nix/store/52yawfmb2rz0sm07px5zcrgv3y78v27v-source/lib/strings.nix:60:25:
           59|   */
           60|   concatMapStrings = f: list: concatStrings (map f list);
             |                         ^
           61|

       … while calling the 'concatStringsSep' builtin
         at /nix/store/52yawfmb2rz0sm07px5zcrgv3y78v27v-source/lib/strings.nix:60:31:
           59|   */
           60|   concatMapStrings = f: list: concatStrings (map f list);
             |                               ^
           61|

       … while calling anonymous lambda
         at /nix/store/3951347g3cqyyd3g9ijp4w59wvz4bk0s-source/apps/generate.nix:238:7:
          237|       for f in ${pkgs.lib.concatMapStrings (
          238|       x:
             |       ^
          239|         escapeShellArg (relativeToFlake x.config.age.rekey.generatedSecretsDir) + "/* "

       … from call site
         at /nix/store/3951347g3cqyyd3g9ijp4w59wvz4bk0s-source/apps/generate.nix:239:9:
          238|       x:
          239|         escapeShellArg (relativeToFlake x.config.age.rekey.generatedSecretsDir) + "/* "
             |         ^
          240|     ) (attrValues nodes)}; do

       … while calling 'escapeShellArg'
         at /nix/store/52yawfmb2rz0sm07px5zcrgv3y78v27v-source/lib/strings.nix:443:20:
          442|   */
          443|   escapeShellArg = arg: "'${replaceStrings ["'"] ["'\\''"] (toString arg)}'";
             |                    ^
          444|

       … while calling the 'replaceStrings' builtin
         at /nix/store/52yawfmb2rz0sm07px5zcrgv3y78v27v-source/lib/strings.nix:443:29:
          442|   */
          443|   escapeShellArg = arg: "'${replaceStrings ["'"] ["'\\''"] (toString arg)}'";
             |                             ^
          444|

       … while evaluating the third argument passed to builtins.replaceStrings

       … while calling the 'toString' builtin
         at /nix/store/52yawfmb2rz0sm07px5zcrgv3y78v27v-source/lib/strings.nix:443:61:
          442|   */
          443|   escapeShellArg = arg: "'${replaceStrings ["'"] ["'\\''"] (toString arg)}'";
             |                                                             ^
          444|

       … from call site
         at /nix/store/3951347g3cqyyd3g9ijp4w59wvz4bk0s-source/apps/generate.nix:239:25:
          238|       x:
          239|         escapeShellArg (relativeToFlake x.config.age.rekey.generatedSecretsDir) + "/* "
             |                         ^
          240|     ) (attrValues nodes)}; do

       … while calling 'relativeToFlake'
         at /nix/store/3951347g3cqyyd3g9ijp4w59wvz4bk0s-source/apps/generate.nix:34:21:
           33|
           34|   relativeToFlake = filePath: let
             |                     ^
           35|     fileStr = builtins.unsafeDiscardStringContext (toString filePath);

       … in the condition of the assert statement
         at /nix/store/3951347g3cqyyd3g9ijp4w59wvz4bk0s-source/apps/generate.nix:37:5:
           36|   in
           37|     assert assertMsg (hasPrefix userFlakeDir fileStr) "Cannot generate ${fileStr} as it isn't a direct subpath of the flake directory ${userFlakeDir}, meaning this script cannot determine its true origin!";
             |     ^
           38|       "." + removePrefix userFlakeDir fileStr;

       … from call site
         at /nix/store/3951347g3cqyyd3g9ijp4w59wvz4bk0s-source/apps/generate.nix:37:12:
           36|   in
           37|     assert assertMsg (hasPrefix userFlakeDir fileStr) "Cannot generate ${fileStr} as it isn't a direct subpath of the flake directory ${userFlakeDir}, meaning this script cannot determine its true origin!";
             |            ^
           38|       "." + removePrefix userFlakeDir fileStr;

       … while calling 'assertMsg'
         at /nix/store/52yawfmb2rz0sm07px5zcrgv3y78v27v-source/lib/asserts.nix:23:5:
           22|     # Message to throw in case `pred` fails
           23|     msg:
             |     ^
           24|     pred || builtins.throw msg;

       … in the right operand of the OR (||) operator
         at /nix/store/52yawfmb2rz0sm07px5zcrgv3y78v27v-source/lib/asserts.nix:24:10:
           23|     msg:
           24|     pred || builtins.throw msg;
             |          ^
           25|

       … while calling the 'throw' builtin
         at /nix/store/52yawfmb2rz0sm07px5zcrgv3y78v27v-source/lib/asserts.nix:24:13:
           23|     msg:
           24|     pred || builtins.throw msg;
             |             ^
           25|

       error: Cannot generate  as it isn't a direct subpath of the flake directory /nix/store/h9fl1a96wgcsfp4qd7hqnqczi2zp8xva-source, meaning this script cannot determine its true origin!

After some digging, I noticed

       … from call site
         at /nix/store/3951347g3cqyyd3g9ijp4w59wvz4bk0s-source/apps/generate.nix:239:25:
          238|       x:
          239|         escapeShellArg (relativeToFlake x.config.age.rekey.generatedSecretsDir) + "/* "
             |                         ^
          240|     ) (attrValues nodes)}; do

       … while calling 'relativeToFlake'
         at /nix/store/3951347g3cqyyd3g9ijp4w59wvz4bk0s-source/apps/generate.nix:34:21:
           33|
           34|   relativeToFlake = filePath: let
             |                     ^
           35|     fileStr = builtins.unsafeDiscardStringContext (toString filePath);

which seems to refer to the cleanup of orphaned files added in this commit: https://github.com/oddlama/agenix-rekey/commit/8d42875722b04a56dc76e45f5ca0f2670d389f6f

Once I realized this, I noticed that I had not set age.rekey.generatedSecretsDir for any of my hosts, as I prefer to keep the generated secrets adjacent to the regular secrets, which means that I usually specify age.secrets.<secretName>.rekeyFile manually. I think it would be great to be able to retain this approach by guarding the removal of orphaned files by checking first if age.rekey.generatedSecretsDir is set for any of the available nodes. I would of course be happy to attempt myself at a PR for what should probably be a relatively simple fix.

Thank you again for your work!

Edit: I fat-fingered and submitted only part of the issue description, updated the comment with the full information.

oddlama commented 3 months ago

I was met with the following output:

[...]
       error: Cannot generate  as it isn't a direct subpath of the flake directory /nix/store/h9fl1a96wgcsfp4qd7hqnqczi2zp8xva-source, meaning this script cannot determine its true origin!

which seems to refer to the cleanup of orphaned files added in this commit: 8d42875

Ah yes, good catch! Sorry for the inconveniences 😅

I think it would be great to be able to retain this approach by guarding the removal of orphaned files by checking first if age.rekey.generatedSecretsDir is set for any of the available nodes. I would of course be happy to attempt myself at a PR for what should probably be a relatively simple fix.

Absolutely, this logic should really only be included if age.rekey.generatedSecretsDir is actually set on a host. The fix should really just need to filter out any hosts that don't set the option. And of course I'd be happy to accept your PR if you are willing to make one!

Thank you again for your work!

❤️

Lukas-C commented 3 months ago

Perfect. I will get to work and send the PR when it's ready!