about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEelco Dolstra <eelco.dolstra@logicblox.com>2012-06-23T04·28-0400
committerEelco Dolstra <eelco.dolstra@logicblox.com>2012-06-23T04·28-0400
commitdf716c98d203ab64cdf05f9c17fdae565b7daa1c (patch)
tree517c173c82cb5e65df3fd3ff4044ca0fadd04097
parent2f3f413e91620d2314be59870f4bd67292f7d2de (diff)
In chroot builds, use a private network namespace
On Linux it's possible to run a process in its own network namespace,
meaning that it gets its own set of network interfaces, disjunct from
the rest of the system.  We use this to completely remove network
access to chroot builds, except that they get a private loopback
interface.  This means that:

- Builders cannot connect to the outside network or to other processes
  on the same machine, except processes within the same build.

- Vice versa, other processes cannot connect to processes in a chroot
  build, and open ports/connections do not show up in "netstat".

- If two concurrent builders try to listen on the same port (e.g. as
  part of a test), they no longer conflict with each other.

This was inspired by the "PrivateNetwork" flag in systemd.
-rw-r--r--src/libstore/build.cc37
1 files changed, 31 insertions, 6 deletions
diff --git a/src/libstore/build.cc b/src/libstore/build.cc
index c27f8db5dc5d..0fb5eb0a86a0 100644
--- a/src/libstore/build.cc
+++ b/src/libstore/build.cc
@@ -47,6 +47,13 @@
 
 #define CHROOT_ENABLED HAVE_CHROOT && HAVE_UNSHARE && HAVE_SYS_MOUNT_H && defined(MS_BIND) && defined(CLONE_NEWNS)
 
+#if CHROOT_ENABLED
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <netinet/ip.h>
+#endif
+
 
 #if HAVE_SYS_PERSONALITY_H
 #include <sys/personality.h>
@@ -1769,12 +1776,30 @@ void DerivationGoal::startBuilder()
 
 #if CHROOT_ENABLED
             if (useChroot) {
-                /* Create our own mount namespace.  This means that
-                   all the bind mounts we do will only show up in this
-                   process and its children, and will disappear
-                   automatically when we're done. */
-                if (unshare(CLONE_NEWNS) == -1)
-                    throw SysError(format("cannot set up a private mount namespace"));
+                /* Create our own mount and network namespace.  This
+                   means that all the bind mounts we do will only show
+                   up in this process and its children, and will
+                   disappear automatically when we're done.
+                   Similarly, this process will not have any network
+                   interface except "lo" created below. */
+                if (unshare(CLONE_NEWNS | CLONE_NEWNET) == -1)
+                    throw SysError("cannot set up a private mount namespace");
+
+                /* Initialise the loopback interface.  Note that this
+                   loopback device is unique to this process and its
+                   children.  Thus they won't be able to open
+                   connections to the rest of the system, or vice
+                   versa. */
+                AutoCloseFD fd(socket(PF_INET, SOCK_DGRAM, IPPROTO_IP));
+                if (fd == -1) throw SysError("cannot open IP socket");
+                
+                struct ifreq ifr;
+                strcpy(ifr.ifr_name, "lo");
+                ifr.ifr_flags = IFF_UP | IFF_LOOPBACK | IFF_RUNNING;
+                if (ioctl(fd, SIOCSIFFLAGS, &ifr) == -1)
+                    throw SysError("cannot set loopback interface flags");
+
+                fd.close();
 
                 /* Bind-mount all the directories from the "host"
                    filesystem that we want in the chroot