New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support private files in the Nix store #8

Open
edolstra opened this Issue Apr 23, 2012 · 65 comments

Comments

Projects
None yet
@edolstra
Member

edolstra commented Apr 23, 2012

Sometimes it's desirable to have files in the Nix store that are not world-readable, such as configuration files containing passwords. This could be implemented as follows:

  • Private files are marked as such by giving them a certain magic prefix to the name, e.g. p:. When these are registered, Nix should make them readable only to root (permission 0700) and the calling user (using an ACL). (The Nix daemon knows the uid of the caller.)
  • The permissions on a derivation should be propagated to the output paths. Also, private derivations should be run with a umask of 0077.
  • If another user attempts to register an already valid private file, then its uid should be added to the ACL of the file. If it's a derivation, then the uid should also be added to the ACLs of any valid outputs.
  • Store operations that read files from the store (such as exportPath()) should check that the caller is in the ACL of the file.

So, for instance, if the root user builds a NixOS configuration containing some private files, then those files are only readable as root. If Alice builds the same configuration, then those files will be readable by root and Alice. If Alice then uses Charon to copy the configuration to a remote machine (i.e. nix-copy-closure --to root@remote ...), then the files on the remote machine will only be readable by root.

This model only allows files that are private to the instantiating user and root. So it doesn't support specifying a file that is readable only to the sshd user. But that's probably not necessary.

@ghost ghost assigned edolstra Apr 23, 2012

@shlevy

This comment has been minimized.

Member

shlevy commented May 10, 2012

A few questions:

  • Does this potentially break nixos-rebuild build as non-root?
  • Is there any way to tell as a user that I've attempted to register an already-valid file? For example, if root and I both use the same password file for a particular program, will I be able to tell when I add that file to the store that root has the same one?
  • Would there be a reasonable way for a NixOS-managed service to know how to find per-user private files?
@edolstra

This comment has been minimized.

Member

edolstra commented May 16, 2012

  • It shouldn't break non-root nixos-rebuilds since any private files would be owned by (or accessible to) the calling user.
  • If a user attempts to register an already-valid file, he would be added to the ACL to the file. So he could conclude from this that root needed the same file.
  • What kind of service, and why would it need to do that?
@viric

This comment has been minimized.

Member

viric commented May 31, 2012

What about the build users? Should they be in the ACLs?

@chaoflow

This comment has been minimized.

Member

chaoflow commented Jul 26, 2012

Being able to tell that somebody else generated the same private file sounds like a no-go for me.

