Skip to content

Commit

Permalink
Merge pull request #71 from ldesgoui/script-dependencies-attrs
Browse files Browse the repository at this point in the history
  • Loading branch information
oddlama authored Feb 4, 2025
2 parents a1dcdd2 + 9a5d2f3 commit 9585d1b
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 10 deletions.
41 changes: 37 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ can use to define our generation script.
| `lib` | Convenience access to the nixpkgs library |
| `pkgs` | The package set for the _host that is running the generation script_. Don't use any other packgage set in the script! |
| `file` | The actual path to the .age file that will be written after this function returns and the content is encrypted. Useful to write additional information to adjacent files. |
| `deps` | The list of all secret files from our `dependencies`. Each entry is a set of `{ name, host, file }`, corresponding to the secret `nixosConfigurations.${host}.age.secrets.${name}`. `file` is the true source location of the secret's `rekeyFile`. You can extract the plaintext with `${decrypt} ${escapeShellArg dep.file}`.
| `deps` | The list or attrset of all secret files from our `dependencies`. Each entry is a set of `{ name, host, file }`, corresponding to the secret `nixosConfigurations.${host}.age.secrets.${name}`. `file` is the true source location of the secret's `rekeyFile`. You can extract the plaintext with `${decrypt} ${escapeShellArg dep.file}`.
| `decrypt` | The base rage command that can decrypt secrets to stdout by using the defined `masterIdentities`.
| `...` | For future/unused arguments

Expand Down Expand Up @@ -390,6 +390,39 @@ which are also generated automatically:
}
```

You can also supply an attrset in `dependencies` and respectively receive an attrset in the `deps` parameter.
This can be easier to deal with than lists in some cases.

```nix
{
# Generate passwords for our app
age.secrets.smtp-password = {
rekeyFile = ./secrets/smtp-password.age;
generator.script = "alnum";
};
age.secrets.oidc-secret = {
rekeyFile = ./secrets/oidc-secret.age;
generator.script = "alnum";
};
# Generate a file in the .env format
age.secrets.env-secrets = {
rekeyFile = ./secrets/env-secrets.age;
generator = {
# Specify an attrset of dependencies as it's easier to select each one
dependencies = {
inherit (config.age.secrets) smtp-password oidc-secret;
};
script = { pkgs, lib, decrypt, deps, ... }: ''
printf 'SMTP_PASSWORD="%s"\n' $(${decrypt} ${lib.escapeShellArg deps.smtp-password.file})
printf 'OIDC_SECRET="%s"\n' $(${decrypt} ${lib.escapeShellArg deps.oidc-secret.file})
'';
};
};
}
```

## Using age instead of rage

If you don't want to use rage for some reason, you can specify a compatible
Expand Down Expand Up @@ -475,17 +508,17 @@ If defined, this generator will be used to bootstrap this secret's when it doesn

## `age.secrets.<name>.generator.dependencies`

| Type | `listOf unspecified` |
| Type | `oneOf [(listOf unspecified) (attrsOf unspecified)]` |
|-----|-----|
| Default | `[]` |
| Example | `[ config.age.secrets.basicAuthPw1 nixosConfigurations.machine2.config.age.secrets.basicAuthPw ]` |
| Example | `[ config.age.secrets.basicAuthPw1 nixosConfigurations.machine2.config.age.secrets.basicAuthPw ]` or `{ inherit (config.age.secrets) smtpPassword; }` |

Other secrets on which this secret depends. This guarantees that in the final
`agenix generate` script, all dependencies will be generated before
this secret is generated, allowing you to use their outputs via the passed `decrypt` function.

The given dependencies will be passed to the defined `script` via the `deps` parameter,
which will be a list of their true source locations (`rekeyFile`) in no particular order.
which will be a list or attrset of their true source locations (`rekeyFile`).

This should refer only to secret definitions from `config.age.secrets` that
have a generator. This is useful if you want to create derived secrets,
Expand Down
10 changes: 7 additions & 3 deletions apps/generate.nix
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ let
head
length
mapAttrs
mapAttrsToList
removePrefix
stringsWithDeps
warnIf
Expand All @@ -39,6 +40,9 @@ let
"Cannot generate ${fileStr} as it isn't a direct subpath of the flake directory ${userFlakeDir}, meaning this script cannot determine its true origin!";
"." + removePrefix userFlakeDir fileStr;

mapListOrAttrs = f: x: if builtins.isList x then map f x else mapAttrs (_: f) x;
mapListOrAttrValues = f: x: if builtins.isList x then map f x else mapAttrsToList (_: f) x;

# Finds the host where the given secret is defines. Matches
# based on secret.id and secret.rekeyFile. If multiple matches
# exist, a warning is issued and the first is returned.
Expand Down Expand Up @@ -73,7 +77,7 @@ let
file = sourceFile;
name = secretName;
decrypt = ageMasterDecrypt;
deps = flip map secret.generator.dependencies (dep: {
deps = flip mapListOrAttrs secret.generator.dependencies (dep: {
host = findHost dep;
name = dep.id;
file = relativeToFlake dep.rekeyFile;
Expand Down Expand Up @@ -116,7 +120,7 @@ let
dep_mtimes=(
1 # Have at least one entry
${concatStringsSep "\n" (
flip map contextSecret.secret.generator.dependencies (
flip mapListOrAttrValues contextSecret.secret.generator.dependencies (
dep: "\"$(stat -c %Y ${escapeShellArg (relativeToFlake dep.rekeyFile)} 2>/dev/null || echo 1)\""
)
)}
Expand Down Expand Up @@ -153,7 +157,7 @@ let
stages = flip mapAttrs secretsWithContext (
_: contextSecret:
stringsWithDeps.fullDepEntry (secretGenerationCommand contextSecret) (
map (x: relativeToFlake x.rekeyFile) (
mapListOrAttrValues (x: relativeToFlake x.rekeyFile) (
filter (dep: dep.generator != null) contextSecret.secret.generator.dependencies
)
)
Expand Down
11 changes: 8 additions & 3 deletions modules/agenix-rekey.nix
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,12 @@ let
generatorType = types.submodule (submod: {
options = {
dependencies = mkOption {
type = types.listOf types.unspecified;
type =
with types;
oneOf [
(listOf unspecified)
(attrsOf unspecified)
];
example = literalExpression ''[ config.age.secrets.basicAuthPw1 nixosConfigurations.machine2.config.age.secrets.basicAuthPw ]'';
default = [ ];
description = ''
Expand All @@ -104,7 +109,7 @@ let
this secret is generated, allowing you use their outputs via the passed `decrypt` function.
The given dependencies will be passed to the defined `script` via the `deps` parameter,
which will be a list of their true source locations (`rekeyFile`) in no particular order.
which will be a list or attrset of their true source locations (`rekeyFile`).
This should refer only to secret definitions from `config.age.secrets` that
have a generator. This is useful if you want to create derived secrets,
Expand All @@ -127,7 +132,7 @@ let
file, # The actual path to the .age file that will be written after
# this function returns and the content is encrypted.
# Useful to write additional information to adjacent files.
deps, # The list of all secret files from our `dependencies`.
deps, # The list or attrset of all secret files from our `dependencies`.
# Each entry is a set of `{ name, host, file }`, corresponding to
# the secret `nixosConfigurations.''${host}.age.secrets.''${name}`.
# `file` is the true source location of the secret's `rekeyFile`.
Expand Down

0 comments on commit 9585d1b

Please sign in to comment.