about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEelco Dolstra <eelco.dolstra@logicblox.com>2015-02-23T14·41+0100
committerEelco Dolstra <eelco.dolstra@logicblox.com>2015-02-23T14·54+0100
commit99897f6979aa21339b80904db1717c65a9202110 (patch)
treeb189e1e30b75f4b9a1a910a17c68760d44c52a79
parent15d2d3c34e454fb7795998a3a2d73010dfbdec38 (diff)
Use chroots for all derivations
If ‘build-use-chroot’ is set to ‘true’, fixed-output derivations are
now also chrooted. However, unlike normal derivations, they don't get
a private network namespace, so they can still access the
network. Also, the use of the ‘__noChroot’ derivation attribute is
no longer allowed.

Setting ‘build-use-chroot’ to ‘relaxed’ gives the old behaviour.
-rw-r--r--doc/manual/command-ref/conf-file.xml36
-rw-r--r--src/libstore/build.cc47
-rw-r--r--src/libstore/globals.cc2
-rw-r--r--src/libstore/globals.hh3
4 files changed, 57 insertions, 31 deletions
diff --git a/doc/manual/command-ref/conf-file.xml b/doc/manual/command-ref/conf-file.xml
index 91aa910a2946..cdfdc1a6e461 100644
--- a/doc/manual/command-ref/conf-file.xml
+++ b/doc/manual/command-ref/conf-file.xml
@@ -227,24 +227,32 @@ flag, e.g. <literal>--option gc-keep-outputs false</literal>.</para>
   <varlistentry><term><literal>build-use-chroot</literal></term>
 
     <listitem><para>If set to <literal>true</literal>, builds will be
-    performed in a <emphasis>chroot environment</emphasis>, i.e., the
-    build will be isolated from the normal file system hierarchy and
-    will only see its dependencies in the Nix store, the temporary
-    build directory, private versions of <filename>/proc</filename>,
+    performed in a <emphasis>chroot environment</emphasis>, i.e.,
+    they’re isolated from the normal file system hierarchy and will
+    only see their dependencies in the Nix store, the temporary build
+    directory, private versions of <filename>/proc</filename>,
     <filename>/dev</filename>, <filename>/dev/shm</filename> and
     <filename>/dev/pts</filename>, and the paths configured with the
     <link linkend='conf-build-chroot-dirs'><literal>build-chroot-dirs</literal>
     option</link>. This is useful to prevent undeclared dependencies
-    on files in directories such as
-    <filename>/usr/bin</filename>.</para>
-
-    <para>The use of a chroot requires that Nix is run as root (so you
-    should use the <link linkend='conf-build-users-group'>“build
-    users” feature</link> to perform the actual builds under different
-    users than root).  Currently, chroot builds only work on Linux
-    because Nix uses “bind mounts” to make the Nix store and other
-    directories available inside the chroot. Kernel version 3.13 or later
-    is needed.</para>
+    on files in directories such as <filename>/usr/bin</filename>. In
+    addition, on Linux, builds run in rivate PID, mount, network, IPC
+    and UTS namespaces to isolate them from other processes in the
+    system (except that fixed-output derivations do not run in private
+    network namespace to ensure they can access the network).</para>
+
+    <para>Currently, chroots only work on Linux and Mac OS X. The use
+    of a chroot requires that Nix is run as root (so you should use
+    the <link linkend='conf-build-users-group'>“build users”
+    feature</link> to perform the actual builds under different users
+    than root).</para>
+
+    <para>If this option is set to <literal>relaxed</literal>, then
+    fixed-output derivations and derivations that have the
+    <varname>__noChroot</varname> attribute set to
+    <literal>true</literal> do not run in chroots.</para>
+
+    <para>The default is <literal>false</literal>.</para>
 
     </listitem>
 
diff --git a/src/libstore/build.cc b/src/libstore/build.cc
index e64bd3fef587..ac5ca7bc3a36 100644
--- a/src/libstore/build.cc
+++ b/src/libstore/build.cc
@@ -1768,12 +1768,20 @@ void DerivationGoal::startBuilder()
        functions like fetchurl (which needs a proper /etc/resolv.conf)
        work properly.  Purity checking for fixed-output derivations
        is somewhat pointless anyway. */
