Dynamically disable user namespaces if CLONE_NEWUSER fails
This makes builds work inside nixos-enter. Fixes #3145.
This commit is contained in:
parent
6aa64627c8
commit
97ffc1e013
|
@ -924,8 +924,8 @@ private:
|
|||
result. */
|
||||
std::map<Path, ValidPathInfo> prevInfos;
|
||||
|
||||
uid_t sandboxUid = 1000;
|
||||
gid_t sandboxGid = 100;
|
||||
uid_t sandboxUid() { return usingUserNamespace ? 1000 : buildUser->getUID(); }
|
||||
gid_t sandboxGid() { return usingUserNamespace ? 100 : buildUser->getGID(); }
|
||||
|
||||
const static Path homeDir;
|
||||
|
||||
|
@ -2428,15 +2428,14 @@ void DerivationGoal::startBuilder()
|
|||
"root:x:0:0:Nix build user:%3%:/noshell\n"
|
||||
"nixbld:x:%1%:%2%:Nix build user:%3%:/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
|
||||
view of the system (e.g., "id -gn"). */
|
||||
writeFile(chrootRootDir + "/etc/group",
|
||||
(format(
|
||||
"root:x:0:\n"
|
||||
fmt("root:x:0:\n"
|
||||
"nixbld:!:%1%:\n"
|
||||
"nogroup:x:65534:\n") % sandboxGid).str());
|
||||
"nogroup:x:65534:\n", sandboxGid()));
|
||||
|
||||
/* Create /etc/hosts with localhost entry. */
|
||||
if (!(derivationIsImpure(derivationType)))
|
||||
|
@ -2640,17 +2639,6 @@ void DerivationGoal::startBuilder()
|
|||
|
||||
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
|
||||
|
@ -2682,11 +2670,12 @@ void DerivationGoal::startBuilder()
|
|||
flags &= ~CLONE_NEWPID;
|
||||
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
|
||||
* user namespaces. If we get EPERM or EINVAL, try
|
||||
* without CLONE_NEWUSER and see if that works.
|
||||
*/
|
||||
usingUserNamespace = false;
|
||||
flags &= ~CLONE_NEWUSER;
|
||||
child = clone(childEntry, stack + stackSize, flags, this);
|
||||
}
|
||||
|
@ -2697,7 +2686,8 @@ void DerivationGoal::startBuilder()
|
|||
_exit(1);
|
||||
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);
|
||||
}, options);
|
||||
|
||||
|
@ -2718,7 +2708,10 @@ void DerivationGoal::startBuilder()
|
|||
});
|
||||
|
||||
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;
|
||||
|
||||
if (usingUserNamespace) {
|
||||
|
@ -2729,12 +2722,16 @@ void DerivationGoal::startBuilder()
|
|||
uid_t hostGid = buildUser ? buildUser->getGID() : getgid();
|
||||
|
||||
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) + "/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
|
||||
|
@ -3595,9 +3592,9 @@ void DerivationGoal::runChild()
|
|||
/* Switch to the sandbox uid/gid in the user namespace,
|
||||
which corresponds to the build user or calling user in
|
||||
the parent namespace. */
|
||||
if (setgid(sandboxGid) == -1)
|
||||
if (setgid(sandboxGid()) == -1)
|
||||
throw SysError("setgid failed");
|
||||
if (setuid(sandboxUid) == -1)
|
||||
if (setuid(sandboxUid()) == -1)
|
||||
throw SysError("setuid failed");
|
||||
|
||||
setUser = false;
|
||||
|
|
Loading…
Reference in a new issue