Nix build: hide non-required path in /nix/store

When building a derivation, the build code can read all of /nix/store if I’m correct. Is it however possible to hide to the build VM the content of the paths that are not required to build the current derivation? The reasons is that I’m thinking to use nix in a setting where the content of /nix/store may fundamentally contain “secret” data, like the source code of a program. Think for instance of this in the context of a Continuous Integration (CI) system, where the /nix store is mounted in all build containers (to save space, CPU usage, bandwidth, time…): if project A uses flake, its source code will be copied in /nix/store, so if we compile project B later, project B will be able to read project A’s source code, which may be unwanted if both projects are owned by different companies that aim to keep their algorithms/programs secret. (in practice, I don’t deal with companies trying to learn the program of other companies, but rather student trying to learn the source code of other (better) students/teachers, as their code is automatically tested in a VM)

I think it’s already the case:

$ nix build --impure --expr 'with import <nixpkgs> {}; hello.overrideAttrs(oa: { preUnpack = "echo /nix/store contents; ls /nix/store/; exit 1"; })' -L
hello> Using versionCheckHook
hello> Running phase: unpackPhase
hello> /nix/store contents
hello> 0fsnicvfpf55nkza12cjnad0w84d6ba7-gcc-wrapper-14.2.1.20250322
hello> 1abbyfv3bpxalfjfgpmwg8jcy931bf76-bzip2-1.0.8-bin
hello> 2jc1jmzis19adawjwhl8qhdvh7vlbk0q-patchelf-0.15.0
hello> 2x51wvk10m9l014lyrfdskc3b360ifjp-ed-1.21.1
hello> 303islqk386z1w2g1ngvxnkl4glfpgrs-glibc-2.40-66-bin
hello> 3mi59bgj22xx29dyss7jhmx3sgznd85m-acl-2.3.2
hello> 5yzw0vhkyszf2d179m0qfkgxmp5wjjx4-move-docs.sh
hello> 6hqzbvz50bm87hcj4qfn51gh7arxj8a6-gcc-14.2.1.20250322-libgcc
hello> 6nkqdqzpa75514lhglgnjs5k4dklw4sb-libidn2-2.3.8
hello> 7c0v0kbrrdc2cqgisi78jdqxn73n3401-gcc-14.2.1.20250322-lib
hello> 7h0sard22wnbz0jyz07w8y9y0fcs795r-diffutils-3.12
hello> 7y59hzi3svdj1xjddjn2k7km96pifcyl-findutils-4.10.0
hello> 87fck6hm17chxjq7badb11mq036zbyv9-coreutils-9.7
hello> 8syylmkvnn7lg2nar9fddpp5izb4gh56-attr-2.5.2
hello> 974a51073v6cb7cr5j0dazanxzmk9bxg-binutils-2.44-lib
hello> 98zamhd8d0jq3skqwz28dlgph94mrqir-xz-5.8.1-bin
hello> 9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322
hello> a9c6rz5183psp30q1nhkakis6ab4km4b-pcre2-10.44
hello> afhkqb5a94zlwjxigsnwsfwkf38h21dk-gzip-1.14
hello> aq5b44b37zp5dfwz5330pxqm699gs4g3-isl-0.20
hello> cg9s562sa33k78m63njfn1rw47dp9z0i-glibc-2.40-66
hello> cickvswrvann041nqxb0rxilc46svw1n-prune-libtool-files.sh
hello> clbb2cvigynr235ab5zgi18dyavznlk2-gnused-4.9
hello> cmzya9irvxzlkh7lfy6i82gbp0saxqj3-multiple-outputs.sh
hello> d46ilc6gzd1piyjfm9sbrl7pq3b3k0hg-busybox-1.36.1
hello> dw402azxjrgrzrk6j0p66wkqrab5mwgw-hello-2.12.2.tar.gz
hello> dwwc14ppzkl0yphcgsz25xvi24c9d1zm-gmp-6.3.0
hello> fcyn0dqszgfysiasdmkv1jh3syncajay-gawk-5.3.2
hello> fyaryjvghbkpfnsyw97hb3lyb37s1pd6-move-lib64.sh
hello> gqmr3gixlddz3667ba1iyqck3c0dkpvd-gnugrep-3.11
hello> gyf8x1fx2k70dbb5njlvhpbjnbq49qr2-version-check-hook
hello> h9lc1dpi14z7is86ffhl3ld569138595-audit-tmpdir.sh
hello> hhfm5fkvb1alg1np5a69m2qlcjqhr062-binutils-wrapper-2.44
hello> hxrjrzngydk24ah8b5n8cl777n39y08b-linux-headers-6.12.7
hello> hxv896faph0rqxjq2ycxpcrbnngc95sz-patch-shebangs.sh
hello> imhzyxqr7swq08ip81az5kfa07r07kg0-file-5.46
hello> jjhw2phnaip4kg0qjas3x3fsaifi8y0w-no-broken-symlinks.sh
hello> kd4xwxjpjxi71jkm6ka0np72if9rm3y0-move-sbin.sh
hello> kv10h4pidkmx8cjs2sw2pi9rlcnighbc-gnumake-4.4.1
hello> lmc7x75jvrca79fc2r2j1frmklzvh04h-update-autotools-gnu-config-scripts-hook
hello> m54bmrhj6fqz8nds5zcj97w9s9bckc9v-compress-man-pages.sh
hello> mb407pssv7zc7pfb4d910k6fshfagm6j-libmpc-1.3.1
hello> n52k1dccv0mipz1s4gkk45x64cmmcvrf-mpfr-4.2.2
hello> nivcx63drxqzm6pic6vm2wbkxl368w83-stdenv-linux
hello> nzg6zqsijbv7yc95wlfcdswx6bg69srq-gmp-with-cxx-6.3.0
hello> p9k7bd23v5yvmap9594f9x7hpvacdh32-expand-response-params
hello> pa60s415p92gnhv5ffz1bmfgzzfvhvd8-xz-5.8.1
hello> pag6l61paj1dc9sv15l7bm5c17xn5kyk-move-systemd-user-units.sh
hello> pilsssjjdxvdphlg2h19p0bfx5q0jzkn-strip.sh
hello> r25srliigrrv5q3n7y8ms6z10spvjcd9-glibc-2.40-66-dev
hello> rzdjxxf4jkv3qdsjxkg54fxbma5zr05k-gnu-config-2024-01-01
hello> shkw4qm9qcw5sc5n1k5jznc83ny02r39-default-builder.sh
hello> srby6wmvg7dp454pwb6qvaxdiri38sc1-zlib-1.3.1
hello> v63bxfiacw082c7ijshf60alvvrpfxsq-binutils-2.44
hello> vj1c3wf9c11a0qs6p3ymfvrnsdgsdcbq-source-stdenv.sh
hello> wgrbkkaldkrlrni33ccvm3b6vbxzb656-make-symlinks-relative.sh
hello> wrxvqj822kz8746608lgns7h8mkpn79f-gnutar-1.35
hello> x0kaspzb5jqvgp357bj27z6iq24ximfg-patch-2.7.6
hello> xy4jjgw87sbgwylm5kn047d9gkbhsr9x-bash-5.2p37
hello> xyff06pkhki3qy1ls77w10s0v79c9il0-reproducible-builds.sh
hello> yypqcvqhnv8y4zpicgxdigp3giq81gzb-libunistring-1.3
hello> z7k98578dfzi6l3hsvbivzm7hfqlk0zc-set-source-date-epoch-to-latest.sh
hello> za53jjhjl1xajv3y1zpjvr9mh4w0c1ay-xgcc-14.2.1.20250322-libgcc
hello> zrnqzhcvlpiycqbswl0w172y4bpn0lb4-bzip2-1.0.8
error: builder for '/nix/store/8wby669b6dw1v78lan1v1fsnph3wkynl-hello-2.12.2.drv' failed with exit code 1;

