Why does multi-user nix need multiple build users?

I was reading here: Multi-User Mode - Nix Reference Manual

I understand that the build user can’t be a regular system user, but I can’t seem to figure out why this is true: “There can never be more concurrent builds than the number of build users”

Mostly just curious, but is this because there is a way for builds under a certain user to impact other builds running under that same user?

This is discussed in guix as well: Build Environment Setup (GNU Guix Reference Manual)

Having several such users allows the daemon to launch distinct build processes under separate UIDs, which guarantees that they do not interfere with each other—an essential feature since builds are regarded as pure functions

Oh is this just because a single user can view the processing it is running and could introspect into the processes running within another build? Kill those processes or read their state?

Maybe that’s just it? Curious if there’s more.

2 Likes

Answering my own question: It’s clearer now that the linux kernel allows users to mess with their own state. Or at least the kernel does not draw hard boundaries around an individual user messing with the state of that same user. I believe if the nix build users were all one user they’d be able to interfere with other builds that are running concurrently.

Also of note, is that the nix-daemon needs to have root access as /nix is usually owned by root. Having a non-privileged user build the package is good security practice. nix-daemon will just ensure that the correct mount points are available when performing the build.

What kind of state manipulation is possible here?
I’m also interested in knowing if it would be possible to achieve build isolation using the systems that containers use (cgroups and namespaces primarily).

Any kind of state manipulation is possible. Linux follows the rule that if you own it, it’s yours and you can do what you want with it. That means creating, modifying, and deleting files, and starting, killing, and debugging* running processes are all allowed. It makes sandboxing tricky, to put it mildly.

Container security works by assigning a block of uids and gids to the container. This presupposes that you can create new users and is more complicated.

Namespaces and cgroups aren’t a security mechanism because they can be escaped just as easily as they are entered. Try starting Firefox in a namespace with networking disabled sometime. Hint: It doesn’t work.

Chrome’s sandboxes use a (relatively) new kernel feature that lets you use a netfilter-like interface to block syscalls, but it’s cludgy and brittle so it only really works well if you have complete control over the sandboxed code and can keep up with kernel syscall changes. It isn’t a general solution.

*There protections for suid and sgid processes because the caller usually doesn’t own those.