diff options
author | Artemis Tosini <lix@artem.ist> | 2024-05-19 22:25:30 +0000 |
---|---|---|
committer | Artemis Tosini <me@artem.ist> | 2024-07-03 22:16:03 +0000 |
commit | af1dcc2d5e5f9f1bc01e12face96259cf4183629 (patch) | |
tree | 7a61f880dc89afba1c8db4aec8169d46595325bc /src/libstore/platform | |
parent | 5eec6418de35daaa7b14b5412e39d85ce80a37cb (diff) |
libstore: Add LocalDerivationGoal prepareSandbox hook
Add a new OS-specific hook called `prepareSandbox`, run before forking
On Darwin this is empty as nothing is required,
on Linux this creates the chroot directory and adds basic files,
and on platforms using a fallback this throws an exception
Change-Id: Ie30c38c387f2e0e5844b2afa32fd4d33b1180dae
Diffstat (limited to 'src/libstore/platform')
-rw-r--r-- | src/libstore/platform/darwin.hh | 6 | ||||
-rw-r--r-- | src/libstore/platform/linux.cc | 88 | ||||
-rw-r--r-- | src/libstore/platform/linux.hh | 16 |
3 files changed, 110 insertions, 0 deletions
diff --git a/src/libstore/platform/darwin.hh b/src/libstore/platform/darwin.hh index 0ac7077fb..70e8a8587 100644 --- a/src/libstore/platform/darwin.hh +++ b/src/libstore/platform/darwin.hh @@ -43,6 +43,12 @@ public: private: /** + * Prepare the sandbox: This is empty on Darwin since sandbox setup happens in + * enterSandbox + */ + void prepareSandbox() override{}; + + /** * Set process flags to enter or leave rosetta, then execute the builder */ void execBuilder(std::string builder, Strings args, Strings envStrs) override; diff --git a/src/libstore/platform/linux.cc b/src/libstore/platform/linux.cc index 6b94c01cc..ac0440e5a 100644 --- a/src/libstore/platform/linux.cc +++ b/src/libstore/platform/linux.cc @@ -1,3 +1,4 @@ +#include "build/worker.hh" #include "cgroup.hh" #include "gc-store.hh" #include "signals.hh" @@ -116,6 +117,93 @@ void LinuxLocalStore::findPlatformRoots(UncheckedRoots & unchecked) readFileRoots("/proc/sys/kernel/poweroff_cmd", unchecked); } +void LinuxLocalDerivationGoal::prepareSandbox() +{ + /* Create a temporary directory in which we set up the chroot + environment using bind-mounts. We put it in the Nix store + to ensure that we can create hard-links to non-directory + inputs in the fake Nix store in the chroot (see below). */ + chrootRootDir = worker.store.Store::toRealPath(drvPath) + ".chroot"; + deletePath(chrootRootDir); + + /* Clean up the chroot directory automatically. */ + autoDelChroot = std::make_shared<AutoDelete>(chrootRootDir); + + printMsg(lvlChatty, "setting up chroot environment in '%1%'", chrootRootDir); + + // FIXME: make this 0700 + if (mkdir(chrootRootDir.c_str(), buildUser && buildUser->getUIDCount() != 1 ? 0755 : 0750) == -1) + throw SysError("cannot create '%1%'", chrootRootDir); + + if (buildUser && chown(chrootRootDir.c_str(), buildUser->getUIDCount() != 1 ? buildUser->getUID() : 0, buildUser->getGID()) == -1) + throw SysError("cannot change ownership of '%1%'", chrootRootDir); + + /* Create a writable /tmp in the chroot. Many builders need + this. (Of course they should really respect $TMPDIR + instead.) */ + Path chrootTmpDir = chrootRootDir + "/tmp"; + createDirs(chrootTmpDir); + chmodPath(chrootTmpDir, 01777); + + /* Create a /etc/passwd with entries for the build user and the + nobody account. The latter is kind of a hack to support + Samba-in-QEMU. */ + createDirs(chrootRootDir + "/etc"); + if (parsedDrv->useUidRange()) + chownToBuilder(chrootRootDir + "/etc"); + + if (parsedDrv->useUidRange() && (!buildUser || buildUser->getUIDCount() < 65536)) + throw Error("feature 'uid-range' requires the setting '%s' to be enabled", settings.autoAllocateUids.name); + + /* Create /etc/hosts with localhost entry. */ + if (derivationType->isSandboxed()) + writeFile(chrootRootDir + "/etc/hosts", "127.0.0.1 localhost\n::1 localhost\n"); + + /* Make the closure of the inputs available in the chroot, + rather than the whole Nix store. This prevents any access + to undeclared dependencies. Directories are bind-mounted, + while other inputs are hard-linked (since only directories + can be bind-mounted). !!! As an extra security + precaution, make the fake Nix store only writable by the + build user. */ + Path chrootStoreDir = chrootRootDir + worker.store.storeDir; + createDirs(chrootStoreDir); + chmodPath(chrootStoreDir, 01775); + + if (buildUser && chown(chrootStoreDir.c_str(), 0, buildUser->getGID()) == -1) + throw SysError("cannot change ownership of '%1%'", chrootStoreDir); + + for (auto & i : inputPaths) { + auto p = worker.store.printStorePath(i); + Path r = worker.store.toRealPath(p); + pathsInChroot.insert_or_assign(p, r); + } + + /* If we're repairing, checking or rebuilding part of a + multiple-outputs derivation, it's possible that we're + rebuilding a path that is in settings.sandbox-paths + (typically the dependencies of /bin/sh). Throw them + out. */ + for (auto & i : drv->outputsAndOptPaths(worker.store)) { + /* If the name isn't known a priori (i.e. floating + content-addressed derivation), the temporary location we use + should be fresh. Freshness means it is impossible that the path + is already in the sandbox, so we don't need to worry about + removing it. */ + if (i.second.second) + pathsInChroot.erase(worker.store.printStorePath(*i.second.second)); + } + + if (cgroup) { + if (mkdir(cgroup->c_str(), 0755) != 0) + throw SysError("creating cgroup '%s'", *cgroup); + chownToBuilder(*cgroup); + chownToBuilder(*cgroup + "/cgroup.procs"); + chownToBuilder(*cgroup + "/cgroup.threads"); + //chownToBuilder(*cgroup + "/cgroup.subtree_control"); + } +} + void LinuxLocalDerivationGoal::killSandbox(bool getStats) { if (cgroup) { diff --git a/src/libstore/platform/linux.hh b/src/libstore/platform/linux.hh index 2cad001ea..7ef578d2c 100644 --- a/src/libstore/platform/linux.hh +++ b/src/libstore/platform/linux.hh @@ -42,7 +42,23 @@ public: using LocalDerivationGoal::LocalDerivationGoal; private: + /** + * Create and populate chroot + */ + void prepareSandbox() override; + + /** + * Kill all processes by build user, possibly using a reused + * cgroup if we have one + */ void killSandbox(bool getStatus) override; + + + bool supportsUidRange() override + { + return true; + } + }; } |