Merge pull request #4121 from NixOS/no-user-namespace
Support user namespaces being disabled
This commit is contained in:
commit
54f4500457
1 changed files with 46 additions and 23 deletions
|
@ -831,6 +831,10 @@ private:
|
||||||
paths to the sandbox as a result of recursive Nix calls. */
|
paths to the sandbox as a result of recursive Nix calls. */
|
||||||
AutoCloseFD sandboxMountNamespace;
|
AutoCloseFD sandboxMountNamespace;
|
||||||
|
|
||||||
|
/* On Linux, whether we're doing the build in its own user
|
||||||
|
namespace. */
|
||||||
|
bool usingUserNamespace = true;
|
||||||
|
|
||||||
/* The build hook. */
|
/* The build hook. */
|
||||||
std::unique_ptr<HookInstance> hook;
|
std::unique_ptr<HookInstance> hook;
|
||||||
|
|
||||||
|
@ -920,8 +924,8 @@ private:
|
||||||
result. */
|
result. */
|
||||||
std::map<Path, ValidPathInfo> prevInfos;
|
std::map<Path, ValidPathInfo> prevInfos;
|
||||||
|
|
||||||
const uid_t sandboxUid = 1000;
|
uid_t sandboxUid() { return usingUserNamespace ? 1000 : buildUser->getUID(); }
|
||||||
const gid_t sandboxGid = 100;
|
gid_t sandboxGid() { return usingUserNamespace ? 100 : buildUser->getGID(); }
|
||||||
|
|
||||||
const static Path homeDir;
|
const static Path homeDir;
|
||||||
|
|
||||||
|
@ -2424,15 +2428,14 @@ void DerivationGoal::startBuilder()
|
||||||
"root:x:0:0:Nix build user:%3%:/noshell\n"
|
"root:x:0:0:Nix build user:%3%:/noshell\n"
|
||||||
"nixbld:x:%1%:%2%:Nix build user:%3%:/noshell\n"
|
"nixbld:x:%1%:%2%:Nix build user:%3%:/noshell\n"
|
||||||
"nobody:x:65534:65534:Nobody:/:/noshell\n",
|
"nobody:x:65534:65534:Nobody:/:/noshell\n",
|
||||||
sandboxUid, sandboxGid, settings.sandboxBuildDir));
|
sandboxUid(), sandboxGid(), settings.sandboxBuildDir));
|
||||||
|
|
||||||
/* Declare the build user's group so that programs get a consistent
|
/* Declare the build user's group so that programs get a consistent
|
||||||
view of the system (e.g., "id -gn"). */
|
view of the system (e.g., "id -gn"). */
|
||||||
writeFile(chrootRootDir + "/etc/group",
|
writeFile(chrootRootDir + "/etc/group",
|
||||||
(format(
|
fmt("root:x:0:\n"
|
||||||
"root:x:0:\n"
|
|
||||||
"nixbld:!:%1%:\n"
|
"nixbld:!:%1%:\n"
|
||||||
"nogroup:x:65534:\n") % sandboxGid).str());
|
"nogroup:x:65534:\n", sandboxGid()));
|
||||||
|
|
||||||
/* Create /etc/hosts with localhost entry. */
|
/* Create /etc/hosts with localhost entry. */
|
||||||
if (!(derivationIsImpure(derivationType)))
|
if (!(derivationIsImpure(derivationType)))
|
||||||
|
@ -2629,6 +2632,13 @@ void DerivationGoal::startBuilder()
|
||||||
|
|
||||||
options.allowVfork = false;
|
options.allowVfork = false;
|
||||||
|
|
||||||
|
Path maxUserNamespaces = "/proc/sys/user/max_user_namespaces";
|
||||||
|
static bool userNamespacesEnabled =
|
||||||
|
pathExists(maxUserNamespaces)
|
||||||
|
&& trim(readFile(maxUserNamespaces)) != "0";
|
||||||
|
|
||||||
|
usingUserNamespace = userNamespacesEnabled;
|
||||||
|
|
||||||
Pid helper = startProcess([&]() {
|
Pid helper = startProcess([&]() {
|
||||||
|
|
||||||
/* Drop additional groups here because we can't do it
|
/* Drop additional groups here because we can't do it
|
||||||
|
@ -2647,9 +2657,11 @@ void DerivationGoal::startBuilder()
|
||||||
PROT_WRITE | PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
|
PROT_WRITE | PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
|
||||||
if (stack == MAP_FAILED) throw SysError("allocating stack");
|
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)
|
if (privateNetwork)
|
||||||
flags |= CLONE_NEWNET;
|
flags |= CLONE_NEWNET;
|
||||||
|
if (usingUserNamespace)
|
||||||
|
flags |= CLONE_NEWUSER;
|
||||||
|
|
||||||
pid_t child = clone(childEntry, stack + stackSize, flags, this);
|
pid_t child = clone(childEntry, stack + stackSize, flags, this);
|
||||||
if (child == -1 && errno == EINVAL) {
|
if (child == -1 && errno == EINVAL) {
|
||||||
|
@ -2658,11 +2670,12 @@ void DerivationGoal::startBuilder()
|
||||||
flags &= ~CLONE_NEWPID;
|
flags &= ~CLONE_NEWPID;
|
||||||
child = clone(childEntry, stack + stackSize, flags, this);
|
child = clone(childEntry, stack + stackSize, flags, this);
|
||||||
}
|
}
|
||||||
if (child == -1 && (errno == EPERM || errno == EINVAL)) {
|
if (usingUserNamespace && child == -1 && (errno == EPERM || errno == EINVAL)) {
|
||||||
/* Some distros patch Linux to not allow unprivileged
|
/* Some distros patch Linux to not allow unprivileged
|
||||||
* user namespaces. If we get EPERM or EINVAL, try
|
* user namespaces. If we get EPERM or EINVAL, try
|
||||||
* without CLONE_NEWUSER and see if that works.
|
* without CLONE_NEWUSER and see if that works.
|
||||||
*/
|
*/
|
||||||
|
usingUserNamespace = false;
|
||||||
flags &= ~CLONE_NEWUSER;
|
flags &= ~CLONE_NEWUSER;
|
||||||
child = clone(childEntry, stack + stackSize, flags, this);
|
child = clone(childEntry, stack + stackSize, flags, this);
|
||||||
}
|
}
|
||||||
|
@ -2673,7 +2686,8 @@ void DerivationGoal::startBuilder()
|
||||||
_exit(1);
|
_exit(1);
|
||||||
if (child == -1) throw SysError("cloning builder process");
|
if (child == -1) throw SysError("cloning builder process");
|
||||||
|
|
||||||
writeFull(builderOut.writeSide.get(), std::to_string(child) + "\n");
|
writeFull(builderOut.writeSide.get(),
|
||||||
|
fmt("%d %d\n", usingUserNamespace, child));
|
||||||
_exit(0);
|
_exit(0);
|
||||||
}, options);
|
}, options);
|
||||||
|
|
||||||
|
@ -2694,22 +2708,31 @@ void DerivationGoal::startBuilder()
|
||||||
});
|
});
|
||||||
|
|
||||||
pid_t tmp;
|
pid_t tmp;
|
||||||
if (!string2Int<pid_t>(readLine(builderOut.readSide.get()), tmp)) abort();
|
auto ss = tokenizeString<std::vector<std::string>>(readLine(builderOut.readSide.get()));
|
||||||
|
assert(ss.size() == 2);
|
||||||
|
usingUserNamespace = ss[0] == "1";
|
||||||
|
if (!string2Int<pid_t>(ss[1], tmp)) abort();
|
||||||
pid = tmp;
|
pid = tmp;
|
||||||
|
|
||||||
/* Set the UID/GID mapping of the builder's user namespace
|
if (usingUserNamespace) {
|
||||||
such that the sandbox user maps to the build user, or to
|
/* Set the UID/GID mapping of the builder's user namespace
|
||||||
the calling user (if build users are disabled). */
|
such that the sandbox user maps to the build user, or to
|
||||||
uid_t hostUid = buildUser ? buildUser->getUID() : getuid();
|
the calling user (if build users are disabled). */
|
||||||
uid_t hostGid = buildUser ? buildUser->getGID() : getgid();
|
uid_t hostUid = buildUser ? buildUser->getUID() : getuid();
|
||||||
|
uid_t hostGid = buildUser ? buildUser->getGID() : getgid();
|
||||||
|
|
||||||
writeFile("/proc/" + std::to_string(pid) + "/uid_map",
|
writeFile("/proc/" + std::to_string(pid) + "/uid_map",
|
||||||
(format("%d %d 1") % sandboxUid % hostUid).str());
|
fmt("%d %d 1", sandboxUid(), hostUid));
|
||||||
|
|
||||||
writeFile("/proc/" + std::to_string(pid) + "/setgroups", "deny");
|
writeFile("/proc/" + std::to_string(pid) + "/setgroups", "deny");
|
||||||
|
|
||||||
writeFile("/proc/" + std::to_string(pid) + "/gid_map",
|
writeFile("/proc/" + std::to_string(pid) + "/gid_map",
|
||||||
(format("%d %d 1") % sandboxGid % hostGid).str());
|
fmt("%d %d 1", sandboxGid(), hostGid));
|
||||||
|
} 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");
|
||||||
|
}
|
||||||
|
|
||||||
/* Save the mount namespace of the child. We have to do this
|
/* Save the mount namespace of the child. We have to do this
|
||||||
*before* the child does a chroot. */
|
*before* the child does a chroot. */
|
||||||
|
@ -2745,7 +2768,7 @@ void DerivationGoal::startBuilder()
|
||||||
ex.addTrace({}, "while setting up the build environment");
|
ex.addTrace({}, "while setting up the build environment");
|
||||||
throw ex;
|
throw ex;
|
||||||
}
|
}
|
||||||
debug(msg);
|
debug("sandbox setup: " + msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3569,9 +3592,9 @@ void DerivationGoal::runChild()
|
||||||
/* Switch to the sandbox uid/gid in the user namespace,
|
/* Switch to the sandbox uid/gid in the user namespace,
|
||||||
which corresponds to the build user or calling user in
|
which corresponds to the build user or calling user in
|
||||||
the parent namespace. */
|
the parent namespace. */
|
||||||
if (setgid(sandboxGid) == -1)
|
if (setgid(sandboxGid()) == -1)
|
||||||
throw SysError("setgid failed");
|
throw SysError("setgid failed");
|
||||||
if (setuid(sandboxUid) == -1)
|
if (setuid(sandboxUid()) == -1)
|
||||||
throw SysError("setuid failed");
|
throw SysError("setuid failed");
|
||||||
|
|
||||||
setUser = false;
|
setUser = false;
|
||||||
|
|
Loading…
Reference in a new issue