aboutsummaryrefslogtreecommitdiff
path: root/src/libstore/build/derivation-goal.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/libstore/build/derivation-goal.cc')
-rw-r--r--src/libstore/build/derivation-goal.cc99
1 files changed, 73 insertions, 26 deletions
diff --git a/src/libstore/build/derivation-goal.cc b/src/libstore/build/derivation-goal.cc
index 241e52116..822cfd817 100644
--- a/src/libstore/build/derivation-goal.cc
+++ b/src/libstore/build/derivation-goal.cc
@@ -561,6 +561,7 @@ void DerivationGoal::inputsRealised()
result = BuildResult();
}
+
void DerivationGoal::started() {
auto msg = fmt(
buildMode == bmRepair ? "repairing outputs of '%s'" :
@@ -575,6 +576,7 @@ void DerivationGoal::started() {
worker.updateProgress();
}
+
void DerivationGoal::tryToBuild()
{
trace("trying to build");
@@ -683,28 +685,21 @@ void DerivationGoal::tryLocalBuild() {
return;
}
- /* If `build-users-group' is not empty, then we have to build as
- one of the members of that group. */
- if (settings.buildUsersGroup != "" && getuid() == 0) {
-#if defined(__linux__) || defined(__APPLE__)
- if (!buildUser) buildUser = std::make_unique<UserLock>();
+ if (useBuildUsers()) {
+ if (!buildUser)
+ buildUser = acquireUserLock();
- if (buildUser->findFreeUser()) {
- /* Make sure that no other processes are executing under this
- uid. */
- buildUser->kill();
- } else {
+ if (!buildUser) {
if (!actLock)
actLock = std::make_unique<Activity>(*logger, lvlWarn, actBuildWaiting,
fmt("waiting for UID to build '%s'", yellowtxt(worker.store.printStorePath(drvPath))));
worker.waitForAWhile(shared_from_this());
return;
}
-#else
- /* Don't know how to block the creation of setuid/setgid
- binaries on this platform. */
- throw Error("build users are not supported on this platform for security reasons");
-#endif
+
+ /* Make sure that no other processes are executing under this
+ uid. */
+ buildUser->kill();
}
actLock.reset();
@@ -1322,6 +1317,9 @@ void DerivationGoal::startBuilder()
}
}
+ useUidRange = parsedDrv->getRequiredSystemFeatures().count("uid-range");
+ useSystemdCgroup = parsedDrv->getRequiredSystemFeatures().count("systemd-cgroup");
+
if (useChroot) {
/* Allow a user-configurable set of directories from the
@@ -1402,10 +1400,11 @@ void DerivationGoal::startBuilder()
printMsg(lvlChatty, format("setting up chroot environment in '%1%'") % chrootRootDir);
- if (mkdir(chrootRootDir.c_str(), 0750) == -1)
+ if (mkdir(chrootRootDir.c_str(), useUidRange ? 0755 : 0750) == -1)
throw SysError("cannot create '%1%'", chrootRootDir);
- if (buildUser && chown(chrootRootDir.c_str(), 0, buildUser->getGID()) == -1)
+ // FIXME: only make root writable for user namespace builds.
+ if (buildUser && chown(chrootRootDir.c_str(), buildUser->getUID(), buildUser->getGID()) == -1)
throw SysError("cannot change ownership of '%1%'", chrootRootDir);
/* Create a writable /tmp in the chroot. Many builders need
@@ -1419,6 +1418,10 @@ void DerivationGoal::startBuilder()
nobody account. The latter is kind of a hack to support
Samba-in-QEMU. */
createDirs(chrootRootDir + "/etc");
+ chownToBuilder(chrootRootDir + "/etc");
+
+ if (useUidRange && (!buildUser || buildUser->getUIDCount() < 65536))
+ throw Error("feature 'uid-range' requires '%s' to be enabled", settings.autoAllocateUids.name);
writeFile(chrootRootDir + "/etc/passwd", fmt(
"root:x:0:0:Nix build user:%3%:/noshell\n"
@@ -1475,12 +1478,32 @@ void DerivationGoal::startBuilder()
dirsInChroot.erase(worker.store.printStorePath(*i.second.second));
}
-#elif __APPLE__
- /* We don't really have any parent prep work to do (yet?)
- All work happens in the child, instead. */
+ if (useSystemdCgroup) {
+ settings.requireExperimentalFeature("systemd-cgroup");
+ std::optional<Path> cgroup;
+ if (!buildUser || !(cgroup = buildUser->getCgroup()))
+ throw Error("feature 'systemd-cgroup' requires 'auto-allocate-uids = true' in nix.conf");
+ chownToBuilder(*cgroup);
+ chownToBuilder(*cgroup + "/cgroup.procs");
+ }
+
#else
- throw Error("sandboxing builds is not supported on this platform");
+ if (useUidRange)
+ throw Error("feature 'uid-range' is not supported on this platform");
+ if (useSystemdCgroup)
+ throw Error("feature 'systemd-cgroup' is not supported on this platform");
+ #if __APPLE__
+ /* We don't really have any parent prep work to do (yet?)
+ All work happens in the child, instead. */
+ #else
+ throw Error("sandboxing builds is not supported on this platform");
+ #endif
#endif
+ } else {
+ if (useUidRange)
+ throw Error("feature 'uid-range' is only supported in sandboxed builds");
+ if (useSystemdCgroup)
+ throw Error("feature 'systemd-cgroup' is only supported in sandboxed builds");
}
if (needsHashRewrite() && pathExists(homeDir))
@@ -1716,14 +1739,16 @@ void DerivationGoal::startBuilder()
the calling user (if build users are disabled). */
uid_t hostUid = buildUser ? buildUser->getUID() : getuid();
uid_t hostGid = buildUser ? buildUser->getGID() : getgid();
+ uint32_t nrIds = buildUser && useUidRange ? buildUser->getUIDCount() : 1;
writeFile("/proc/" + std::to_string(pid) + "/uid_map",
- fmt("%d %d 1", sandboxUid(), hostUid));
+ fmt("%d %d %d", sandboxUid(), hostUid, nrIds));
- writeFile("/proc/" + std::to_string(pid) + "/setgroups", "deny");
+ if (!useUidRange)
+ writeFile("/proc/" + std::to_string(pid) + "/setgroups", "deny");
writeFile("/proc/" + std::to_string(pid) + "/gid_map",
- fmt("%d %d 1", sandboxGid(), hostGid));
+ fmt("%d %d %d", sandboxGid(), hostGid, nrIds));
} else {
debug("note: not using a user namespace");
if (!buildUser)
@@ -1736,6 +1761,12 @@ void DerivationGoal::startBuilder()
if (sandboxMountNamespace.get() == -1)
throw SysError("getting sandbox mount namespace");
+ /* Move the child into its own cgroup. */
+ if (buildUser) {
+ if (auto cgroup = buildUser->getCgroup())
+ writeFile(*cgroup + "/cgroup.procs", fmt("%d", (pid_t) pid));
+ }
+
/* Signal the builder that we've updated its user namespace. */
writeFull(userNamespaceSync.writeSide.get(), "1");
@@ -2524,6 +2555,13 @@ void DerivationGoal::runChild()
if (mount("none", (chrootRootDir + "/proc").c_str(), "proc", 0, 0) == -1)
throw SysError("mounting /proc");
+ /* Mount sysfs on /sys. */
+ if (useUidRange) {
+ createDirs(chrootRootDir + "/sys");
+ if (mount("none", (chrootRootDir + "/sys").c_str(), "sysfs", 0, 0) == -1)
+ throw SysError("mounting /sys");
+ }
+
/* Mount a new tmpfs on /dev/shm to ensure that whatever
the builder puts in /dev/shm is cleaned up automatically. */
if (pathExists("/dev/shm") && mount("none", (chrootRootDir + "/dev/shm").c_str(), "tmpfs", 0,
@@ -2566,6 +2604,12 @@ void DerivationGoal::runChild()
if (unshare(CLONE_NEWNS) == -1)
throw SysError("unsharing mount namespace");
+ /* Unshare the cgroup namespace. This means
+ /proc/self/cgroup will show the child's cgroup as '/'
+ rather than whatever it is in the parent. */
+ if (useSystemdCgroup && unshare(CLONE_NEWCGROUP) == -1)
+ throw SysError("unsharing cgroup namespace");
+
/* Do the chroot(). */
if (chdir(chrootRootDir.c_str()) == -1)
throw SysError("cannot change directory to '%1%'", chrootRootDir);
@@ -2946,7 +2990,10 @@ void DerivationGoal::registerOutputs()
/* Canonicalise first. This ensures that the path we're
rewriting doesn't contain a hard link to /etc/shadow or
something like that. */
- canonicalisePathMetaData(actualPath, buildUser ? buildUser->getUID() : -1, inodesSeen);
+ canonicalisePathMetaData(
+ actualPath,
+ buildUser ? std::optional(buildUser->getUIDRange()) : std::nullopt,
+ inodesSeen);
debug("scanning for references for output '%s' in temp location '%s'", outputName, actualPath);
@@ -3035,7 +3082,7 @@ void DerivationGoal::registerOutputs()
/* FIXME: set proper permissions in restorePath() so
we don't have to do another traversal. */
- canonicalisePathMetaData(actualPath, -1, inodesSeen);
+ canonicalisePathMetaData(actualPath, {}, inodesSeen);
}
};