From df716c98d203ab64cdf05f9c17fdae565b7daa1c Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Sat, 23 Jun 2012 00:28:35 -0400 Subject: [PATCH] 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. --- src/libstore/build.cc | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/src/libstore/build.cc b/src/libstore/build.cc index c27f8db5dc..0fb5eb0a86 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 +#include +#include +#include +#endif + #if HAVE_SYS_PERSONALITY_H #include @@ -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