To allow a Nix store to be shared safely among multiple users, it is important that users cannot meaningfully influence the execution of derivation builds such that they could inject malicious code into them without changing their (either input- or output- addressed) hash. If they could do so, they could install a Trojan horse in some package and compromise the accounts of other users.

To prevent this, the Nix store and database are owned by some privileged user (usually root) and builders are executed under unprivileged system user accounts (usually named nixbld1, nixbld2, etc.). When an unprivileged user runs a Nix command, actions that operate on the Nix store (such as builds) are forwarded to a Nix daemon running under the owner of the Nix store/database that performs the operation.

The buried lede in the above sentence is that currently, even in multi-user mode using a daemon, if executing as the user that owns the store, Lix directly manipulates the store unless --store daemon is specified. We intend to change this in the future.

Do not evaluate or build untrusted, potentially-malicious, Nix language code on machines that you care deeply about maintaining user isolation on.

Although we would consider any sandbox escapes to be serious security bugs and we intend to fix them, we are not confident enough in the daemon's security to call the daemon a security boundary.

There are two categories of users of the Lix daemon: trusted users and untrusted users. The Lix daemon only allows connections from users that are either trusted users, or are specified in, or are members of groups specified in, allowed-users in nix.conf. Trusted users are users and users of groups specified in trusted-users in nix.conf.

All users of the Lix daemon may do the following to bring things into the Nix store:

The Lix team and wider community does not consider the Lix daemon to be a security boundary against malicious Nix language code.

Although we do our best to make it secure, we do not recommend sharing a Lix daemon with potentially malicious users. That means that public continuous integration (CI) builds of untrusted Nix code should not share builders with CI that writes into a cache used by trusted infrastructure.

For example, hydra.nixos.org, which is the builder for cache.nixos.org, does not execute untrusted Nix language code; a separate system, ofborg is used for CI of nixpkgs pull requests. The build output of pull request CI is never pushed to cache.nixos.org, and those systems are considered entirely untrusted.

This is because, among other things, the Lix sandbox is more susceptible to kernel exploits than Docker, which, unlike Lix, blocks nested user namespaces via seccomp in its default policy, and there have been many kernel bugs only exposed to unprivileged users via user namespaces allowing otherwise-root-only system calls. In general, the Lix sandbox is set up to be relatively unrestricted while maintaining its goals of building useful, reproducible software; security is not its primary goal.

The Lix sandbox is a custom non-rootless Linux container implementation that has not been audited to nearly the same degree as Docker and similar systems. Also, the Lix daemon is a complex and historied C++ executable running as root with very little privilege separation. All of this means that a security hole in the Lix daemon gives immediate root access. Systems like Docker (especially non-rootless Docker) should themselves probably not be used in a multi-tenant manner with mutually distrusting tenants, but the Lix daemon especially should not be used as such as of this writing.

The primary purpose of the sandbox is to strongly encourage packages to be reproducible, a goal which it is generally quite successful at.

Trusted users are permitted to set any setting and bypass security restrictions on the daemon. They are currently in widespread use for a couple of reasons such as remote builds (which we intend to fix).

Trusted users are effectively root on Nix daemons running as root (the default configuration) for at least the following reasons, and should be thus thought of as equivalent to passwordless sudo. This is not a comprehensive list.

Note that setting accept-flake-config allows arbitrary Nix flakes to set Nix settings in the nixConfig stanza. Do not set this setting or pass --accept-flake-config while executing untrusted Nix language code as a trusted user for the reasons above!

The build users are the special UIDs under which builds are performed. A build user is selected for a build by looking in the group specified by build-users-group, by default, nixbld, then a member of that group not currently executing a build is selected for the build. The build users should not be members of any other group.

There can never be more concurrent builds than the number of build users, unless using auto-allocate-uids (tracking issue).

If, for some reason, you need to create such users manually, the following command will create 10 build users on Linux:

$ groupadd -r nixbld
$ for n in $(seq 1 10); do useradd -c "Nix build user $n" \
    -d /var/empty -g nixbld -G nixbld -M -N -r -s "$(which nologin)" \
    nixbld$n; done

The Nix daemon can be started manually as follows (as root):

# nix-daemon

In standard installations of Lix, the daemon is started by a systemd unit (Linux) or launchd service (macOS).