WIP: implement a secret vars store in nixpkgs#370444
WIP: implement a secret vars store in nixpkgs#370444Lassulus wants to merge 2 commits intoNixOS:masterfrom
Conversation
|
How this compares to sops-nix? |
This enhances sops-nix, It is more of an upstream evolution of agenix-rekey. The gist is: This supports declaring how to generate the secrets you store in sops-nix in nixpkgs in the service module. I will add a declaration to a service soon, so it is easier to understand :) |
|
alright, removed some features to make the first implementation easier. |
3f0acbe to
339d2bc
Compare
|
This pull request has been mentioned on NixOS Discourse. There might be relevant details there: https://discourse.nixos.org/t/2025-01-08-clan-weekly-changelog/58501/1 |
|
This looks awesome, I'll have to play around with it a bit more to understand all the details. Have you already thought about how this would work when someone wants to use vars/secrets in the initrd in stage1? I imagine there could be some kind of hen-egg problem since the initrd will be generated pretty early on when a new generation is activated. The configured vars/secrets may probably not exist at that time, so this might require activating twice (?) |
|
I am curious about how it would work in stage 1 because AFAIK / is not mounted yet in this phase, so how it will read the host key to decrypt the secrets. |
The current code here doesn't run during activation but creates a script that can be run before activation. in clan.lol we solved that issue differently by having a requiredBy option which can take "activation". We would create the files necessary for activation beforehand in that case, but I think this adds too much complexity to this initial PR. Another problem is also the prompts which some vars can ask for. Having the generation run in the activation would make prompting for input very hard. So I think a better way would be to prompt in nixos-install/nixos-rebuild, so before running activation. But this is also something I will add in a later iteration :) |
This is not really in scope for this PR. The current on-machine implementation I added here, doesn't encrypt the secrets (yet?) so there should be no issue with stage-1. secrets can still be added to initrd if they are required to early boot and they should be available at that time. |
46b881c to
b6b0bed
Compare
There was a problem hiding this comment.
Can we add a value option or something similar to each prompt? I would like to be able to pre-define the value of the prompt to ensure that a user can always opt-in to a purely declarative configuration.
Btw, I currently don't understand when an author of a nixos module would decide to utilize a prompt instead of a generated random value. If I am writing a generator for my personal configuration, I can understand that I might want to use prompt for certain things - but when would I want to use a prompt in an upstream module and force interactivity on every user?
I'm just a little worried right now that introducing prompts without a way to declaratively set their value will sooner or later result in some generators being added to nixpkgs which depend on some form of interactivity. Therefore I'd like to have a way to specify the value of a prompt ahead of time, so the generation script can then just copy the specified file to prompts/$name instead of asking the user a question.
There was a problem hiding this comment.
we mainly use prompts for API keys, which we usually don't want to leak into the config. I guess we can think about pre declaring things, this would also make the testing code a bit easier which I worked on last time I touched that code. But this increases the surface every backend implementation has to take care of.
There was a problem hiding this comment.
These are currently not used by the on machine generator. Please ignore if that is still in the works :)
There was a problem hiding this comment.
I was thinking of removing them again, since the chmod/etc needs to happen after activation anyway and the user/group has to exist for that to work. But we use those definitions in clan.lol generators every now and then.
There was a problem hiding this comment.
If you remove these, everything will be owned by root I presume? That might make it hard to provision secrets for services which don't use LoadCredential
There was a problem hiding this comment.
ideally we want to fix that in the preStart or with tmpfiles.d but there could be certain cases where it would be necessary to do it beforehand. But maybe it would be better to have a separate activation-script/service for that, that takes a file somewhere on the filesystem and links/copies it to the target? but tmpfiles also does that already, so not sure
There was a problem hiding this comment.
I'm also not sure what would be the best action here, I guess I'm mostly just used to being able to specify it on the secret itself
This allows to create secrets (and public files) outside or inside the nix store in a more delclarative way. This is shipped with an example (working) implementation of an on-machine storage. The vars options can easily be used to implement custom backends and extend the behaviour to integrate with already existing solutions, like sops-nix or agenix.
jfly
left a comment
There was a problem hiding this comment.
I'm excited about this!
Sorry for the pretty myopic/nitpicky review, hopefully it's more useful than noisy. Here are some more high level thoughts:
-
The vars options can easily be used to implement custom backends and extend the behaviour to integrate with already existing solutions, like sops-nix or agenix.
I'd be interested to hear more about how this would work. Is the idea that a tool like sops-nix would provide a "backend" (similar to the on-machine backend you've included here?). It seems like the bulk of the logic in this PR is in the
generate-varsscript in the on-machine backend. Are all backends going to have a similar large chunk of logic (generating inputs, prompting, invoking generators with tempdirs, moving files)? -
I think it would be nice to lightly document just what a "backend" is (how to create one). For example, are they all supposed to provide a globally installed
generate-varscommand? -
I personally find the
generate-varsshell script to be surprisingly readable, but hard to confirm is robust. I think I saw a few missing shell escapes (I left comments about them). I'd feel more sure about things if we were instead generating a JSON config file for something written in python (or something) to deal with. (Similar to how sops-nix works under the hood if you are familiar with that.) -
I'm not very fond of the name vars anymore, so maybe a first change could be to come up with a better name
I personally am not bothered by the word "vars". I assume this isn't called "secrets" because these things sometimes have a secret and a public part (as in your syncthing example)?
I did find the use of the word "var" to be a little confusing while reading the code. Specifically, the relationship between a "var", a "generator" and a "file". IIUC, a "var" has multiple "files" in it, and each file may be secret or public. A "generator" can generate these files. But what's the difference between a var and a generator? They sort of seem to be the same thing. I also saw the word "fact" floating around. I assume that's an old name, or something from clan?
There was a problem hiding this comment.
nit: I'd feel better about this if we somehow asserted that the result of this is exactly 1 ID. I imagine things would get weird if this started returning multiple IDs or no ID at all.
Or, perhaps we could ask upstream syncthing if they'd be open to a change to syncthing generate to persist the id somewhere?
There was a problem hiding this comment.
| < "$out"/config.xml grep -oP '(?<=<device id=")[^"]+' | uniq > "$out"/syncthing.pub | |
| < "$out"/config.xml grep -m 1 -oP '(?<=<device id=")[^"]+' > "$out"/syncthing.pub |
should guarantee that it can yield no more than 1 id (uniq could still yield multiple, different ids). Due to the regex match not applying to empty strings, grep will already return rc 1 if no id is found, so the set -e in the reference backend should fetch it.
Might be a good idea to explicitly define that scripts are supposed to be run with set -e. :)
There was a problem hiding this comment.
nit:
| # if not, they are in an incosistent state and we bail out | |
| # if not, they are in an inconsistent state and we bail out |
There was a problem hiding this comment.
nit:
| Takes the file as an arument and returns maybe an attrset with should at least contain the `path` attribute. | |
| Takes the file as an argument and returns maybe an attrset with should at least contain the `path` attribute. |
There was a problem hiding this comment.
"fact" feels like a new piece of vocabulary here. Is this perhaps a term from clan? Should this be "generated file" (or "var")?
Related: what does "public" mean here? Are there also private facts?
There was a problem hiding this comment.
nit: This admonition seems a little bit at odds with the fact that this defaults to true. I personally would expect an option that's documented as "enable this only if ..." to default to false.
idea: Would it be better if we could get rid of this option and instead deduce it based on if the generated file is used as an input to other generators? Right now, I think the API here doesn't allow us to know that (generators depend on other generators). Perhaps it would be better for generators to depend on specific files from other generators? In addition to allowing us to remove this option, it might also move some issues "further left" (such as if generator B depends on generator A and assumes that generator A will create a certain file, but never actually creates the file. We could detect that error when running A, rather than waiting for B to (hopefully) fail.
There was a problem hiding this comment.
should be disable. We cannot deduce this automatically since a file can be referenced in another generator and still be needed to be deployed. We also cannot depend on single files by other generators, because files are stored as a unit. For the on-machine backend this option doesn't really make sense though, since we have no other storage location than where the files should be anyway.
There was a problem hiding this comment.
We cannot deduce this automatically since a file can be referenced in another generator and still be needed to be deployed.
Sorry, I'm not quite following this sentence: if a file is referenced in another generator, then doesn't that mean that it does need to be deployed?
I think I'm unclear on where the generators are supposed to run. Is it on the target machine, or on some other machine doing the build?
There was a problem hiding this comment.
well that depends on the backend that runs the generators. The declarations of the generators inside nixpkgs should be independent of where they run.
What I wanted to say, is that a generator can be a dependency of another generator but the secret file that is depended on can still be required by the system to be present during runtime. for example ssh-ca, where we would generate a certificate and a private key in a generator and later have a ssh key be generated that needs the private key to sign the public key, so it gets trusted automatically. In that case we would need to deploy the certificate from the first generator to the machine and the public key, private key and certificate from the second generator.
There was a problem hiding this comment.
OK, I think that makes sense.
I think the part I'm missing is how these generated files actually get "wired up" to running services. Is that present in the syncthing example in this PR? (I don't think it is).
If we have some way of expressing "this file created by this generator is needed by this service" (which feels like something we'd have to do anyway), then wouldn't that let us deduce if the file should be deployed to the machine?
There was a problem hiding this comment.
There was a problem hiding this comment.
Ah, that makes sense, thanks! For the record, the reason I was asking this question was because I was trying to understand what mechanism would ensure that services would get restarted when secrets change. In other words, is the vars interface missing something like sops-nix's restartUnits functionality to annotate this relationship, or perhaps some other mechanism to ensure restarts (such as requiring that vars backends always generate "input addressed secret paths").
You, me, @a-kenji, @Kranzes (and others) spent some time discussing this last week. My summary of the conversation:
- Generators that store secrets completely "out of band" (such as the on machine backend in this PR) cannot possibly implement something like "input addressed secret paths".
- We don't need to expose an additional api similar to sops-nix's
restartUnitsfunctionality, because we can already achieve this with systemd functionality.
(set up a unit to watch the file, trigger a restart of our service based on that)- We could propose a patch to systemd to have it automatically watch the files that that
LoadCredentialpoints at and restart them.- @Kranzes pointed out that this wouldn't be possible for systemd to do if
LoadCredentialpoints at a socket. Still, this does feel like something that should be supported in systemd... somehow.
- @Kranzes pointed out that this wouldn't be possible for systemd to do if
- We could propose a patch to systemd to have it automatically watch the files that that
There was a problem hiding this comment.
Generators that store secrets completely "out of band" (such as the on machine backend in this PR) cannot possibly implement something like "input addressed secret paths".
Upon further reflection, I think it's a little more nuanced than this. It's hard to be concrete about this because the precise context in which generators are invoked is not yet specified. But, you could imagine that invocation of generators is done in a way such that we store a "generation id" and are able to inject that id into the string interpolation that produces secret paths. This implies a couple things:
- There's some mechanism for storing and fetching the secret "generation id" that is readable at nix evaluation time. Either files in a repo, or module args injected "impurely". This would be a change to the vars api as in this PR.
- It would not be possible to rotate secrets completely out of band. You'd have to deploy an updated nixos configuration. Lame, but still better than the "reboot your machine to pull in updated secrets" that we have with the current state of things + this PR.
There was a problem hiding this comment.
Does this need shell escaping? (could file.path have spaces in it?)
There was a problem hiding this comment.
Does this need shell escaping? Could gen.name contain a double quote? I think so.
There was a problem hiding this comment.
Not double quotes as currently all names have the type lib.types.strMatching "[a-zA-Z0-9:_\\.-]*", but ending a name with \ would break this line. Quoting using ' would alleviate this, but for robustness sake, escaping most injected values seems preferable to me.
There was a problem hiding this comment.
This '${file.name}' feels like a "poor man's escaping". I'm pretty sure that file.name could itself contain a ', so I don't think this works. Can we use lib.escapeShellArg here instead?
There was a problem hiding this comment.
nit: This is unnecessary because of the trap 'rm -rf $out' EXIT up above, right?
There was a problem hiding this comment.
ah, the trap only runs on exit, it's better to clean up the files if they are no longer needed earlier. the trap is just in case the script exists unexpectedly
There was a problem hiding this comment.
I'm not understanding the glue between this generator and the syncthing service. How do the files created by this generator actually get wired up into the running service?
There was a problem hiding this comment.
Hope I am not misreading your question, but the pem files are seemingly simply used to set conventional nixos options such as on line 639-641 in this example.
The device id in syncthing.pub does not seem to be used in the example, but only stored. Presumably for outside use just as scripted setups of other devices to sync with.
yes basically every implementation has to take care of executing the scripts and storing the generated files in the location, also deployment of those files needs to be taken care of by a backend.
no, the generate-vars is mostly only useful in this usecase, since generating and deploying of files happens at the same time. Otherwise I would do deployment in activation. But we cannot prompt for inputs there. So this was moved into a script that can be manually run at any time.
sure, you can just dump the vars config from nix with json. But that increases the complexity of the on-machine implementation, I want a simple one that people can read and understand how to adapt. sops-nix probably would interact more directly with the vars instead of shell scripts.
yeah, fact is the old name for vars. we can't really call them generators, because that is already used by other parts of nixos. but in the end vars are generators that generate secret or public files :D |
|
pushed some changes, so some of the nitpicks should be more or less solved now. primarily restricted the allowed characters in certain names, so we can make escaping easier. |
|
Possible names for vars could possibly be:
Would be happy if someone has more inputs on this. But what this would be is some form of key/var/fact-generator / generating mechanism. |
| elif [ $all_files_missing = true ] ; then | ||
|
|
||
| # prompts | ||
| prompts=$(mktemp -d) |
There was a problem hiding this comment.
We should slap umask 077 in places...
|
|
||
| # outputs | ||
| out=$(mktemp -d) | ||
| trap 'rm -rf $out' EXIT |
There was a problem hiding this comment.
Traps override each other. Confer (trap 'echo a' EXIT; trap 'echo b' EXIT).
The way it stands, unless the script exits prematurely, only the very final 'rm -rf $out' gets executed, leaving all the other $outs, and all $ins and $promptss unremoved. This issue becomes apparent when declaring variables readonly.
One possible solution would be to spawn a subshell for each generator, e.g.:
...
elif [ $all_files_missing = true ] ; then
(
prompts=$(mktemp -d)
in=$(mktemp -d)
out=$(mktemp -d)
readonly prompts in out # optional assertion
trap 'rm -fR "$prompts" "$in" "$out"' EXIT
export prompts in out
...
)
fi
There was a problem hiding this comment.
It's also possible to have an array of cleanup function: https://github.com/Mic92/dotfiles/blob/4ce52e794985cf159e69e259e8b63d2f7cadfa22/pkgs/merge-when-green/script.sh#L10
| prompts=$(mktemp -d) | ||
| trap 'rm -rf $prompts' EXIT | ||
| export prompts | ||
| mkdir -p "$prompts" |
There was a problem hiding this comment.
Not double quotes as currently all names have the type lib.types.strMatching "[a-zA-Z0-9:_\\.-]*", but ending a name with \ would break this line. Quoting using ' would alleviate this, but for robustness sake, escaping most injected values seems preferable to me.
|
|
||
| # move the files to the correct location | ||
| ${lib.concatMapStringsSep "\n" (file: '' | ||
| OUT_FILE="$OUT_DIR"/${if file.secret then "secret" else "public"}/${file.generator}/${file.name} |
There was a problem hiding this comment.
Can we somehow reuse vars.settings.fileModule here? I see how we do want OUT_DIR to be overridable, but reconstructing this path it multiple times feels wrong to me.
| user-home-mode = handleTest ./user-home-mode.nix {}; | ||
| ustreamer = handleTest ./ustreamer.nix {}; | ||
| uwsgi = handleTest ./uwsgi.nix {}; | ||
| vars = handleTest ./vars.nix {}; |
There was a problem hiding this comment.
This placed seemed pretty ordered until this line got added. Or is all-tests.nix also just kindasorted like module-list.nix :)
Why not? I see that #370444 (comment) says
but I fail to see it being used as an option in NixOS by both looking the manual and by trying to define (and use) |
|
would this method also support templating techniques like https://github.com/Mic92/sops-nix#templates or https://github.com/jhillyerd/agenix-template? |
@KiaraGrouwstra, I believe the way you'd do this with vars is to define a secret (the "templated secret") that depends on other secret(s). That alone wouldn't give you a templating language like in the examples you've linked to (I think that's out of scope here), but you could then invoke your preferred templating language when generating the "templated secret". I don't think this PR has support for dependencies between vars. I'm not sure if the plan is to add that to this PR, or do it in a subsequent PR. |
There was a problem hiding this comment.
Thank you so much for this, I'd love having such a feature upstream and think this would at the very least be good step forward!
I've left a few comments, but feel free to ignore all of them (@Kranzes comment regarding umasks and @4z3 about traps overriding each other as well as the eval error are the only 3 i noticed, that should be blockers IMO).
I know you've got a lot on your plate but hope you'll continue working on this or let us know if not, so someone could pick this up, @Lassulus
(Personally not too fond of bash as a language for something that entails so much string & path manipulation, but that's the authors choice ;))
There was a problem hiding this comment.
Hope I am not misreading your question, but the pem files are seemingly simply used to set conventional nixos options such as on line 639-641 in this example.
The device id in syncthing.pub does not seem to be used in the example, but only stored. Presumably for outside use just as scripted setups of other devices to sync with.
There was a problem hiding this comment.
| < "$out"/config.xml grep -oP '(?<=<device id=")[^"]+' | uniq > "$out"/syncthing.pub | |
| < "$out"/config.xml grep -m 1 -oP '(?<=<device id=")[^"]+' > "$out"/syncthing.pub |
should guarantee that it can yield no more than 1 id (uniq could still yield multiple, different ids). Due to the regex match not applying to empty strings, grep will already return rc 1 if no id is found, so the set -e in the reference backend should fetch it.
Might be a good idea to explicitly define that scripts are supposed to be run with set -e. :)
| '') (lib.attrValues gen.files)} | ||
|
|
||
| if [ $all_files_missing = false ] && [ $all_files_present = false ] ; then | ||
| echo "Inconsistent state for generator: {gen.name}" |
There was a problem hiding this comment.
| echo "Inconsistent state for generator: {gen.name}" | |
| echo "Inconsistent state for generator: ${gen.name}" |
(unless this some ultra-obscure bash syntax i am not aware of ;)
There was a problem hiding this comment.
I sadly think that "Inconsistent state for generator: ${gen.name}" is a pretty bad error message here. I think it should at least try to communicate to the user what exactly is wrong, i.e. which file was found / not found. Ideally it would communicate something actionable as well, i.e:
You can try purging ''${OUT_DIR:-${cfg.fileLocation}}, after making sure it contains nothing you want to keep
I understand that it's annoying to implement in bash, but I am not sure that bash is the right choice of language here in the first place tbh.
roberth
left a comment
There was a problem hiding this comment.
Without an accompanying section in the NixOS manual, you're kind of leaving me guessing here, so I hope my comments make sense.
| { | ||
| options.vars = { | ||
| settings = { | ||
| fileModule = lib.mkOption { |
There was a problem hiding this comment.
Maybe
| fileModule = lib.mkOption { | |
| allFiles = lib.mkOption { |
| }: | ||
| { | ||
| options.vars = { | ||
| settings = { |
There was a problem hiding this comment.
Not RFC 42. What's the thought behind this name?
There was a problem hiding this comment.
I understand it's the provider-specific settings, similar in spirit to how SelfHostBlocks does it, except here it's (IMO correctly) in terms of modules.
| A set of files to generate. | ||
| The generator 'script' is expected to produce exactly these files under $out. | ||
| ''; | ||
| defaultText = "attrs of files"; |
There was a problem hiding this comment.
| defaultText = "attrs of files"; | |
| defaultText = literalMD "attrs of files"; |
Not sure.
| default = { }; | ||
| }; | ||
| }; | ||
| generators = lib.mkOption { |
There was a problem hiding this comment.
This contains both generators (executables), as well as their target files (calls of those executables).
Since those are in a 1:many relationship (potentially), this part of the option tree should be named after the "target files" instead (or whichever term those are described with).
There was a problem hiding this comment.
It seems that you have an insofar unnamed domain entity for groups of files that are generated together, and perhaps its name should be put here.
There was a problem hiding this comment.
hmm, the idea was, that one generator can create many files, so the files are below the generators in the hierarchy. Files belong to a generator in that they need to be generated by one, not sure where else in the option tree they could live
There was a problem hiding this comment.
Without an accompanying section in the NixOS manual, you're kind of leaving me guessing here, so I hope my comments make sense.
|
This pull request has been mentioned on NixOS Discourse. There might be relevant details there: https://discourse.nixos.org/t/vars-a-framework-for-managing-secrets-and-computed-values/62411/2 |
|
This pull request has been mentioned on NixOS Discourse. There might be relevant details there: https://discourse.nixos.org/t/vars-a-framework-for-managing-secrets-and-computed-values/62411/6 |
|
there is also a flake now to test this out (or PR against, instead of doing code suggestions here) https://github.com/Lassulus/vars I will mainly develop this PR there and at a later point bring over the changes made there into this PR again |
|
This pull request has been mentioned on NixOS Discourse. There might be relevant details there: https://discourse.nixos.org/t/vars-a-framework-for-managing-secrets-and-computed-values/62411/14 |
| prompt_value=$(cat) | ||
| ''; | ||
| }; | ||
| generate-vars = pkgs.writeShellApplication { |
There was a problem hiding this comment.
If I understand correctly, this script may be run on the deployer host, which can be a different platform.
| generate-vars = pkgs.writeShellApplication { | |
| generate-vars = deployerPkgs: deployerPkgs.writeShellApplication { |
It is then up to the caller to pass a suitable pkgs instance that works for the deployer host.
EDIT: this is of course not the complete solution because the script contents still refer to pkgs. Use a separate file and callPackage to avoid a footgun.
| generate-vars = pkgs.writeShellApplication { | |
| generate-vars = { deployerPkgs }: deployerPkgs.callPackage ./generate-vars.nix { inherit cfg; }; |
There was a problem hiding this comment.
As a test case, you could add aarch64-darwin to this and run the checks on darwin.
There was a problem hiding this comment.
usually we overwrite pkgs of the system where the vars are defined if the caller architecture is different from the target systems architecture, since this also supports string context references to pkgs and the vars definitons want to live next to the service definitions.
There was a problem hiding this comment.
I recommend against that. It blurs the line too much.
- You'll be evaluating a NixOS configuration that "runs on" macOS, which is unexpected. It may work out for you anyway, thanks to laziness, but if vars calls someone's code that reads something from the rest of the NixOS config (accidentally or otherwise), they'll get strange errors like "util-linux is not supported on darwin" or worse.
- Overriding is a code smell.
- Overriding is in general less efficient than evaluating the right thing in one go, especially if it's a NixOS config.
Explicit is good. By making this a proper parameter, readers and module authors have at least an idea of what's going on, and you don't rely on the overriding magic to never hit them in the face.
| lib.types.submodule (file: { | ||
| imports = [ | ||
| config.vars.settings.fileModule | ||
| ]; | ||
| options = { | ||
| name = lib.mkOption { | ||
| type = lib.types.strMatching "[a-zA-Z0-9:_\\.-]*"; | ||
| description = '' | ||
| name of the generated file | ||
| ''; | ||
| readOnly = true; | ||
| default = file.config._module.args.name; |
There was a problem hiding this comment.
| lib.types.submodule (file: { | |
| imports = [ | |
| config.vars.settings.fileModule | |
| ]; | |
| options = { | |
| name = lib.mkOption { | |
| type = lib.types.strMatching "[a-zA-Z0-9:_\\.-]*"; | |
| description = '' | |
| name of the generated file | |
| ''; | |
| readOnly = true; | |
| default = file.config._module.args.name; | |
| lib.types.submodule ({ name, ... }: { | |
| imports = [ | |
| config.vars.settings.fileModule | |
| ]; | |
| options = { | |
| name = lib.mkOption { | |
| type = lib.types.strMatching "[a-zA-Z0-9:_\\.-]*"; | |
| description = '' | |
| name of the generated file | |
| ''; | |
| readOnly = true; | |
| default = name; |
This seems to be a much more idiomatic way of writing it. That piece of code had confused the hell out of me, but maybe I'm just used to different things.
There was a problem hiding this comment.
Only changing it to default = file.name would work to the same effect, but I still find it unusual.
| This name will be used to refer to the generator in other generators. | ||
| ''; | ||
| readOnly = true; | ||
| default = generator.config._module.args.name; |
There was a problem hiding this comment.
| default = generator.config._module.args.name; | |
| default = generator.name; |
|
This pull request has been mentioned on NixOS Discourse. There might be relevant details there: https://discourse.nixos.org/t/zurich-25-05-zhf-hackathon-report/64882/1 |
|
This pull request has been mentioned on NixOS Discourse. There might be relevant details there: |
|
if i understood correctly (?), clan's motivation for vars involved cross-node communication as facilitated by e.g. at the same time, the contracts PR (#432529) offers tooling around handling of secrets agnostic to storage back-ends as well, raising questions on their potential integration. |
This allows to create secrets (and public files) outside or inside the nix store in a more delclarative way. This is shipped with an example (working) implementation of an on-machine storage. The vars options can easily be used to implement custom backends and extend the behaviour to integrate with already existing solutions, like sops-nix or agenix.
I wanted to upstream this, since we use code similiar to this in https://clan.lol now for a while. Happy to discuss it a bit more. I'm not very fond of the name vars anymore, so maybe a first change could be to come up with a better name :)
I'm not sure if this code is working yet, I will try to develop it a bit more in the next coming days, but I wanted to get this out earlier so other people can throw in some feedback already :)