aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/build-remote/build-remote.cc43
-rw-r--r--src/libstore/daemon.cc2
-rw-r--r--src/libstore/store-api.cc43
-rw-r--r--src/libstore/store-api.hh27
-rw-r--r--src/libutil/experimental-features.cc1
-rw-r--r--src/libutil/experimental-features.hh8
-rw-r--r--src/nix/daemon.cc26
-rw-r--r--tests/build-remote-trustless-should-fail-0.sh11
-rw-r--r--tests/build-remote-trustless-should-pass-0.sh9
-rw-r--r--tests/build-remote-trustless-should-pass-1.sh9
-rw-r--r--tests/build-remote-trustless-should-pass-2.sh9
-rw-r--r--tests/build-remote-trustless-should-pass-3.sh10
-rw-r--r--tests/build-remote-trustless.sh16
-rw-r--r--tests/init.sh2
-rw-r--r--tests/local.mk5
-rwxr-xr-xtests/nix-daemon-untrusting.sh3
16 files changed, 194 insertions, 30 deletions
diff --git a/src/build-remote/build-remote.cc b/src/build-remote/build-remote.cc
index 9d2eacb54..d9e59d865 100644
--- a/src/build-remote/build-remote.cc
+++ b/src/build-remote/build-remote.cc
@@ -252,6 +252,8 @@ static int main_build_remote(int argc, char * * argv)
connected:
close(5);
+ assert(sshStore);
+
std::cerr << "# accept\n" << storeUri << "\n";
auto inputs = readStrings<PathSet>(source);
@@ -272,31 +274,39 @@ connected:
auto substitute = settings.buildersUseSubstitutes ? Substitute : NoSubstitute;
+ auto copyStorePathImpl = sshStore->isTrusting ? copyStorePathAdapter : copyOrBuildStorePath;
+
{
Activity act(*logger, lvlTalkative, actUnknown, fmt("copying dependencies to '%s'", storeUri));
- copyPaths(*store, *sshStore, store->parseStorePathSet(inputs), NoRepair, NoCheckSigs, substitute);
+ copyPaths(*store, *sshStore, store->parseStorePathSet(inputs), NoRepair, NoCheckSigs, substitute, copyStorePathImpl);
}
uploadLock = -1;
auto drv = store->readDerivation(*drvPath);
- auto outputHashes = staticOutputHashes(*store, drv);
-
- // Hijack the inputs paths of the derivation to include all the paths
- // that come from the `inputDrvs` set.
- // We don’t do that for the derivations whose `inputDrvs` is empty
- // because
- // 1. It’s not needed
- // 2. Changing the `inputSrcs` set changes the associated output ids,
- // which break CA derivations
- if (!drv.inputDrvs.empty())
- drv.inputSrcs = store->parseStorePathSet(inputs);
- auto result = sshStore->buildDerivation(*drvPath, drv);
+ std::optional<BuildResult> optResult;
+ if (sshStore->isTrusting || derivationIsCA(drv.type())) {
+ // Hijack the inputs paths of the derivation to include all the paths
+ // that come from the `inputDrvs` set.
+ // We don’t do that for the derivations whose `inputDrvs` is empty
+ // because
+ // 1. It’s not needed
+ // 2. Changing the `inputSrcs` set changes the associated output ids,
+ // which break CA derivations
+ if (!drv.inputDrvs.empty())
+ drv.inputSrcs = store->parseStorePathSet(inputs);
+ optResult = sshStore->buildDerivation(*drvPath, (const BasicDerivation &) drv);
+ auto & result = *optResult;
+ if (!result.success())
+ throw Error("build of '%s' on '%s' failed: %s", store->printStorePath(*drvPath), storeUri, result.errorMsg);
+ } else {
+ copyPaths(*store, *sshStore, StorePathSet {*drvPath}, NoRepair, NoCheckSigs, substitute, copyStorePathImpl);
+ sshStore->buildPaths({ DerivedPath::Built { *drvPath, {} } });
+ }
- if (!result.success())
- throw Error("build of '%s' on '%s' failed: %s", store->printStorePath(*drvPath), storeUri, result.errorMsg);
+ auto outputHashes = staticOutputHashes(*store, drv);
std::set<Realisation> missingRealisations;
StorePathSet missingPaths;
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations) && !derivationHasKnownOutputPaths(drv.type())) {
@@ -305,6 +315,8 @@ connected:
auto thisOutputId = DrvOutput{ thisOutputHash, outputName };
if (!store->queryRealisation(thisOutputId)) {
debug("missing output %s", outputName);
+ assert(optResult);
+ auto & result = *optResult;
assert(result.builtOutputs.count(thisOutputId));
auto newRealisation = result.builtOutputs.at(thisOutputId);
missingRealisations.insert(newRealisation);
@@ -325,6 +337,7 @@ connected:
if (auto localStore = store.dynamic_pointer_cast<LocalStore>())
for (auto & path : missingPaths)
localStore->locksHeld.insert(store->printStorePath(path)); /* FIXME: ugly */
+ /* No `copyStorePathImpl` because we always trust ourselves. */
copyPaths(*sshStore, *store, missingPaths, NoRepair, NoCheckSigs, NoSubstitute);
}
// XXX: Should be done as part of `copyPaths`
diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc
index 2ba03f0dd..971adef51 100644
--- a/src/libstore/daemon.cc
+++ b/src/libstore/daemon.cc
@@ -1014,6 +1014,8 @@ void processConnection(
opCount++;
+ debug("performing daemon worker op: %d", op);
+
try {
performOp(tunnelLogger, store, trusted, recursive, clientVersion, from, to, op);
} catch (Error & e) {
diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc
index 86fa6a211..51485bd43 100644
--- a/src/libstore/store-api.cc
+++ b/src/libstore/store-api.cc
@@ -923,13 +923,47 @@ void copyStorePath(
}
+void copyStorePathAdapter(Store & srcStore, Store & dstStore,
+ const ValidPathInfo & info, RepairFlag repair, CheckSigsFlag checkSigs)
+{
+ copyStorePath(srcStore, dstStore, info.path, repair, checkSigs);
+}
+
+void copyOrBuildStorePath(Store & srcStore, Store & dstStore,
+ const ValidPathInfo & info, RepairFlag repair, CheckSigsFlag checkSigs)
+{
+ auto storePath = info.path;
+ if (dstStore.isTrusting || info.ca) {
+ copyStorePath(srcStore, dstStore, storePath, repair, checkSigs);
+ } else if (info.deriver && dstStore.storeDir == srcStore.storeDir) {
+ auto drvPath = *info.deriver;
+ auto outputMap = srcStore.queryDerivationOutputMap(drvPath);
+ auto p = std::find_if(outputMap.begin(), outputMap.end(), [&](auto & i) {
+ return i.second == storePath;
+ });
+ // drv file is always CA
+ srcStore.ensurePath(drvPath);
+ copyStorePath(srcStore, dstStore, drvPath, repair, checkSigs);
+ dstStore.buildPaths({
+ DerivedPath::Built {
+ .drvPath = drvPath,
+ .outputs = p != outputMap.end() ? StringSet { p->first } : StringSet {},
+ },
+ });
+ } else {
+ dstStore.ensurePath(storePath);
+ }
+}
+
+
std::map<StorePath, StorePath> copyPaths(
Store & srcStore,
Store & dstStore,
const RealisedPath::Set & paths,
RepairFlag repair,
CheckSigsFlag checkSigs,
- SubstituteFlag substitute)
+ SubstituteFlag substitute,
+ std::function<void(Store &, Store &, const ValidPathInfo &, RepairFlag, CheckSigsFlag)> copyStorePathImpl)
{
StorePathSet storePaths;
std::set<Realisation> toplevelRealisations;
@@ -940,7 +974,7 @@ std::map<StorePath, StorePath> copyPaths(
toplevelRealisations.insert(*realisation);
}
}
- auto pathsMap = copyPaths(srcStore, dstStore, storePaths, repair, checkSigs, substitute);
+ auto pathsMap = copyPaths(srcStore, dstStore, storePaths, repair, checkSigs, substitute, copyStorePathImpl);
ThreadPool pool;
@@ -983,7 +1017,8 @@ std::map<StorePath, StorePath> copyPaths(
const StorePathSet & storePaths,
RepairFlag repair,
CheckSigsFlag checkSigs,
- SubstituteFlag substitute)
+ SubstituteFlag substitute,
+ std::function<void(Store &, Store &, const ValidPathInfo &, RepairFlag, CheckSigsFlag)> copyStorePathImpl)
{
auto valid = dstStore.queryValidPaths(storePaths, substitute);
@@ -1083,7 +1118,7 @@ std::map<StorePath, StorePath> copyPaths(
MaintainCount<decltype(nrRunning)> mc(nrRunning);
showProgress();
try {
- copyStorePath(srcStore, dstStore, storePath, repair, checkSigs);
+ copyStorePathImpl(srcStore, dstStore, *info, repair, checkSigs);
} catch (Error &e) {
nrFailed++;
if (!settings.keepGoing)
diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh
index e4fb1f1fd..6b2ad4b68 100644
--- a/src/libstore/store-api.hh
+++ b/src/libstore/store-api.hh
@@ -216,7 +216,9 @@ struct StoreConfig : public Config
const Setting<int> pathInfoCacheSize{this, 65536, "path-info-cache-size", "size of the in-memory store path information cache"};
- const Setting<bool> isTrusted{this, false, "trusted", "whether paths from this store can be used as substitutes even when they lack trusted signatures"};
+ const Setting<bool> isTrusted{this, false, "trusted", "whether paths from this store can be used as substitutes even when they lack trusted signatures. Compare \"trusting\""};
+
+ Setting<bool> isTrusting{this, true, "trusting", "whether (we think) paths can be added to this store even when they lack trusted signatures. Compare \"trusted\""};
Setting<int> priority{this, 0, "priority", "priority of this substituter (lower value means higher priority)"};
@@ -800,25 +802,42 @@ void copyStorePath(
CheckSigsFlag checkSigs = CheckSigs);
+/* copyStorePath wrapped to be used with `copyPaths`. */
+void copyStorePathAdapter(Store & srcStore, Store & dstStore,
+ const ValidPathInfo & info, RepairFlag repair = NoRepair, CheckSigsFlag checkSigs = CheckSigs);
+
+/* The more liberal alternative to `copyStorePathAdapter`, useful for remote
+ stores that do not trust us. */
+void copyOrBuildStorePath(Store & srcStore, Store & dstStore,
+ const ValidPathInfo & info, RepairFlag repair = NoRepair, CheckSigsFlag checkSigs = CheckSigs);
+
/* Copy store paths from one store to another. The paths may be copied
in parallel. They are copied in a topologically sorted order (i.e.
if A is a reference of B, then A is copied before B), but the set
of store paths is not automatically closed; use copyClosure() for
that. Returns a map of what each path was copied to the dstStore
- as. */
+ as.
+
+ The `copyStorePathImpl` parameter allows doing something other than just
+ copying. For example, this is used with the build hook to allow the other
+ side to build dependencies we don't have permission to copy. This behavior
+ isn't just the default that way `nix copy` etc. still can be relied upon to
+ not build anything. */
std::map<StorePath, StorePath> copyPaths(
Store & srcStore, Store & dstStore,
const RealisedPath::Set &,
RepairFlag repair = NoRepair,
CheckSigsFlag checkSigs = CheckSigs,
- SubstituteFlag substitute = NoSubstitute);
+ SubstituteFlag substitute = NoSubstitute,
+ std::function<void(Store &, Store &, const ValidPathInfo &, RepairFlag, CheckSigsFlag)> copyStorePathImpl = copyStorePathAdapter);
std::map<StorePath, StorePath> copyPaths(
Store & srcStore, Store & dstStore,
const StorePathSet & paths,
RepairFlag repair = NoRepair,
CheckSigsFlag checkSigs = CheckSigs,
- SubstituteFlag substitute = NoSubstitute);
+ SubstituteFlag substitute = NoSubstitute,
+ std::function<void(Store &, Store &, const ValidPathInfo &, RepairFlag, CheckSigsFlag)> copyStorePathImpl = copyStorePathAdapter);
/* Copy the closure of `paths` from `srcStore` to `dstStore`. */
void copyClosure(
diff --git a/src/libutil/experimental-features.cc b/src/libutil/experimental-features.cc
index b49f47e1d..687857869 100644
--- a/src/libutil/experimental-features.cc
+++ b/src/libutil/experimental-features.cc
@@ -11,6 +11,7 @@ std::map<ExperimentalFeature, std::string> stringifiedXpFeatures = {
{ Xp::NixCommand, "nix-command" },
{ Xp::RecursiveNix, "recursive-nix" },
{ Xp::NoUrlLiterals, "no-url-literals" },
+ { Xp::NixTesting, "nix-testing" },
};
const std::optional<ExperimentalFeature> parseExperimentalFeature(const std::string_view & name)
diff --git a/src/libutil/experimental-features.hh b/src/libutil/experimental-features.hh
index 291a58e32..6c5f55bd6 100644
--- a/src/libutil/experimental-features.hh
+++ b/src/libutil/experimental-features.hh
@@ -19,7 +19,13 @@ enum struct ExperimentalFeature
Flakes,
NixCommand,
RecursiveNix,
- NoUrlLiterals
+ NoUrlLiterals,
+
+ /**
+ * A "permanent" experimental feature for extra features we just
+ * need for testing.
+ **/
+ NixTesting,
};
/**
diff --git a/src/nix/daemon.cc b/src/nix/daemon.cc
index 940923d3b..b592de5f7 100644
--- a/src/nix/daemon.cc
+++ b/src/nix/daemon.cc
@@ -265,10 +265,16 @@ static void daemonLoop()
}
}
-static void runDaemon(bool stdio)
+static void runDaemon(bool stdio, std::optional<TrustedFlag> isTrustedOpt = {})
{
+ auto ensureNoTrustedFlag = [&]() {
+ if (isTrustedOpt)
+ throw Error("--trust and --no-trust flags are only for use with --stdio when this nix-daemon process is not proxying another");
+ };
+
if (stdio) {
if (auto store = openUncachedStore().dynamic_pointer_cast<RemoteStore>()) {
+ ensureNoTrustedFlag();
auto conn = store->openConnectionWrapper();
int from = conn->from.fd;
int to = conn->to.fd;
@@ -302,16 +308,20 @@ static void runDaemon(bool stdio)
/* Auth hook is empty because in this mode we blindly trust the
standard streams. Limiting access to those is explicitly
not `nix-daemon`'s responsibility. */
- processConnection(openUncachedStore(), from, to, Trusted, NotRecursive, [&](Store & _){});
+ auto isTrusted = isTrustedOpt.value_or(Trusted);
+ processConnection(openUncachedStore(), from, to, isTrusted, NotRecursive, [&](Store & _){});
}
- } else
+ } else {
+ ensureNoTrustedFlag();
daemonLoop();
+ }
}
static int main_nix_daemon(int argc, char * * argv)
{
{
auto stdio = false;
+ std::optional<TrustedFlag> isTrustedOpt;
parseCmdLine(argc, argv, [&](Strings::iterator & arg, const Strings::iterator & end) {
if (*arg == "--daemon")
@@ -322,11 +332,17 @@ static int main_nix_daemon(int argc, char * * argv)
printVersion("nix-daemon");
else if (*arg == "--stdio")
stdio = true;
- else return false;
+ else if (*arg == "--trust") {
+ settings.requireExperimentalFeature(Xp::NixTesting);
+ isTrustedOpt = Trusted;
+ } else if (*arg == "--no-trust") {
+ settings.requireExperimentalFeature(Xp::NixTesting);
+ isTrustedOpt = NotTrusted;
+ } else return false;
return true;
});
- runDaemon(stdio);
+ runDaemon(stdio, isTrustedOpt);
return 0;
}
diff --git a/tests/build-remote-trustless-should-fail-0.sh b/tests/build-remote-trustless-should-fail-0.sh
new file mode 100644
index 000000000..b72c134e7
--- /dev/null
+++ b/tests/build-remote-trustless-should-fail-0.sh
@@ -0,0 +1,11 @@
+source common.sh
+
+# We act as if remote trusts us, but it doesn't. This fails since we are
+# building input-addressed derivations with `buildDerivation`, which
+# depends on trust.
+file=build-hook.nix
+prog=$(readlink -e ./nix-daemon-untrusting.sh)
+proto=ssh-ng
+trusting=true
+
+! source build-remote-trustless.sh
diff --git a/tests/build-remote-trustless-should-pass-0.sh b/tests/build-remote-trustless-should-pass-0.sh
new file mode 100644
index 000000000..82c3f4520
--- /dev/null
+++ b/tests/build-remote-trustless-should-pass-0.sh
@@ -0,0 +1,9 @@
+source common.sh
+
+# Remote trusts us but we pretend it doesn't.
+file=build-hook.nix
+prog=nix-store
+proto=ssh
+trusting=false
+
+source build-remote-trustless.sh
diff --git a/tests/build-remote-trustless-should-pass-1.sh b/tests/build-remote-trustless-should-pass-1.sh
new file mode 100644
index 000000000..22c304bc2
--- /dev/null
+++ b/tests/build-remote-trustless-should-pass-1.sh
@@ -0,0 +1,9 @@
+source common.sh
+
+# Remote trusts us but we pretend it doesn't.
+file=build-hook.nix
+prog=nix-daemon
+proto=ssh-ng
+trusting=false
+
+source build-remote-trustless.sh
diff --git a/tests/build-remote-trustless-should-pass-2.sh b/tests/build-remote-trustless-should-pass-2.sh
new file mode 100644
index 000000000..941ddca05
--- /dev/null
+++ b/tests/build-remote-trustless-should-pass-2.sh
@@ -0,0 +1,9 @@
+source common.sh
+
+# Remote doesn't trust us nor do we think it does
+file=build-hook.nix
+prog=$(readlink -e ./nix-daemon-untrusting.sh)
+proto=ssh-ng
+trusting=false
+
+source build-remote-trustless.sh
diff --git a/tests/build-remote-trustless-should-pass-3.sh b/tests/build-remote-trustless-should-pass-3.sh
new file mode 100644
index 000000000..0c4e191fc
--- /dev/null
+++ b/tests/build-remote-trustless-should-pass-3.sh
@@ -0,0 +1,10 @@
+source common.sh
+
+# We act as if remote trusts us, but it doesn't. This is fine because we
+# are only building (fixed) CA derivations.
+file=build-hook-ca-fixed.nix
+prog=$(readlink -e ./nix-daemon-untrusting.sh)
+proto=ssh-ng
+trusting=true
+
+source build-remote-trustless.sh
diff --git a/tests/build-remote-trustless.sh b/tests/build-remote-trustless.sh
new file mode 100644
index 000000000..8260c4dcd
--- /dev/null
+++ b/tests/build-remote-trustless.sh
@@ -0,0 +1,16 @@
+if ! canUseSandbox; then exit; fi
+if ! [[ $busybox =~ busybox ]]; then exit; fi
+
+unset NIX_STORE_DIR
+unset NIX_STATE_DIR
+
+# Note: ssh{-ng}://localhost bypasses ssh. See tests/build-remote.sh for
+# more details.
+nix build -L -v -f $file -o $TEST_ROOT/result --max-jobs 0 \
+ --arg busybox $busybox \
+ --store $TEST_ROOT/local \
+ --builders "$proto://localhost?remote-program=$prog&trusting=$trusting&remote-store=$TEST_ROOT/remote%3Fsystem-features=foo%20bar%20baz - - 1 1 foo,bar,baz"
+
+outPath=$(readlink -f $TEST_ROOT/result)
+
+grep 'FOO BAR BAZ' $TEST_ROOT/${subDir}/local${outPath}
diff --git a/tests/init.sh b/tests/init.sh
index 3c6d5917d..909b50f63 100644
--- a/tests/init.sh
+++ b/tests/init.sh
@@ -17,7 +17,7 @@ cat > "$NIX_CONF_DIR"/nix.conf <<EOF
build-users-group =
keep-derivations = false
sandbox = false
-experimental-features = nix-command flakes
+experimental-features = nix-command flakes nix-testing
gc-reserved-space = 0
substituters =
flake-registry = $TEST_ROOT/registry.json
diff --git a/tests/local.mk b/tests/local.mk
index 53a9179a3..7074579c9 100644
--- a/tests/local.mk
+++ b/tests/local.mk
@@ -50,6 +50,11 @@ nix_tests = \
check-reqs.sh \
build-remote-content-addressed-fixed.sh \
build-remote-content-addressed-floating.sh \
+ build-remote-trustless-should-pass-0.sh \
+ build-remote-trustless-should-pass-1.sh \
+ build-remote-trustless-should-pass-2.sh \
+ build-remote-trustless-should-pass-3.sh \
+ build-remote-trustless-should-fail-0.sh \
nar-access.sh \
pure-eval.sh \
ca/post-hook.sh \
diff --git a/tests/nix-daemon-untrusting.sh b/tests/nix-daemon-untrusting.sh
new file mode 100755
index 000000000..a3a420147
--- /dev/null
+++ b/tests/nix-daemon-untrusting.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+exec nix-daemon --no-trust "$@"