As @trofi already said, this is already mitigated by the sandbox in most cases, which is enabled by default on Linux. There is a sandbox for Darwin too as far as I can tell, but as a strict Linux user I wouldn’t know specifics about that.

Since you are referring to potentially malicious code, I would like to point to the Lix documentation which has an excellent section on the Lix daemon as a security non-boundary.
This is something which was added specifically to the Lix documentation and is not present in CppNix docs to my knowledge, however it applies to both on a technical level (the Lix docs do not necessarily reflect the opinions of the CppNix team though).

1 Like

Oh great, I should have checked first, thanks a lot!

Btw, do you know if there is a simple way to get access to the same kind of sandbox, but at run time instead of build time?

Hum really? But then does it mean that any multi-user system installing nix (including NixOS) cannot possibly be secure, in the sense that any unpriviledge user can gain root access? Because any user has access to the nix socket…

Access to the socket does not automatically give you root access.
However the daemon does run as root, and it is a complex piece of software, so any existing vulnerability in the code could lead to privilege escalation.
The advice given in that document is more along the lines of “you should take additional steps to ensure that untrusted code is either not executed or the environment can mitigate any potential issues in the daemon code” (such as using seccomp and/or additional sandboxing of the daemon).

To quote the documentation:

custom non-rootless Linux container implementation that has not been audited to nearly the same degree as Docker and similar systems

