I am looking at different ways to manage secrets (private keys, passwords, etc.) that are needed in my NixOS configuration. There seem to be many different schemes to manage secrets but I am missing a good overview that directly compares the different schemes to one another. I am thinking about the typical “comparison of XXX programs” lists on Wikipedia.
The starting point for my investigation was that I wanted to use flakes but I did not want to store my wifi passwords in plain text in my git repository backing /etc/nixos. Until now I had the networking.wireless.networks setting in a file that was not tracked by git but that conflicts with the flake philosophy.
In general I think these properties of a scheme might be of interest:
Where do I put my secrets before building/deploying? In git, outside of git?
Is the secret encrypted before deploy/build? Which encryption is used?
When is the file decrypted? At evaluation time, at build time, at activation time, at runtime?
I did find some tools, hints and post about managing secrets for NixOS though:
Is there a good comparison explaining the requirements, constraints and aims of different solutions somewhere? I was not able to find one yet. Did I miss any important tools or solutions? Do you think it is worth wile to start a wiki page with such a comparison on the nixos wiki?
builtins.readFile and builtins.exec have a different scope. They are useful when you need secrets during the build but are forgotten when finished so they don’t end up in the Nix store.
Your last link combines that with NixOps keys so that secrets don’t have to be stored unencrypted on disk.
On a separate note, builtins.exec is impure and probably does not work with flakes.
I’ve some undocumented stuff based on the Nixops keys post in an extra builtins overlay which supports secrets with git-crypt, pass or sops.
And I use it to define nix options for building my nixos configurations or home manager profiles.
The nix plugin loading has changed recently, until I figure out what breaks, I need to switch back to builtins.exec with the command line option --option allow-unsafe-native-code-during-evaluation true
Both approaches (buitlins.extraBuiltins or builtins.exec) are working well BTW with flakes.
Actually, it seems that as an user of Yubikey using essentially PGP to sign/crypt/SSH (and not PIV Securing SSH with OpenPGP or PIV ), there are not so much “mature” options to use in combination with Flakes. Sops / Age are not compatible, or compatible at our own risk …
Hey, it’s great to have some discussion on this topic.
Can we be more ambitious and not just list all the options but actually pick some recommendations?
It might be good to really call out the brainstorming nature of that page and point to this thread. Otherwise the page might just contribute to the general noise and confusion that wikis create. We could instead aim to find some recommendations and add them to official documentation.
To continue the brainstorming, how many different operating methods do we actually need? How cells in the table represent useful scenarios?
By now others and I have indeed filled out big parts of the table in the wiki so my statement above is kind of outdated. I added a link back to this thread for you.
I did not start the wiki page to give a recommendation because I was actually looking for a recommendation. I did (and still not have) enough expertise to recommend any scheme for a particular use case. But I am happy to contribute to a discussion about these.
I designed the table in the wiki with columns about specific properties about the scheme/project which I though would be beneficial to future readers to make a more informed decision themselves.
If we have found some “common” or interesting scenarios we could add a section about these scenarios to the wiki page. But I think that is the kind of thing one puts into blog posts because every user will have some “non standard” conditions.
I join @chreekat’s call for use cases / scenarios: It would be interesting to get a list of the problems that you people solve with these projects. I will go first:
git:encrypted + store:plain
This is my main use cases which I have not yet solved really: store my wifi passwords in git encrypted (so that I can share my config repo publicly) and then decrypt them at build time. I do not care for my wifi passwords to be encrypted in the store.
A couple of comments.
It mentions agenix putting secrets in /run/secrets unencrypted.
It’s true, but agenix does give you the ability to set the owner. It does not give you the ability to set full permission though.
Regarding permissions, what is the best practice for permissions on secrets?
The recommended permissions on secrets depend on the use case / requirements. I think the common rule of thumb is
as few permissions as possible
as much as absolutely necessary
Examples (my interpretation of the principle)
If you have a daemon program that runs under one user only you could chown the relevant secret to that user and give it 600 or even 400 permissions. Compare for example man ssh_config under FILES.
If you have a secret that is shared by two (or more) programs that run under different users you could add a new unix group to the system (say new_group) chown the secrets file to new_group and give it (at least) 040 permissions and add the two users (which run the relevant programs) to the new_group.
Nice overall discussion, but I don’t think builtins.readfile or builtins.exec should be part of this list. Sure, in theory they can be used for secret management, but it should probably never be done in practice.
It also takes away from the simplicity of the comparison; See how for everything except the last point “is my secret in the nix store?” is a nice yes/no, but for this it’s “dunno, up to you”.
Thank you @lucc@ryantm for your work on the table!
Regarding best practices: I understand that sops-nix and agenix provide a mechanism for deployment of secrets to individual files (stored in /run/secrets by default), which is useful for modules with attributes that accept paths (e.g., hashedPasswordFile as opposed to the inline hashedPassword). For an inline secret (such as an environment variable storing an API key), however, is it safe to assume that it is necessary to either write a custom module (e.g., that adds shell script code called during shell initialization that reads the secret from its deployed path and sets the variable) or otherwise implement an external solution?
I think yes, you have to write something yourself unless your program expects a file(name) (e.g ssl keys in apache/nginx). I think the main behind many of these programs/solutions is to work around the fact that the nix store does not have secure permissions, which means they only think about files.
nixos-cn provides an interesting module nixos-cn/flakes sops module, that can be used together with sops-nix to render the decrypted secrets with templates.
Thanks for sharing! This looks to provide a great boost in UX for applicable sops-nix users. With age support also underway, sops-nix is shaping up to be a rather versatile solution.
For general management of file-based secrets, I’ve found the simplicity of agenix to be wonderfully refreshing but struggle to imagine how an equivalent module may be implemented without the use of sops.