feature | start-date | author | co-authors | shepherd-team | shepherd-leader | related-issues |
---|---|---|---|---|---|---|
private-derivations |
2023-02-03 |
poscat |
(find a buddy later to help out with the RFC) |
(names, to be nominated and accepted by RFC steering committee) |
(name to be appointed by RFC steering committee) |
(will contain links to implementation PRs) |
This RFC proposes to add a special type of derivation called private derivation, which, upon being built, will have their file permissions set to 000/111 instead of the usual 444/555.
In short: This RFC mainly concerns with how to safely manage credentials using nix (as opposed to using impure methods like manually copying them over) on NixOS.
The world readability of nix store means that, to safely store credentials, they must first somehow be encrypted before written into the store. They also need to be decrypted before the services are started.
This is less than ideal because one needs to setup a key (which is stored as plaintext on disk) on every machine just to prevent unauthorized users from seeting the credentials.
Furthermore, if encryption is done before the evaluation of the system configuration (as is the case with agenix and sops-nix), then the nixos module system cannot be utilized to generate configs that contain credentials and one must write them manually.
All of this can be prevented if we added the ability to make derivation outputs as not readable by anyone other than root, by setting the file mode to 111 (directories and executables) or 000 (regular files). We can then use a trustworthy credential manager, for example systemd with its LoadCredential=
, to distribute these derivations to the consumers safely.
We propose adding a noReadAccess
option to builtins.derivation
, which, when set to true, makes this derivation a private derivation.
The only difference between a private derivation and a normal derivation, apart
from the hash, is that upon instantiation and after building, the read bit of
the .drv
file and the output path will be removed (recursively).
In our design, the responsibility of access control is to be delegated to a separate process called the credential manager, which is a process with
the CAP_DAC_OVERRIDE
capability. This frees us from further bloating the store model.
On NixOS there are many modules where configurations that might contain sensitive information get written into nix store in plaintext (for example the wpa_supplicant module). Private derivations can solve this issue by
- Writing a helper function
writeTextPrivate
that functions similar towriteText
, but instead outputs a private derivation - Replace the
writeText
function withwriteTextPrivate
inside the module - Use
LoadCredential=
to load the private derivation - Replace the derivation output path with
%d/<credName>
(see systemd.exec)
- Adding private derivations further complicates the nix store model.
- In the intended use case, the credential remains plaintext at all time, and while the resulting derivation is unreadable, the inputs aren't. This means the necessary materials that can be used to reconstruct the credential are exposed during build until the inputs are garbage collected. A dedicated attacker can in theory monitor for file system changes, save the inputs before they are being GC'd and later reconstruct the final derivation.
- Supporting more complicated ACLs as described in this Nix issue.
- Storing private derivations in a separate store path, for example
/nix/private-store
that have its executable bit removed so that the hashes are not visible to non-root users.
How do we prevent the attacker from using nix copy
to simply copy out the
private derivation to another machine?
What changes are needed for binary cache providers such as nix-serve
to handle
private derivations?
The hash is still exposed to the attacker, which opens up some possible attacks. How does this impact the security?
It is not yet known how this might interact with content addressed paths.
What future work, if any, would be implied or impacted by this feature without being directly part of the work?