What do you think about including the user the file is private to, i.e. the one who created it, to be included in the hashing (for example an empty file with the user's name in the output)? So even if root, Alice and Charon all want a file with the same content, they would end up with 3 different files.

@shlevy

This comment has been minimized.

Member

shlevy commented Jul 26, 2012

The problem with including the user in the hashing is you may eventually share stores, but it's better than nothing. Maybe a wrapper function in nixpkgs that hashed in a GUID read from some personal file? So I have ~/.nixpkgs/guid and when I run pkgs.lib.privateFile 'mypass' "passw0rd" it creates a file whose first line is passw0rd and the second line is the guid?

@edolstra

This comment has been minimized.

Member

edolstra commented Jul 26, 2012

Why is this a problem? If the secret stored in the file is sufficiently secure (i.e. random), then this won't happen. I mean, if your login password is "foobar", then other people may be able to find out. So just don't do that.

@shlevy

This comment has been minimized.

Member

shlevy commented Jul 26, 2012

Eh, it's not really a problem, at least not for nix itself. The security-paranoid folk can just make sure all of their private files have some unique identifier included.

@mornfall

This comment has been minimized.

mornfall commented Mar 31, 2013

Unless I am mistaken, private files in the store are sensitive to offline dictionary attacks, since you can learn the hash and you have a procedure to check your guesses. It is also conceivable, that a rainbow attack could be mounted against a particular version of nixpkgs. It would be advisable that any such private files contain significant salting, regardless of the "learn a secret by random collision" problem. A hypothetical lib.privateFile could then enforce that salting by taking a template string and substituting in the salt (which makes it possible to hide that salt in comments or other such convenient places).

@viric

This comment has been minimized.

Member

viric commented Mar 31, 2013

Yes, the sysadmin could care storing the salt somewhere only readable by the root user.

@domenkozar

This comment has been minimized.

Member

domenkozar commented May 27, 2013

There is a use case for pkgs.bacula that bacula user needs access to a configuration file with passwords (so non-root).

@MarcWeber

This comment has been minimized.

Contributor

MarcWeber commented Nov 30, 2013

Alternative quick & dirty solution which may be useful for some use cases: https://github.com/MarcWeber/nix/blob/experimental/write-file-hashed/.topmsg

@domenkozar

This comment has been minimized.

Member

domenkozar commented Feb 22, 2014

Better link to see @MarcWeber diff: https://github.com/MarcWeber/nix/compare/experimental;write-file-hashed

What's the current workaround for this if a config file includes a password?

@MarcWeber

This comment has been minimized.

Contributor

MarcWeber commented Feb 23, 2014

My version allows storing passwords in config, but instead of writing it to the store a path to a file containing it will be written to the store. The containing file can be read only by root. The complicated piece is that you have to write code which has to be run as root (eg when preparing config files for services) - but its very easy to review (eg in contrast to patching nix store allowing paths to be readable by some users only requires to think about many issues such as copying store paths - preserving privileges thereby etc). State is "I don't use my patch but it should be working".

@edolstra edolstra added bug and removed bug labels Feb 26, 2014

@domenkozar

This comment has been minimized.

Member

domenkozar commented Mar 9, 2014

What about a function with signature:

pkgs.lib.privateFile "my.conf" "user" "secret content";

And only user would have read permissions to that file? That solves the use cases I've been seeing on daily basis. Many services require to specify an user and thus we can pass that user to privateFile function.

No idea how that would work with nix-copy-closure, but we could just preserve the uid/gid.

@edolstra

This comment has been minimized.

Member

edolstra commented Mar 10, 2014

One problem with that is that user may not exist yet, since it might be created at activation time.

However, for the most common case (root), it would be fine. And if the service needs to be readable by a non-root service, you could always copy it to some private location in /run in the service's preStart script.

@domenkozar

This comment has been minimized.

Member

domenkozar commented Mar 10, 2014

Now that our users require an uid, we could use that instead of the username. That would work then right?

aszlig added a commit to aszlig/nixops that referenced this issue Jun 24, 2014

MachineState: Implement permissions for keys.
This however only implements setting permissions if "storeKeysOnMachine" is
set to false right now, because if the value is set to true the keys are
symlinked from the store and we actually have to find a way to control
permisions on it, which for the store is only possible if NixOS/nix#8 is
implemented.

Also, this ensures that the key filename is properly escaped.

Signed-off-by: aszlig <aszlig@redmoonstudios.org>

aszlig added a commit to aszlig/nixops that referenced this issue Jul 4, 2014

MachineState: Implement permissions for keys.
This however only implements setting permissions if "storeKeysOnMachine" is
set to false right now, because if the value is set to true the keys are
symlinked from the store and we actually have to find a way to control
permisions on it, which for the store is only possible if NixOS/nix#8 is
implemented.

Also, this ensures that the key filename is properly escaped.

Signed-off-by: aszlig <aszlig@redmoonstudios.org>
@nbp

This comment has been minimized.

Member

nbp commented Oct 18, 2014

@edolstra , with the test cases I made in #329, I am confident that the current proposal is secure enough to handle secret files for the owner of the nix store. When would you be able to review it?

This branch does not use ACL, it only remove the read/execute access for the group and others. Still, the implementation is made such as we can extend it with ACL later. I think it would be good to have a first iteration of NixOS with this branch before adding ACL.

atsukotakahashi pushed a commit to atsukotakahashi/ops that referenced this issue Nov 1, 2014

MachineState: Implement permissions for keys.
This however only implements setting permissions if "storeKeysOnMachine" is
set to false right now, because if the value is set to true the keys are
symlinked from the store and we actually have to find a way to control
permisions on it, which for the store is only possible if NixOS/nix#8 is
implemented.

Also, this ensures that the key filename is properly escaped.

Signed-off-by: aszlig <aszlig@redmoonstudios.org>
@wmertens

This comment has been minimized.

Contributor

wmertens commented Jan 5, 2015

I just thought of another use-case of @MarcWeber's patch: setuid binaries.

As a reminder, MarcWeber/nix@master...experimental/write-file-hashed shows how nix can write content with special permissions outside of the store. It's a very minimal patch compared to #329. (what it is missing is that the hash needs to include the permissions and ownership as well for security)

Using this, you can either sideload secret files, generated them from an expression or have them be copied on package install from a specific directory under /nix-support.

Files can be secrets or simply regular files that need special permissions, like setuid binaries.

@edolstra @nbp?

@domenkozar

This comment has been minimized.

Member

domenkozar commented Mar 10, 2015

How can I help make this happen? NixOS is getting used more and more and this may lead to bad PR in the future since it's indeed a big issue (the biggest?).

@lethalman

This comment has been minimized.

Contributor

lethalman commented Mar 10, 2015

Personally I overcome this problem by providing files as strings instead of a nix store path. Yes that means the configuration is not deployable/reproducible, but perhaps I don't want my passwords to be deployable/reproducible?

@wmertens

This comment has been minimized.

Contributor

wmertens commented Mar 11, 2015

@lethalman can you give an example? I don't understand what you're doing when providing a private file as a string.

@abbradar

This comment has been minimized.

Member

abbradar commented Dec 7, 2016

FWIW there's another use case that I'd like to be supported. Currently we can enable SSH in initrd, which allows one to e.g. enter LUKS passwords remotely. However, for this private SSH server keys need to be written to initrd. We can read-protect /boot so that they cannot be read from there but they are still in /nix/store, globally accessible.

tl;dr: a way to protect all intermediate build products, and for bootloader installation scripts (i.e. some things ran as part of nixos-rebuild with root access rights) to have protected access to those products.

@Ericson2314

This comment has been minimized.

Member

Ericson2314 commented Dec 7, 2016

I'm thinking a content-addressed store could cause similar breakage, so it might make sense to stage them together.

@CMCDragonkai

This comment has been minimized.

CMCDragonkai commented Jan 27, 2017

Here's another usecase: https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/security/prey.nix It requires API keys specified as text options in the service.

@offlinehacker

This comment has been minimized.

Contributor

offlinehacker commented Mar 24, 2017

Nice, i didn't know about encryptString

@domenkozar

This comment has been minimized.

Member

domenkozar commented Apr 3, 2017

RFC for this issue is up: NixOS/rfcs#5

@ghost

This comment has been minimized.

ghost commented Apr 11, 2017

I'm no expert, but it seems to me that the store needs to be broken up between a global store and several user stores. There must be tons of problems with this approach; what are they?
Edit. I dont think this is the right solution, which probably lays at the filesystem layer. Nix is a bit ahead of its time. A different approach to ownership/permission perhaps. I'v seen the mention of plan9 namespace. I'll delve into it at some point, hopefully.

@edolstra edolstra closed this in 45c6405 Apr 25, 2017

@jgillich

This comment has been minimized.

jgillich commented Apr 25, 2017

@edolstra That doesn't seem right?

@Azulinho

This comment has been minimized.

Azulinho commented Apr 25, 2017

Certainly a mistake, but probably worth closing this and follow up on the RFC ?

@edolstra edolstra reopened this Apr 25, 2017

@edolstra

This comment has been minimized.

Member

edolstra commented Apr 25, 2017

Doh. This was a side-effect of merging the nix-repl repo.

@offlinehacker

This comment has been minimized.

Contributor

offlinehacker commented Apr 25, 2017

@ip1981

This comment has been minimized.

ip1981 commented May 9, 2017

Nix store is the code, secrets are the data. Let's do not make it more complicated :)
Code needs less protection if needs at all.

@catern

This comment has been minimized.

Contributor

catern commented Jun 12, 2017

Has anyone looked seriously at a model based on can-only-read-if-you-have-the-hash? As said above, that could be trivially implemented by removing read but not execute permissions from the store. This seems like the most principled way to do it, and would immediately open up a lot of interesting applications.

What would be required to fix the hash leakage? We would need to prevent there from being any globally-readable references to store objects which might transitively mention secrets. That doesn't seem absolutely impossible. It kind of parallels similar efforts to prevent address leakage from the Linux kernel, and other information leakage from trusted code to untrusted code.

Do any tools themselves leak paths? nix-daemon?

@Ekleog

This comment has been minimized.

Member

Ekleog commented Jun 12, 2017

Trying to erase all leakage is just like trying to erase all security flaws in a program: it's doomed to failure, and is way better to add hardening (and erase all security flaws we encounter, also, of course).

For example of path leaks out of the top of my head, the grub command line leaks paths, the kernel, the initrd, /etc/systemd/services... basically the entire NixOS system has not been thought so as to erase all references to hashes. The only case I could think of for avoiding hash leaks is in the context of containers, where the entire filesystem is restricted to a specific host-independent configuration, which allows to be “sure” (as much as one can be) that no hash will leak. But that doesn't solve the broader problem of hash leaks.

Basically, all the nix and nixpkgs ecosystem would have to be fixed to account for this new “no hash leak” rule, so that isn't really possible in my opinion.

(Then, I wonder where the encryption implementation currently lies, I seem to remember it was (almost?) ready in nix1.12?)

@ip1981

This comment has been minimized.

ip1981 commented Jun 13, 2017

can-only-read-if-you-have-the-hash

It seems like the setuid bit. There is at least one problem: interpreters like Python or Bash (who owns a hash: a script or an interpreter?).

@catern

This comment has been minimized.

Contributor

catern commented Jun 13, 2017

Trying to erase all leakage is just like trying to erase all security flaws in a program: it's doomed to failure, and is way better to add hardening (and erase all security flaws we encounter, also, of course).

That is probably true. Even if the whole ecosystem was fixed to avoid leaking hashes, we would still need some kind of hardening to avoid problems resulting from hash leaks.

Is it possible to do such hardening? Maybe if there's some clever and elegant way to harden against path leaks, we can have a solution for secrets that is fundamentally based on paths-as-capabilities, plus hardening.

Maybe the desired property for such hardening would be that even if someone is able to get access to a hash, they won't necessarily be able to access the underlying data, because of additional checks which validate their access. Not sure if that's a correct formulation of the problem, or how to approach solving it in an elegant way.

Maybe we want revocability, in some sense? Lots of revocability techniques out there, maybe something could work without losing our existing advantages.

@copumpkin

This comment has been minimized.

Member

copumpkin commented Jun 20, 2017

The way I was proposing a while ago was a "can-only-read-if-you-have-the-hash-preimage", but you'd likely need a fancy protocol for gaining access to the filesystem entry that way.

@7c6f434c

This comment has been minimized.

Member

7c6f434c commented Apr 23, 2018

I wanted to experiment with a non-world-enmuratable Nix store and see if there are any drawbacks/unexpected problems, but apparently the local store constructor enforces the 0o1775 mode (and I wanted to use mainline Nix). Is there a specific well-known reason for that?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment