Trustless remote building
Co-authored-by: Matthew Bauer <mjbauer95@gmail.com>
This commit is contained in:
parent
53f92c779a
commit
cbc4344297
|
@ -247,6 +247,9 @@ static int _main(int argc, char * * argv)
|
||||||
connected:
|
connected:
|
||||||
close(5);
|
close(5);
|
||||||
|
|
||||||
|
assert(sshStore);
|
||||||
|
auto sshStore2 = ref<Store>(sshStore);
|
||||||
|
|
||||||
std::cerr << "# accept\n" << storeUri << "\n";
|
std::cerr << "# accept\n" << storeUri << "\n";
|
||||||
|
|
||||||
auto inputs = readStrings<PathSet>(source);
|
auto inputs = readStrings<PathSet>(source);
|
||||||
|
@ -269,18 +272,23 @@ connected:
|
||||||
|
|
||||||
{
|
{
|
||||||
Activity act(*logger, lvlTalkative, actUnknown, fmt("copying dependencies to '%s'", storeUri));
|
Activity act(*logger, lvlTalkative, actUnknown, fmt("copying dependencies to '%s'", storeUri));
|
||||||
copyPaths(store, ref<Store>(sshStore), store->parseStorePathSet(inputs), NoRepair, NoCheckSigs, substitute);
|
copyPaths(store, sshStore2, store->parseStorePathSet(inputs), NoRepair, NoCheckSigs, substitute);
|
||||||
}
|
}
|
||||||
|
|
||||||
uploadLock = -1;
|
uploadLock = -1;
|
||||||
|
|
||||||
auto drv = store->readDerivation(*drvPath);
|
BasicDerivation drv = store->readDerivation(*drvPath);
|
||||||
|
|
||||||
|
if (sshStore2->isTrusting || derivationIsCA(drv.type())) {
|
||||||
drv.inputSrcs = store->parseStorePathSet(inputs);
|
drv.inputSrcs = store->parseStorePathSet(inputs);
|
||||||
|
auto result = sshStore2->buildDerivation(*drvPath, drv);
|
||||||
auto result = sshStore->buildDerivation(*drvPath, drv);
|
|
||||||
|
|
||||||
if (!result.success())
|
if (!result.success())
|
||||||
throw Error("build of '%s' on '%s' failed: %s", store->printStorePath(*drvPath), storeUri, result.errorMsg);
|
throw Error("build of '%s' on '%s' failed: %s", store->printStorePath(*drvPath), storeUri, result.errorMsg);
|
||||||
|
} else {
|
||||||
|
copyPaths(store, sshStore2, {*drvPath}, NoRepair, NoCheckSigs, substitute);
|
||||||
|
sshStore2->buildPaths({{*drvPath}});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
StorePathSet missing;
|
StorePathSet missing;
|
||||||
for (auto & path : outputs)
|
for (auto & path : outputs)
|
||||||
|
@ -290,7 +298,7 @@ connected:
|
||||||
Activity act(*logger, lvlTalkative, actUnknown, fmt("copying outputs from '%s'", storeUri));
|
Activity act(*logger, lvlTalkative, actUnknown, fmt("copying outputs from '%s'", storeUri));
|
||||||
for (auto & i : missing)
|
for (auto & i : missing)
|
||||||
store->locksHeld.insert(store->printStorePath(i)); /* FIXME: ugly */
|
store->locksHeld.insert(store->printStorePath(i)); /* FIXME: ugly */
|
||||||
copyPaths(ref<Store>(sshStore), store, missing, NoRepair, NoCheckSigs, NoSubstitute);
|
copyPaths(sshStore2, store, missing, NoRepair, NoCheckSigs, NoSubstitute);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -914,6 +914,8 @@ void processConnection(
|
||||||
|
|
||||||
opCount++;
|
opCount++;
|
||||||
|
|
||||||
|
debug("performing daemon worker op: %d", op);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
performOp(tunnelLogger, store, trusted, recursive, clientVersion, from, to, op);
|
performOp(tunnelLogger, store, trusted, recursive, clientVersion, from, to, op);
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
|
|
|
@ -834,7 +834,23 @@ std::map<StorePath, StorePath> copyPaths(ref<Store> srcStore, ref<Store> dstStor
|
||||||
MaintainCount<decltype(nrRunning)> mc(nrRunning);
|
MaintainCount<decltype(nrRunning)> mc(nrRunning);
|
||||||
showProgress();
|
showProgress();
|
||||||
try {
|
try {
|
||||||
|
if (dstStore->isTrusting || info->ca) {
|
||||||
copyStorePath(srcStore, dstStore, storePath, repair, checkSigs);
|
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
|
||||||
|
copyStorePath(srcStore, dstStore, drvPath, repair, checkSigs);
|
||||||
|
dstStore->buildPaths({{
|
||||||
|
drvPath,
|
||||||
|
p != outputMap.end() ? StringSet { p->first } : StringSet {},
|
||||||
|
}});
|
||||||
|
} else {
|
||||||
|
dstStore->ensurePath(storePath);
|
||||||
|
}
|
||||||
} catch (Error &e) {
|
} catch (Error &e) {
|
||||||
nrFailed++;
|
nrFailed++;
|
||||||
if (!settings.keepGoing)
|
if (!settings.keepGoing)
|
||||||
|
|
|
@ -158,7 +158,9 @@ public:
|
||||||
|
|
||||||
const Setting<int> pathInfoCacheSize{this, 65536, "path-info-cache-size", "size of the in-memory store path information cache"};
|
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)"};
|
Setting<int> priority{this, 0, "priority", "priority of this substituter (lower value means higher priority)"};
|
||||||
|
|
||||||
|
|
|
@ -268,6 +268,7 @@ static int _main(int argc, char * * argv)
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
auto stdio = false;
|
auto stdio = false;
|
||||||
|
std::optional<TrustedFlag> isTrustedOpt;
|
||||||
|
|
||||||
parseCmdLine(argc, argv, [&](Strings::iterator & arg, const Strings::iterator & end) {
|
parseCmdLine(argc, argv, [&](Strings::iterator & arg, const Strings::iterator & end) {
|
||||||
if (*arg == "--daemon")
|
if (*arg == "--daemon")
|
||||||
|
@ -278,14 +279,26 @@ static int _main(int argc, char * * argv)
|
||||||
printVersion("nix-daemon");
|
printVersion("nix-daemon");
|
||||||
else if (*arg == "--stdio")
|
else if (*arg == "--stdio")
|
||||||
stdio = true;
|
stdio = true;
|
||||||
else return false;
|
else if (*arg == "--trust") {
|
||||||
|
settings.requireExperimentalFeature("nix-testing");
|
||||||
|
isTrustedOpt = Trusted;
|
||||||
|
} else if (*arg == "--no-trust") {
|
||||||
|
settings.requireExperimentalFeature("nix-testing");
|
||||||
|
isTrustedOpt = NotTrusted;
|
||||||
|
} else return false;
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
initPlugins();
|
initPlugins();
|
||||||
|
|
||||||
|
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 (stdio) {
|
||||||
if (getStoreType() == tDaemon) {
|
if (getStoreType() == tDaemon) {
|
||||||
|
ensureNoTrustedFlag();
|
||||||
// Forward on this connection to the real daemon
|
// Forward on this connection to the real daemon
|
||||||
auto socketPath = settings.nixDaemonSocketFile;
|
auto socketPath = settings.nixDaemonSocketFile;
|
||||||
auto s = socket(PF_UNIX, SOCK_STREAM, 0);
|
auto s = socket(PF_UNIX, SOCK_STREAM, 0);
|
||||||
|
@ -335,9 +348,11 @@ static int _main(int argc, char * * argv)
|
||||||
/* Auth hook is empty because in this mode we blindly trust the
|
/* Auth hook is empty because in this mode we blindly trust the
|
||||||
standard streams. Limitting access to thoses is explicitly
|
standard streams. Limitting access to thoses is explicitly
|
||||||
not `nix-daemon`'s responsibility. */
|
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(argv);
|
daemonLoop(argv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
52
tests/build-hook-ca.nix
Normal file
52
tests/build-hook-ca.nix
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
{ busybox }:
|
||||||
|
|
||||||
|
with import ./config.nix;
|
||||||
|
|
||||||
|
let
|
||||||
|
|
||||||
|
mkDerivation = args:
|
||||||
|
derivation ({
|
||||||
|
inherit system;
|
||||||
|
builder = busybox;
|
||||||
|
args = ["sh" "-e" args.builder or (builtins.toFile "builder-${args.name}.sh" "if [ -e .attrs.sh ]; then source .attrs.sh; fi; eval \"$buildCommand\"")];
|
||||||
|
outputHashMode = "recursive";
|
||||||
|
outputHashAlgo = "sha256";
|
||||||
|
} // removeAttrs args ["builder" "meta"])
|
||||||
|
// { meta = args.meta or {}; };
|
||||||
|
|
||||||
|
input1 = mkDerivation {
|
||||||
|
shell = busybox;
|
||||||
|
name = "build-remote-input-1";
|
||||||
|
buildCommand = "echo FOO > $out";
|
||||||
|
outputHash = "sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc=";
|
||||||
|
};
|
||||||
|
|
||||||
|
input2 = mkDerivation {
|
||||||
|
shell = busybox;
|
||||||
|
name = "build-remote-input-2";
|
||||||
|
buildCommand = "echo BAR > $out";
|
||||||
|
outputHash = "sha256-XArauVH91AVwP9hBBQNlkX9ccuPpSYx9o0zeIHb6e+Q=";
|
||||||
|
};
|
||||||
|
|
||||||
|
input3 = mkDerivation {
|
||||||
|
shell = busybox;
|
||||||
|
name = "build-remote-input-3";
|
||||||
|
buildCommand = ''
|
||||||
|
read x < ${input2}
|
||||||
|
echo $x BAZ > $out
|
||||||
|
'';
|
||||||
|
outputHash = "sha256-daKAcPp/+BYMQsVi/YYMlCKoNAxCNDsaivwSHgQqD2s=";
|
||||||
|
};
|
||||||
|
|
||||||
|
in
|
||||||
|
|
||||||
|
mkDerivation {
|
||||||
|
shell = busybox;
|
||||||
|
name = "build-remote";
|
||||||
|
buildCommand = ''
|
||||||
|
read x < ${input1}
|
||||||
|
read y < ${input3}
|
||||||
|
echo "$x $y" > $out
|
||||||
|
'';
|
||||||
|
outputHash = "sha256-5SxbkUw6xe2l9TE1uwCvTtTDysD1vhRor38OtDF0LqQ=";
|
||||||
|
}
|
11
tests/build-remote-trustless-should-fail-0.sh
Normal file
11
tests/build-remote-trustless-should-fail-0.sh
Normal file
|
@ -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
|
9
tests/build-remote-trustless-should-pass-0.sh
Normal file
9
tests/build-remote-trustless-should-pass-0.sh
Normal file
|
@ -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
|
9
tests/build-remote-trustless-should-pass-1.sh
Normal file
9
tests/build-remote-trustless-should-pass-1.sh
Normal file
|
@ -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
|
9
tests/build-remote-trustless-should-pass-2.sh
Normal file
9
tests/build-remote-trustless-should-pass-2.sh
Normal file
|
@ -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
|
10
tests/build-remote-trustless-should-pass-3.sh
Normal file
10
tests/build-remote-trustless-should-pass-3.sh
Normal file
|
@ -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.nix
|
||||||
|
prog=$(readlink -e ./nix-daemon-untrusting.sh)
|
||||||
|
proto=ssh-ng
|
||||||
|
trusting=true
|
||||||
|
|
||||||
|
source build-remote-trustless.sh
|
18
tests/build-remote-trustless.sh
Normal file
18
tests/build-remote-trustless.sh
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
if ! canUseSandbox; then exit; fi
|
||||||
|
if ! [[ $busybox =~ busybox ]]; then exit; fi
|
||||||
|
|
||||||
|
unset NIX_STORE_DIR
|
||||||
|
unset NIX_STATE_DIR
|
||||||
|
|
||||||
|
# Note: ssh://localhost bypasses ssh, directly invoking nix-store as a
|
||||||
|
# child process. This allows us to test LegacySSHStore::buildDerivation().
|
||||||
|
# ssh-ng://... likewise allows us to test RemoteStore::buildDerivation().
|
||||||
|
|
||||||
|
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}
|
|
@ -17,7 +17,7 @@ cat > "$NIX_CONF_DIR"/nix.conf <<EOF
|
||||||
build-users-group =
|
build-users-group =
|
||||||
keep-derivations = false
|
keep-derivations = false
|
||||||
sandbox = false
|
sandbox = false
|
||||||
experimental-features = nix-command flakes
|
experimental-features = nix-command flakes nix-testing
|
||||||
gc-reserved-space = 0
|
gc-reserved-space = 0
|
||||||
flake-registry = $TEST_ROOT/registry.json
|
flake-registry = $TEST_ROOT/registry.json
|
||||||
include nix.conf.extra
|
include nix.conf.extra
|
||||||
|
|
|
@ -15,6 +15,10 @@ nix_tests = \
|
||||||
linux-sandbox.sh \
|
linux-sandbox.sh \
|
||||||
build-dry.sh \
|
build-dry.sh \
|
||||||
build-remote.sh \
|
build-remote.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 \
|
nar-access.sh \
|
||||||
structured-attrs.sh \
|
structured-attrs.sh \
|
||||||
fetchGit.sh \
|
fetchGit.sh \
|
||||||
|
@ -34,6 +38,7 @@ nix_tests = \
|
||||||
recursive.sh \
|
recursive.sh \
|
||||||
flakes.sh
|
flakes.sh
|
||||||
# parallel.sh
|
# parallel.sh
|
||||||
|
# build-remote-trustless-should-pass-0.sh # problem with legacy ssh-store only
|
||||||
|
|
||||||
install-tests += $(foreach x, $(nix_tests), tests/$(x))
|
install-tests += $(foreach x, $(nix_tests), tests/$(x))
|
||||||
|
|
||||||
|
|
3
tests/nix-daemon-untrusting.sh
Executable file
3
tests/nix-daemon-untrusting.sh
Executable file
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
exec nix-daemon --no-trust "$@"
|
Loading…
Reference in a new issue