libstore: move netrc/ca init outside of the build child process

this will make it easier to turn build sandbox processes into helpers.
the point they were at was effectivly unsandboxed except for a few fds
that were redirected by commonExecveingChildInit, which only made logs
of any errors that much harder to convey from the child to the parent.

Change-Id: I67006eb33e1e13311bb8d14e6a0c3d5e6baf0c13
This commit is contained in:
eldritch horrors
2026-01-21 15:59:30 +01:00
parent 5ba9a3961c
commit 6da0389d0f
4 changed files with 62 additions and 35 deletions
+42 -29
View File
@@ -35,6 +35,7 @@
#include <cstddef>
#include <dirent.h>
#include <exception>
#include <optional>
#include <regex>
#include <queue>
@@ -836,8 +837,31 @@ try {
buildResult.startTime = time(0);
/* Make the contents of netrc and the CA certificate bundle
available to builtin:fetchurl (which may run under a
different uid and/or in a sandbox). */
std::optional<std::string> netrcData;
std::optional<std::string> caFileData;
if (drv->isBuiltin() && drv->builder == "builtin:fetchurl" && !derivationType->isSandboxed()) {
try {
netrcData = readFile(settings.netrcFile);
} catch (SysError &) {
netrcData = "";
}
try {
caFileData = readFile(settings.caFile);
} catch (SysError &) {
caFileData = "";
}
}
if (!derivationType->isSandboxed()) {
setupConfiguredCertificateAuthority();
}
/* Fork a child to build the package. */
pid = startChild(std::move(builderOut));
pid = startChild(netrcData, caFileData, std::move(builderOut));
/* parent */
pid.setSeparatePG(true);
@@ -873,14 +897,18 @@ try {
co_return result::current_exception();
}
Pid LocalDerivationGoal::startChild(AutoCloseFD logPTY)
Pid LocalDerivationGoal::startChild(
const std::optional<std::string> & netrcData,
const std::optional<std::string> & caFileData,
AutoCloseFD logPTY
)
{
return startProcess([&]() {
if (dup2(logPTY.get(), STDERR_FILENO) == -1) {
throw SysError("failed to redirect build output to log file");
}
closeOnExec(STDERR_FILENO, false);
runChild();
runChild(netrcData, caFileData);
});
}
@@ -1115,8 +1143,9 @@ void LocalDerivationGoal::chownToBuilder(const AutoCloseFD & fd)
throw SysError("cannot change ownership of file '%1%'", fd.guessOrInventPath());
}
void LocalDerivationGoal::runChild()
void LocalDerivationGoal::runChild(
const std::optional<std::string> & netrcData, const std::optional<std::string> & caFileData
)
{
/* Warning: in the child we should absolutely not make any SQLite
calls! */
@@ -1127,25 +1156,6 @@ void LocalDerivationGoal::runChild()
commonExecveingChildInit();
/* Make the contents of netrc and the CA certificate bundle
available to builtin:fetchurl (which may run under a
different uid and/or in a sandbox). */
std::string netrcData;
std::string caFileData;
if (drv->isBuiltin() && drv->builder == "builtin:fetchurl" && !derivationType->isSandboxed()) {
try {
netrcData = readFile(settings.netrcFile);
} catch (SysError &) { }
try {
caFileData = readFile(settings.caFile);
} catch (SysError &) { }
}
if (!derivationType->isSandboxed()) {
setupConfiguredCertificateAuthority();
}
const bool setUser = prepareChildSetup();
if (sys::chdir(tmpDirInSandbox) == -1) {
@@ -1217,14 +1227,17 @@ void LocalDerivationGoal::runChild()
for (auto & e : drv2.env)
e.second = rewriteStrings(e.second, inputRewrites);
if (drv->builder == "builtin:fetchurl")
builtinFetchurl(drv2, netrcData, caFileData);
else if (drv->builder == "builtin:buildenv")
if (drv->builder == "builtin:fetchurl") {
assert(netrcData.has_value());
assert(caFileData.has_value());
builtinFetchurl(drv2, netrcData.value(), caFileData.value());
} else if (drv->builder == "builtin:buildenv") {
builtinBuildenv(drv2);
else if (drv->builder == "builtin:unpack-channel")
} else if (drv->builder == "builtin:unpack-channel") {
builtinUnpackChannel(drv2);
else
} else {
throw Error("unsupported builtin builder '%1%'", drv->builder.substr(8));
}
_exit(0);
} catch (std::exception & e) { // NOLINT(lix-foreign-exceptions)
writeFull(STDERR_FILENO, e.what() + std::string("\n"));
+7 -2
View File
@@ -214,7 +214,8 @@ struct LocalDerivationGoal : public DerivationGoal
/**
* Run the builder's process.
*/
void runChild();
void
runChild(const std::optional<std::string> & netrcData, const std::optional<std::string> & caFileData);
/**
* Check that the derivation outputs all exist and register them
@@ -311,7 +312,11 @@ protected:
* Create a new process that runs `openSlave` and `runChild`
* On some platforms this process is created with sandboxing flags.
*/
virtual Pid startChild(AutoCloseFD logPTY);
virtual Pid startChild(
const std::optional<std::string> & netrcData,
const std::optional<std::string> & caFileData,
AutoCloseFD logPTY
);
kj::Promise<Result<WorkResult>> handleRawChild() noexcept;
kj::Promise<Result<std::optional<WorkResult>>> handleRawChildStream() noexcept;
+8 -3
View File
@@ -20,6 +20,7 @@
#include <memory>
#include <net/if.h>
#include <netinet/in.h>
#include <optional>
#include <regex>
#include <sys/ioctl.h>
#include <sys/mount.h>
@@ -1237,7 +1238,11 @@ bool LinuxLocalDerivationGoal::prepareChildSetup()
return false;
}
Pid LinuxLocalDerivationGoal::startChild(AutoCloseFD logPTY)
Pid LinuxLocalDerivationGoal::startChild(
const std::optional<std::string> & netrcData,
const std::optional<std::string> & caFileData,
AutoCloseFD logPTY
)
{
#if HAVE_SECCOMP
// Our seccomp filter program is surprisingly expensive to compile (~10ms).
@@ -1248,7 +1253,7 @@ Pid LinuxLocalDerivationGoal::startChild(AutoCloseFD logPTY)
// If we're not sandboxing no need to faff about, use the fallback
if (!useChroot) {
return LocalDerivationGoal::startChild(std::move(logPTY));
return LocalDerivationGoal::startChild(netrcData, caFileData, std::move(logPTY));
}
/* Set up private namespaces for the build:
@@ -1324,7 +1329,7 @@ Pid LinuxLocalDerivationGoal::startChild(AutoCloseFD logPTY)
options.cloneFlags |= CLONE_NEWUSER;
}
pid_t child = startProcess([&]() { runChild(); }, options).release();
pid_t child = startProcess([&]() { runChild(netrcData, caFileData); }, options).release();
writeFull(sendPid.writeSide.get(), fmt("%d\n", child));
_exit(0);
+5 -1
View File
@@ -73,7 +73,11 @@ private:
* Start child process in new namespaces,
* create /etc/passwd and /etc/group based on discovered uid/gid
*/
Pid startChild(AutoCloseFD logPTY) override;
Pid startChild(
const std::optional<std::string> & netrcData,
const std::optional<std::string> & caFileData,
AutoCloseFD logPTY
) override;
/**
* Kill all processes by build user.