Meaning that it has not received some scrutiny but not even vaguely comparable to projects like podman which were built specifically for separation and security.

maintaining its goals of building useful, reproducible software; security is not its primary goal

The primary goal of that piece of software is not security, but functionality and reproducibility (read: it must work first and foremost), so while the people working on it do their best to catch any security issues, if you need to guarantee that the system is safe, you should take additional steps.
This can include separate builders for individual projects (in your example of having two customers with separate projects which need to be separated).

Thanks, I understand, but I’m very much worried about this statement:

because, even forgetting CI stuff, if the nix daemon is not secure, so is NixOS itself… And, IMHO, the primary goal of a linux system should be to provide security to its users, especially since linux systems are typically used in sensitive applications in contact with untrusted users (e.g. servers, schools, company networks…).

So is the take home message of this discussion is that NixOs should not be used in places where you create accounts for untrusted users? Or at least that untrusted users should not be granted access to the nix socket by chowning/chmoding-it accordingly? Is there any documentation on how to secure these systems, especially if we want untrusted users to be able to run nix commands? (e.g. in a school, I may want to allow students to locally install programs etc). You mentionned seccomp/sandboxing etc, but not sure how to use it here.

I’m not aware of a way to expose nix sandbox as is. Would be nice to have an equivalent of guix environment --container / guix shell --container.

1 Like

You can set the allowed-users Nix configuration to mitigate this, e.g. to [ "@wheel" ]. (trusted-users is much more trivially escalation to root.)

1 Like

First, there is a distinction here between the daemon, and the sandbox.
You know how the Docker daemon has its one socket, and everyone having access to that socket is able to bind-mount directories into a container read-write, effectively allowing arbitrary modification of the host filesystem?
That is a security concern, however it does not constitute a vulnerability because due to the principle of least privilege, nobody has access to the socket by default (except for root of course).

Similarly having access to the nix-daemon does open up the possibility of a security vulnerability causing issues like a sandbox escape.
The issue that the Docker daemon has, where one is able to escalate privileges is attempted to be mitigated (and the people working on it do a good job at security in general), however functionality of the daemon comes first, meaning that if you do not want someone to potentially escape the sandbox, you shouldn’t allow them access to the daemon.

You could e.g. change both trusted-users and allowed-users in nix.conf as others suggested.
Trusted users may e.g. provide additional substituters (which allowed users can’t), by which they could provide bogus packages to introduce a backdoor in any of the system programs (providing a different version of sudo which always allows your user for instance).
However, you implicitly or explicitly gave that person the elevated permissions to do so, just like you could give someone root access directly (that’s why they are trusted users).
Allowed users would have to exploit several mechanisms, from a sandbox escape to a different kind of privilege escalation to escape the confines of the build user (or subuid if the feature is enabled), and while mitigating those issues is in scope of either implementation, it is not the primary concern.
You can always outsource the actual build process of a derivation to a different machine via a remote builder, and then give every customer their own machine.
The results would both be copied back to the same machine, but the build would be separate, and since the built outputs are not actually executed by the daemon, you’d be able to share/cache any redundant dependencies between the two.

If you are really concerned about the security of a running machine you can either change those settings, or you could even disable the daemon altogether and rely on running all nix commands as root, which would then proceed to use the “local” store just fine, moving all of the authorization to other tools such as sshd, sudo, or doas.
For anything that needs to be even more locked down you can always remove not just the daemon but nix, nixos-rebuild, and all the other tools entirely and ship the whole thing as an appliance (there are options for that that I can’t remember off the top of my head).
That way there is no nix on the machine and the only way to change the machine is to write a new image to it (“rebuild” the machine off of a different image in OpenStack for instance), or boot off of a specially crafted USB installer/updater.

The original link, the Lix daemon as a security non-boundary has a lot of information on this subject, including a detailed description of the trust model and the kinds of access that users have.