aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/libstore/build.cc27
-rw-r--r--src/libstore/globals.hh3
-rw-r--r--src/nix/run.cc5
3 files changed, 31 insertions, 4 deletions
diff --git a/src/libstore/build.cc b/src/libstore/build.cc
index 4bec37e0f..be52b66a7 100644
--- a/src/libstore/build.cc
+++ b/src/libstore/build.cc
@@ -2357,17 +2357,37 @@ void DerivationGoal::startBuilder()
flags |= CLONE_NEWNET;
pid_t child = clone(childEntry, stack + stackSize, flags, this);
- if (child == -1 && errno == EINVAL)
+ if (child == -1 && errno == EINVAL) {
/* Fallback for Linux < 2.13 where CLONE_NEWPID and
CLONE_PARENT are not allowed together. */
- child = clone(childEntry, stack + stackSize, flags & ~CLONE_NEWPID, this);
+ flags &= ~CLONE_NEWPID;
+ child = clone(childEntry, stack + stackSize, flags, this);
+ }
+ if (child == -1 && (errno == EPERM || errno == EINVAL)) {
+ /* Some distros patch Linux to not allow unpriveleged
+ * user namespaces. If we get EPERM or EINVAL, try
+ * without CLONE_NEWUSER and see if that works.
+ */
+ flags &= ~CLONE_NEWUSER;
+ child = clone(childEntry, stack + stackSize, flags, this);
+ }
+ /* Otherwise exit with EPERM so we can handle this in the
+ parent. This is only done when sandbox-fallback is set
+ to true (the default). */
+ if (child == -1 && (errno == EPERM || errno == EINVAL) && settings.sandboxFallback)
+ _exit(1);
if (child == -1) throw SysError("cloning builder process");
writeFull(builderOut.writeSide.get(), std::to_string(child) + "\n");
_exit(0);
}, options);
- if (helper.wait() != 0)
+ int res = helper.wait();
+ if (res != 0 && settings.sandboxFallback) {
+ useChroot = false;
+ tmpDirInSandbox = tmpDir;
+ goto fallback;
+ } else if (res != 0)
throw Error("unable to start build process");
userNamespaceSync.readSide = -1;
@@ -2398,6 +2418,7 @@ void DerivationGoal::startBuilder()
} else
#endif
{
+ fallback:
options.allowVfork = !buildUser && !drv->isBuiltin();
pid = startProcess([&]() {
runChild();
diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh
index 13effb507..ab1c09aa2 100644
--- a/src/libstore/globals.hh
+++ b/src/libstore/globals.hh
@@ -209,6 +209,9 @@ public:
"The paths to make available inside the build sandbox.",
{"build-chroot-dirs", "build-sandbox-paths"}};
+ Setting<bool> sandboxFallback{this, true, "sandbox-fallback",
+ "Whether to disable sandboxing when the kernel doesn't allow it."};
+
Setting<PathSet> extraSandboxPaths{this, {}, "extra-sandbox-paths",
"Additional paths to make available inside the build sandbox.",
{"build-extra-chroot-dirs", "build-extra-sandbox-paths"}};
diff --git a/src/nix/run.cc b/src/nix/run.cc
index 35b763345..90b76d666 100644
--- a/src/nix/run.cc
+++ b/src/nix/run.cc
@@ -199,7 +199,10 @@ void chrootHelper(int argc, char * * argv)
uid_t gid = getgid();
if (unshare(CLONE_NEWUSER | CLONE_NEWNS) == -1)
- throw SysError("setting up a private mount namespace");
+ /* Try with just CLONE_NEWNS in case user namespaces are
+ specifically disabled. */
+ if (unshare(CLONE_NEWNS) == -1)
+ throw SysError("setting up a private mount namespace");
/* Bind-mount realStoreDir on /nix/store. If the latter mount
point doesn't already exists, we have to create a chroot