-    useChroot = settings.useChroot;
-
-    if (fixedOutput) useChroot = false;
-
-    /* Hack to allow derivations to disable chroot builds. */
-    if (get(drv.env, "__noChroot") == "1") useChroot = false;
+    {
+        string x = settings.get("build-use-chroot", string("false"));
+        if (x != "true" && x != "false" && x != "relaxed")
+            throw Error("option ‘build-use-chroot’ must be set to one of ‘true’, ‘false’ or ‘relaxed’");
+        if (x == "true") {
+            if (get(drv.env, "__noChroot") == "1")
+                throw Error(format("derivation ‘%1%’ has ‘__noChroot’ set, but that's not allowed when ‘build-use-chroot’ is ‘true’") % drvPath);
+            useChroot = true;
+        }
+        else if (x == "false")
+            useChroot = false;
+        else if (x == "relaxed")
+            useChroot = !fixedOutput && get(drv.env, "__noChroot") != "1";
+    }
 
     if (useChroot) {
         /* Allow a user-configurable set of directories from the
@@ -1856,7 +1864,8 @@ void DerivationGoal::startBuilder()
                 % (buildUser.enabled() ? buildUser.getGID() : getgid())).str());
 
         /* Create /etc/hosts with localhost entry. */
-        writeFile(chrootRootDir + "/etc/hosts", "127.0.0.1 localhost\n");
+        if (!fixedOutput)
+            writeFile(chrootRootDir + "/etc/hosts", "127.0.0.1 localhost\n");
 
         /* Make the closure of the inputs available in the chroot,
            rather than the whole Nix store.  This prevents any access
@@ -1964,7 +1973,9 @@ void DerivationGoal::startBuilder()
 
            - The private network namespace ensures that the builder
              cannot talk to the outside world (or vice versa).  It
-             only has a private loopback interface.
+             only has a private loopback interface. (Fixed-output
+             derivations are not run in a private network namespace
+             to allow functions like fetchurl to work.)
 
            - The IPC namespace prevents the builder from communicating
              with outside processes using SysV IPC mechanisms (shared
@@ -1983,8 +1994,9 @@ void DerivationGoal::startBuilder()
         */
         Pid helper = startProcess([&]() {
             char stack[32 * 1024];
-            pid_t child = clone(childEntry, stack + sizeof(stack) - 8,
-                CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWNET | CLONE_NEWIPC | CLONE_NEWUTS | CLONE_PARENT | SIGCHLD, this);
+            int flags = CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWIPC | CLONE_NEWUTS | CLONE_PARENT | SIGCHLD;
+            if (!fixedOutput) flags |= CLONE_NEWNET;
+            pid_t child = clone(childEntry, stack + sizeof(stack) - 8, flags, this);
             if (child == -1) {
                 if (errno == EINVAL)
                     throw SysError("cloning builder process (Linux chroot builds require 3.13 or later)");
@@ -2081,10 +2093,10 @@ void DerivationGoal::runChild()
 
             /* Set up a nearly empty /dev, unless the user asked to
                bind-mount the host /dev. */
+            Strings ss;
             if (dirsInChroot.find("/dev") == dirsInChroot.end()) {
                 createDirs(chrootRootDir + "/dev/shm");
                 createDirs(chrootRootDir + "/dev/pts");
-                Strings ss;
                 ss.push_back("/dev/full");
 #ifdef __linux__
                 if (pathExists("/dev/kvm"))
@@ -2095,13 +2107,24 @@ void DerivationGoal::runChild()
                 ss.push_back("/dev/tty");
                 ss.push_back("/dev/urandom");
                 ss.push_back("/dev/zero");
-                foreach (Strings::iterator, i, ss) dirsInChroot[*i] = *i;
                 createSymlink("/proc/self/fd", chrootRootDir + "/dev/fd");
                 createSymlink("/proc/self/fd/0", chrootRootDir + "/dev/stdin");
                 createSymlink("/proc/self/fd/1", chrootRootDir + "/dev/stdout");
                 createSymlink("/proc/self/fd/2", chrootRootDir + "/dev/stderr");
             }
 
+            /* Fixed-output derivations typically need to access the
+               network, so give them access to /etc/resolv.conf and so
+               on. */
+            if (fixedOutput) {
+                ss.push_back("/etc/resolv.conf");
+                ss.push_back("/etc/nsswitch.conf");
+                ss.push_back("/etc/services");
+                ss.push_back("/etc/hosts");
+            }
+
+            for (auto & i : ss) dirsInChroot[i] = i;
+
             /* Bind-mount all the directories from the "host"
                filesystem that we want in the chroot
                environment. */
diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc
index c5abeee282a3..e382b3aac03a 100644
--- a/src/libstore/globals.cc
+++ b/src/libstore/globals.cc
@@ -47,7 +47,6 @@ Settings::Settings()
     syncBeforeRegistering = false;
     useSubstitutes = true;
     buildUsersGroup = getuid() == 0 ? "nixbld" : "";
-    useChroot = false;
     useSshSubstituter = true;
     impersonateLinux26 = false;
     keepLog = true;
@@ -158,7 +157,6 @@ void Settings::update()
     _get(syncBeforeRegistering, "sync-before-registering");
     _get(useSubstitutes, "build-use-substitutes");
     _get(buildUsersGroup, "build-users-group");
-    _get(useChroot, "build-use-chroot");
     _get(impersonateLinux26, "build-impersonate-linux-26");
     _get(keepLog, "build-keep-log");
     _get(compressLog, "build-compress-log");
diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh
index c17e10d7c32d..0230a540e655 100644
--- a/src/libstore/globals.hh
+++ b/src/libstore/globals.hh
@@ -145,9 +145,6 @@ struct Settings {
     /* The Unix group that contains the build users. */
     string buildUsersGroup;
 
-    /* Whether to build in chroot. */
-    bool useChroot;
-
     /* Set of ssh connection strings for the ssh substituter */
     Strings sshSubstituterHosts;