diff --git a/src/libstore/build.cc b/src/libstore/build.cc index 4c88504c4..4ff232e2b 100644 --- a/src/libstore/build.cc +++ b/src/libstore/build.cc @@ -720,6 +720,10 @@ private: paths to the sandbox as a result of recursive Nix calls. */ AutoCloseFD sandboxMountNamespace; + /* On Linux, whether we're doing the build in its own user + namespace. */ + bool usingUserNamespace = true; + /* The build hook. */ std::unique_ptr hook; @@ -2319,8 +2323,8 @@ void DerivationGoal::startBuilder() if (useUidRange && (!buildUser || buildUser->getUIDCount() < 65536)) throw Error("feature 'uid-range' requires '%s' to be enabled", settings.autoAllocateUids.name); - sandboxUid = useUidRange ? 0 : 1000; - sandboxGid = useUidRange ? 0 : 100; + sandboxUid = useUidRange && usingUserNamespace ? 0 : 1000; + sandboxGid = useUidRange && usingUserNamespace ? 0 : 100; writeFile(chrootRootDir + "/etc/passwd", fmt( "root:x:0:0:Nix build user:%3%:/noshell\n" @@ -2551,6 +2555,24 @@ void DerivationGoal::startBuilder() options.allowVfork = false; + Path maxUserNamespaces = "/proc/sys/user/max_user_namespaces"; + static bool userNamespacesEnabled = + pathExists(maxUserNamespaces) + && trim(readFile(maxUserNamespaces)) != "0"; + + usingUserNamespace = userNamespacesEnabled; + + if (usingUserNamespace) { + sandboxUid = 1000; + sandboxGid = 100; + } else { + debug("note: not using a user namespace"); + if (!buildUser) + throw Error("cannot perform a sandboxed build because user namespaces are not enabled; check /proc/sys/user/max_user_namespaces"); + sandboxUid = buildUser->getUID(); + sandboxGid = buildUser->getGID(); + } + Pid helper = startProcess([&]() { /* Drop additional groups here because we can't do it @@ -2569,9 +2591,11 @@ void DerivationGoal::startBuilder() PROT_WRITE | PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0); if (stack == MAP_FAILED) throw SysError("allocating stack"); - int flags = CLONE_NEWUSER | CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWIPC | CLONE_NEWUTS | CLONE_PARENT | SIGCHLD; + int flags = CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWIPC | CLONE_NEWUTS | CLONE_PARENT | SIGCHLD; if (privateNetwork) flags |= CLONE_NEWNET; + if (usingUserNamespace) + flags |= CLONE_NEWUSER; pid_t child = clone(childEntry, stack + stackSize, flags, this); if (child == -1 && errno == EINVAL) { @@ -2619,21 +2643,23 @@ void DerivationGoal::startBuilder() if (!string2Int(readLine(builderOut.readSide.get()), tmp)) abort(); pid = tmp; - /* Set the UID/GID mapping of the builder's user namespace - such that the sandbox user maps to the build user, or to - the calling user (if build users are disabled). */ - uid_t hostUid = buildUser ? buildUser->getUID() : getuid(); - uid_t hostGid = buildUser ? buildUser->getGID() : getgid(); - uint32_t nrIds = buildUser && useUidRange ? buildUser->getUIDCount() : 1; + if (usingUserNamespace) { + /* Set the UID/GID mapping of the builder's user namespace + such that the sandbox user maps to the build user, or to + the calling user (if build users are disabled). */ + uid_t hostUid = buildUser ? buildUser->getUID() : getuid(); + uid_t hostGid = buildUser ? buildUser->getGID() : getgid(); + uint32_t nrIds = buildUser && useUidRange ? buildUser->getUIDCount() : 1; - writeFile("/proc/" + std::to_string(pid) + "/uid_map", - fmt("%d %d %d", sandboxUid, hostUid, nrIds)); + writeFile("/proc/" + std::to_string(pid) + "/uid_map", + fmt("%d %d %d", sandboxUid, hostUid, nrIds)); - if (!useUidRange) - writeFile("/proc/" + std::to_string(pid) + "/setgroups", "deny"); + if (!useUidRange) + writeFile("/proc/" + std::to_string(pid) + "/setgroups", "deny"); - writeFile("/proc/" + std::to_string(pid) + "/gid_map", - fmt("%d %d %d", sandboxGid, hostGid, nrIds)); + writeFile("/proc/" + std::to_string(pid) + "/gid_map", + fmt("%d %d %d", sandboxGid, hostGid, nrIds)); + } /* Save the mount namespace of the child. We have to do this *before* the child does a chroot. */ @@ -2675,7 +2701,7 @@ void DerivationGoal::startBuilder() ex.addTrace({}, "while setting up the build environment"); throw ex; } - debug(msg); + debug("sandbox setup: " + msg); } }