Skip to content
Permalink
Browse files

Support user namespaces being disabled

If max_user_namespaces is set to 0, then don't run the build in a user
namespace.

Fixes #4092.
  • Loading branch information
edolstra committed Oct 7, 2020
1 parent f66bbd8 commit 6aa64627c8e431c3b187f7bb44c943d06e39b929
Showing with 40 additions and 14 deletions.
  1. +40 −14 src/libstore/build.cc
@@ -831,6 +831,10 @@ class DerivationGoal : public Goal
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<HookInstance> hook;

@@ -920,8 +924,8 @@ class DerivationGoal : public Goal
result. */
std::map<Path, ValidPathInfo> prevInfos;

const uid_t sandboxUid = 1000;
const gid_t sandboxGid = 100;
uid_t sandboxUid = 1000;
gid_t sandboxGid = 100;

const static Path homeDir;

@@ -2629,6 +2633,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
@@ -2647,9 +2669,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) {
@@ -2697,19 +2721,21 @@ void DerivationGoal::startBuilder()
if (!string2Int<pid_t>(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();
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();

writeFile("/proc/" + std::to_string(pid) + "/uid_map",
(format("%d %d 1") % sandboxUid % hostUid).str());
writeFile("/proc/" + std::to_string(pid) + "/uid_map",
(format("%d %d 1") % sandboxUid % hostUid).str());

writeFile("/proc/" + std::to_string(pid) + "/setgroups", "deny");
writeFile("/proc/" + std::to_string(pid) + "/setgroups", "deny");

writeFile("/proc/" + std::to_string(pid) + "/gid_map",
(format("%d %d 1") % sandboxGid % hostGid).str());
writeFile("/proc/" + std::to_string(pid) + "/gid_map",
(format("%d %d 1") % sandboxGid % hostGid).str());
}

/* Save the mount namespace of the child. We have to do this
*before* the child does a chroot. */
@@ -2745,7 +2771,7 @@ void DerivationGoal::startBuilder()
ex.addTrace({}, "while setting up the build environment");
throw ex;
}
debug(msg);
debug("sandbox setup: " + msg);
}
}

0 comments on commit 6aa6462

Please sign in to comment.
You can’t perform that action at this time.