Nixos-containers with isolated /nix/store

I started using Nix relatively recently, probably no longer than a couple of months at this point. Once I discovered flakes, direnv, and nixos-containers, I was hooked.

However, I felt my containers was sharing, if anything, too much with the host. Especially, I find that sharing /nix/store (and daemon) is a bit iffy. So I decided to see if I could write something that would solve this.

I wrote a fuse client in Go that allows a more fine-grained view of /nix/store by interrogating the Nix daemon. By providing a name (that exists in /nix/store), it looks up the references recursively and builds an index of allowed dependencies.

$ ./go-nix-fs wlwyqvdalg32pdf20klxndhhqmra9mmh-bash-interactive-5.2p37 /mnt/nix-view

$ ls -1 /mnt/nix-view
1ci7cipl06rf3c8cr7vz2zzr36wpxms1-glibc-2.40-36
49l1b1x9aw21c00qmma7zxzbj7qa91pz-readline-8.2p13
60i9rqqaj1zzyspws5byblcc3gq8kp4v-libidn2-2.3.7
83mg03jav95qmj15qbw9c9l464brlrg3-libunistring-1.2
l2xykaacd3xgc1i83j7qvdc4k064w820-xgcc-13.3.0-libgcc
qirrh7cdr8vm2wg56mbx5slgcvdnhcb9-ncurses-6.4.20221231
wlwyqvdalg32pdf20klxndhhqmra9mmh-bash-interactive-5.2p37

$ mount
nix: wlwyqvdalg32pdf20klxndhhqmra9mmh-bash-interactive-5.2p37 on /mnt/nix-view type fuse.nix-store-fs (rw,nosuid,nodev,relatime,user_id=0,group_id=0,allow_other,max_read=131072)

Additionally, I took the original nixos-containers source and modified it to use my new fuse client for mounting /nix/store. I removed other /nix bind mounts as well (such as daemon-socket and db) to further isolate the container.

This is just an experiment, and I have no idea what I’m doing. :rofl:

Feedback is welcome.

https://github.com/DanielAdolfsson/nixos-containers
https://github.com/DanielAdolfsson/nix-store-fs

3 Likes

However, I felt my containers was sharing, if anything, too much with the host.

If you’re doing this to defend against some attacker, note that NixOS containers are not secure. Here’s what the manual says:

Warning

Currently, NixOS containers are not perfectly isolated from the host system. This means that a user with root access to the container can do things that affect the host. So you should not give container root access to untrusted users.

So, while systemd-nspawn (what nixos-containers.nix is wrapping) provides some security by allowing to unshare namespaces, I believe the NixOS containers are pretty wide open (unless you changed something).

1 Like

Leaves room for improvements, for sure. I don’t see why containers couldn’t be more secure. But the point is, why share stuff that doesn’t have to be shared. I’m not particularly trying to secure against an attacker, or create a perfectly isolated box - I just improve what I find is a weak point even for an insecure container.

Well, yes, you may be making it more secure, but you’re probably patching small holes in the walls while the front door is open.

Anyway, I’m not trying to deter you from improving the isolation, just warning you to be careful.

Taking your metaphor, I would argue that the front door in this case is the /nix/store.
But thanks for the feedback nonetheless :grin:

For me, that would be more like a locked window: it’s read only and the container could peek at your secrets, if you have any there.

Personally I’m more worried by the fact that processes can easily escape the container, become the real root user in the host and own you. (At this point shared /nix/store or not is pointless)

Agreed, that is a big problem. I’m looking into adding --private-users as well.

1 Like

It would be great if we could make the containers reasonably secure by default or with a simple flag.

1 Like

My unofficial version of nixos-containers have two options; privateStore to turn on /nix/store isolation, and privateUsers to turn on -U with systemd-nspawn. I’m sure it will break things, but it’s not a bad plan to use user namespaces if possible.

1 Like

This is great! Are you planning on upstreaming your modifications to the module and nix-store-fs to nixpkgs?

I have replaced my module with yours and it works perfectly. privateUsers / -U breaks some stuff, sure, (as expected) but privateStore is working flawlessly so far (apart from file = ./some-file.txt, I replaced those with file = pkgs.copyToStore ./some-file.txt, but that’s better anyway).

Nice job!