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/libutil/experimental-features.cc1
-rw-r--r--src/libutil/experimental-features.hh6
-rw-r--r--src/nix/daemon.cc26
-rw-r--r--tests/build-remote-trustless-after.sh2
-rw-r--r--tests/build-remote-trustless-should-fail-0.sh25
-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.sh14
-rwxr-xr-xtests/init.sh2
-rw-r--r--tests/local.mk5
-rwxr-xr-xtests/nix-daemon-untrusting.sh3
15 files changed, 146 insertions, 20 deletions
diff --git a/src/build-remote/build-remote.cc b/src/build-remote/build-remote.cc
index cfc4baaca..3d4dbc3d6 100644
--- a/src/build-remote/build-remote.cc
+++ b/src/build-remote/build-remote.cc
@@ -258,6 +258,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);
@@ -286,23 +288,34 @@ connected:
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 we don't know whether we are trusted (e.g. `ssh://`
+ // stores), we assume we are. This is neccessary for backwards
+ // compat.
+ if (std::optional trust = sshStore->isTrustedClient(); (!trust || *trust) || drv.type().isCA()) {
+ // 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);
+ sshStore->buildPaths({ DerivedPath::Built { *drvPath, OutputsSpec::All {} } });
+ }
- 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 (experimentalFeatureSettings.isEnabled(Xp::CaDerivations) && !drv.type().hasKnownOutputPaths()) {
@@ -311,6 +324,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);
diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc
index 63898f8dc..366a3130a 100644
--- a/src/libstore/daemon.cc
+++ b/src/libstore/daemon.cc
@@ -1064,6 +1064,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/libutil/experimental-features.cc b/src/libutil/experimental-features.cc
index 58d762ebb..32aa66db1 100644
--- a/src/libutil/experimental-features.cc
+++ b/src/libutil/experimental-features.cc
@@ -17,6 +17,7 @@ std::map<ExperimentalFeature, std::string> stringifiedXpFeatures = {
{ Xp::AutoAllocateUids, "auto-allocate-uids" },
{ Xp::Cgroups, "cgroups" },
{ Xp::DiscardReferences, "discard-references" },
+ { 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 5948ad7ad..6a3c929df 100644
--- a/src/libutil/experimental-features.hh
+++ b/src/libutil/experimental-features.hh
@@ -27,6 +27,12 @@ enum struct ExperimentalFeature
AutoAllocateUids,
Cgroups,
DiscardReferences,
+
+ /**
+ * 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 7e4a7ba86..6e0a6d7f6 100644
--- a/src/nix/daemon.cc
+++ b/src/nix/daemon.cc
@@ -294,10 +294,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;
@@ -331,16 +337,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);
+ auto isTrusted = isTrustedOpt.value_or(Trusted);
+ processConnection(openUncachedStore(), from, to, isTrusted, NotRecursive);
}
- } 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")
@@ -351,11 +361,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") {
+ experimentalFeatureSettings.require(Xp::NixTesting);
+ isTrustedOpt = Trusted;
+ } else if (*arg == "--no-trust") {
+ experimentalFeatureSettings.require(Xp::NixTesting);
+ isTrustedOpt = NotTrusted;
+ } else return false;
return true;
});
- runDaemon(stdio);
+ runDaemon(stdio, isTrustedOpt);
return 0;
}
diff --git a/tests/build-remote-trustless-after.sh b/tests/build-remote-trustless-after.sh
new file mode 100644
index 000000000..19f59e6ae
--- /dev/null
+++ b/tests/build-remote-trustless-after.sh
@@ -0,0 +1,2 @@
+outPath=$(readlink -f $TEST_ROOT/result)
+grep 'FOO BAR BAZ' ${remoteDir}/${outPath}
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..b5cedb544
--- /dev/null
+++ b/tests/build-remote-trustless-should-fail-0.sh
@@ -0,0 +1,25 @@
+source common.sh
+
+[[ $busybox =~ busybox ]] || skipTest "no busybox"
+
+unset NIX_STORE_DIR
+unset NIX_STATE_DIR
+
+# We first build a dependency of the derivation we eventually want to
+# build.
+nix-build build-hook.nix -A passthru.input2 \
+ -o "$TEST_ROOT/input2" \
+ --arg busybox "$busybox" \
+ --store "$TEST_ROOT/local" \
+ --option system-features bar
+
+# Now when we go to build that downstream derivation, Nix will try to
+# copy our already-build `input2` to the remote store. That store object
+# is input-addressed, so this will fail.
+
+file=build-hook.nix
+prog=$(readlink -e ./nix-daemon-untrusting.sh)
+proto=ssh-ng
+
+expectStderr 1 source build-remote-trustless.sh \
+ | grepQuiet "cannot add path '[^ ]*' because it lacks a signature by a trusted key"
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..2a7ebd8c6
--- /dev/null
+++ b/tests/build-remote-trustless-should-pass-0.sh
@@ -0,0 +1,9 @@
+source common.sh
+
+# Remote trusts us
+file=build-hook.nix
+prog=nix-store
+proto=ssh
+
+source build-remote-trustless.sh
+source build-remote-trustless-after.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..516bdf092
--- /dev/null
+++ b/tests/build-remote-trustless-should-pass-1.sh
@@ -0,0 +1,9 @@
+source common.sh
+
+# Remote trusts us
+file=build-hook.nix
+prog=nix-daemon
+proto=ssh-ng
+
+source build-remote-trustless.sh
+source build-remote-trustless-after.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..6383f5489
--- /dev/null
+++ b/tests/build-remote-trustless-should-pass-2.sh
@@ -0,0 +1,9 @@
+source common.sh
+
+# Remote doesn't trust us
+file=build-hook.nix
+prog=$(readlink -e ./nix-daemon-untrusting.sh)
+proto=ssh-ng
+
+source build-remote-trustless.sh
+source build-remote-trustless-after.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..c3ec359fb
--- /dev/null
+++ b/tests/build-remote-trustless-should-pass-3.sh
@@ -0,0 +1,10 @@
+source common.sh
+
+# Remote doesn't trusts us, but 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
+
+source build-remote-trustless.sh
+source build-remote-trustless-after.sh
diff --git a/tests/build-remote-trustless.sh b/tests/build-remote-trustless.sh
new file mode 100644
index 000000000..9df44e0c5
--- /dev/null
+++ b/tests/build-remote-trustless.sh
@@ -0,0 +1,14 @@
+requireSandboxSupport
+[[ $busybox =~ busybox ]] || skipTest "no busybox"
+
+unset NIX_STORE_DIR
+unset NIX_STATE_DIR
+
+remoteDir=$TEST_ROOT/remote
+
+# Note: ssh{-ng}://localhost bypasses ssh. See tests/build-remote.sh for
+# more details.
+nix-build $file -o $TEST_ROOT/result --max-jobs 0 \
+ --arg busybox $busybox \
+ --store $TEST_ROOT/local \
+ --builders "$proto://localhost?remote-program=$prog&remote-store=${remoteDir}%3Fsystem-features=foo%20bar%20baz - - 1 1 foo,bar,baz"
diff --git a/tests/init.sh b/tests/init.sh
index c420e8c9f..2c4f4a2f3 100755
--- a/tests/init.sh
+++ b/tests/init.sh
@@ -20,7 +20,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 6cb466e8e..0910fcd91 100644
--- a/tests/local.mk
+++ b/tests/local.mk
@@ -70,6 +70,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 \
eval.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 "$@"