2018-09-24 14:53:44 +03:00
|
|
|
|
#include "daemon.hh"
|
2024-04-04 19:25:01 +03:00
|
|
|
|
#include "signals.hh"
|
2018-09-24 14:53:44 +03:00
|
|
|
|
#include "worker-protocol.hh"
|
2022-03-08 23:53:26 +02:00
|
|
|
|
#include "worker-protocol-impl.hh"
|
2022-03-01 21:43:07 +02:00
|
|
|
|
#include "build-result.hh"
|
2018-09-24 14:53:44 +03:00
|
|
|
|
#include "store-api.hh"
|
2022-03-09 17:27:48 +02:00
|
|
|
|
#include "store-cast.hh"
|
2022-03-01 20:31:36 +02:00
|
|
|
|
#include "gc-store.hh"
|
2022-03-08 20:20:39 +02:00
|
|
|
|
#include "log-store.hh"
|
2023-03-23 16:06:45 +02:00
|
|
|
|
#include "indirect-root-store.hh"
|
2021-03-02 05:50:41 +02:00
|
|
|
|
#include "path-with-outputs.hh"
|
2018-09-24 14:53:44 +03:00
|
|
|
|
#include "finally.hh"
|
|
|
|
|
#include "archive.hh"
|
|
|
|
|
#include "derivations.hh"
|
|
|
|
|
#include "args.hh"
|
2023-09-04 16:51:23 +03:00
|
|
|
|
#include "git.hh"
|
2018-09-24 14:53:44 +03:00
|
|
|
|
|
2023-09-03 00:35:16 +03:00
|
|
|
|
#ifndef _WIN32 // TODO need graceful async exit support on Windows?
|
|
|
|
|
# include "monitor-fd.hh"
|
|
|
|
|
#endif
|
|
|
|
|
|
2018-09-24 14:53:44 +03:00
|
|
|
|
namespace nix::daemon {
|
|
|
|
|
|
|
|
|
|
Sink & operator << (Sink & sink, const Logger::Fields & fields)
|
|
|
|
|
{
|
|
|
|
|
sink << fields.size();
|
|
|
|
|
for (auto & f : fields) {
|
|
|
|
|
sink << f.type;
|
|
|
|
|
if (f.type == Logger::Field::tInt)
|
|
|
|
|
sink << f.i;
|
|
|
|
|
else if (f.type == Logger::Field::tString)
|
|
|
|
|
sink << f.s;
|
|
|
|
|
else abort();
|
|
|
|
|
}
|
|
|
|
|
return sink;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Logger that forwards log messages to the client, *if* we're in a
|
|
|
|
|
state where the protocol allows it (i.e., when canSendStderr is
|
|
|
|
|
true). */
|
|
|
|
|
struct TunnelLogger : public Logger
|
|
|
|
|
{
|
|
|
|
|
FdSink & to;
|
|
|
|
|
|
|
|
|
|
struct State
|
|
|
|
|
{
|
|
|
|
|
bool canSendStderr = false;
|
|
|
|
|
std::vector<std::string> pendingMsgs;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Sync<State> state_;
|
|
|
|
|
|
2023-10-17 18:15:36 +03:00
|
|
|
|
WorkerProto::Version clientVersion;
|
2018-09-24 14:53:44 +03:00
|
|
|
|
|
2023-10-17 18:15:36 +03:00
|
|
|
|
TunnelLogger(FdSink & to, WorkerProto::Version clientVersion)
|
2018-09-24 14:53:44 +03:00
|
|
|
|
: to(to), clientVersion(clientVersion) { }
|
|
|
|
|
|
|
|
|
|
void enqueueMsg(const std::string & s)
|
|
|
|
|
{
|
|
|
|
|
auto state(state_.lock());
|
|
|
|
|
|
|
|
|
|
if (state->canSendStderr) {
|
|
|
|
|
assert(state->pendingMsgs.empty());
|
|
|
|
|
try {
|
|
|
|
|
to(s);
|
|
|
|
|
to.flush();
|
|
|
|
|
} catch (...) {
|
|
|
|
|
/* Write failed; that means that the other side is
|
|
|
|
|
gone. */
|
|
|
|
|
state->canSendStderr = false;
|
|
|
|
|
throw;
|
|
|
|
|
}
|
|
|
|
|
} else
|
|
|
|
|
state->pendingMsgs.push_back(s);
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-02 16:44:19 +02:00
|
|
|
|
void log(Verbosity lvl, std::string_view s) override
|
2018-09-24 14:53:44 +03:00
|
|
|
|
{
|
|
|
|
|
if (lvl > verbosity) return;
|
|
|
|
|
|
|
|
|
|
StringSink buf;
|
2023-03-02 16:44:19 +02:00
|
|
|
|
buf << STDERR_NEXT << (s + "\n");
|
2022-01-17 23:20:05 +02:00
|
|
|
|
enqueueMsg(buf.s);
|
2018-09-24 14:53:44 +03:00
|
|
|
|
}
|
|
|
|
|
|
2020-04-18 00:07:44 +03:00
|
|
|
|
void logEI(const ErrorInfo & ei) override
|
|
|
|
|
{
|
2020-04-20 02:16:51 +03:00
|
|
|
|
if (ei.level > verbosity) return;
|
|
|
|
|
|
2020-06-15 15:12:39 +03:00
|
|
|
|
std::stringstream oss;
|
2020-06-29 19:20:51 +03:00
|
|
|
|
showErrorInfo(oss, ei, false);
|
2020-04-20 02:16:51 +03:00
|
|
|
|
|
|
|
|
|
StringSink buf;
|
2020-07-08 16:53:14 +03:00
|
|
|
|
buf << STDERR_NEXT << oss.str();
|
2022-01-17 23:20:05 +02:00
|
|
|
|
enqueueMsg(buf.s);
|
2020-04-18 00:07:44 +03:00
|
|
|
|
}
|
|
|
|
|
|
2018-09-24 14:53:44 +03:00
|
|
|
|
/* startWork() means that we're starting an operation for which we
|
2020-07-29 01:48:39 +03:00
|
|
|
|
want to send out stderr to the client. */
|
2018-09-24 14:53:44 +03:00
|
|
|
|
void startWork()
|
|
|
|
|
{
|
|
|
|
|
auto state(state_.lock());
|
|
|
|
|
state->canSendStderr = true;
|
|
|
|
|
|
|
|
|
|
for (auto & msg : state->pendingMsgs)
|
|
|
|
|
to(msg);
|
|
|
|
|
|
|
|
|
|
state->pendingMsgs.clear();
|
|
|
|
|
|
|
|
|
|
to.flush();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* stopWork() means that we're done; stop sending stderr to the
|
|
|
|
|
client. */
|
2020-10-07 18:13:54 +03:00
|
|
|
|
void stopWork(const Error * ex = nullptr)
|
2018-09-24 14:53:44 +03:00
|
|
|
|
{
|
|
|
|
|
auto state(state_.lock());
|
|
|
|
|
|
|
|
|
|
state->canSendStderr = false;
|
|
|
|
|
|
2020-10-07 18:13:54 +03:00
|
|
|
|
if (!ex)
|
2018-09-24 14:53:44 +03:00
|
|
|
|
to << STDERR_LAST;
|
|
|
|
|
else {
|
2020-10-07 18:13:54 +03:00
|
|
|
|
if (GET_PROTOCOL_MINOR(clientVersion) >= 26) {
|
|
|
|
|
to << STDERR_ERROR << *ex;
|
|
|
|
|
} else {
|
libexpr: Support structured error classes
While preparing PRs like #9753, I've had to change error messages in
dozens of code paths. It would be nice if instead of
EvalError("expected 'boolean' but found '%1%'", showType(v))
we could write
TypeError(v, "boolean")
or similar. Then, changing the error message could be a mechanical
refactor with the compiler pointing out places the constructor needs to
be changed, rather than the error-prone process of grepping through the
codebase. Structured errors would also help prevent the "same" error
from having multiple slightly different messages, and could be a first
step towards error codes / an error index.
This PR reworks the exception infrastructure in `libexpr` to
support exception types with different constructor signatures than
`BaseError`. Actually refactoring the exceptions to use structured data
will come in a future PR (this one is big enough already, as it has to
touch every exception in `libexpr`).
The core design is in `eval-error.hh`. Generally, errors like this:
state.error("'%s' is not a string", getAttrPathStr())
.debugThrow<TypeError>()
are transformed like this:
state.error<TypeError>("'%s' is not a string", getAttrPathStr())
.debugThrow()
The type annotation has moved from `ErrorBuilder::debugThrow` to
`EvalState::error`.
2024-01-23 03:08:29 +02:00
|
|
|
|
to << STDERR_ERROR << ex->what() << ex->info().status;
|
2020-10-07 18:13:54 +03:00
|
|
|
|
}
|
2018-09-24 14:53:44 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void startActivity(ActivityId act, Verbosity lvl, ActivityType type,
|
|
|
|
|
const std::string & s, const Fields & fields, ActivityId parent) override
|
|
|
|
|
{
|
|
|
|
|
if (GET_PROTOCOL_MINOR(clientVersion) < 20) {
|
|
|
|
|
if (!s.empty())
|
|
|
|
|
log(lvl, s + "...");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
StringSink buf;
|
|
|
|
|
buf << STDERR_START_ACTIVITY << act << lvl << type << s << fields << parent;
|
2022-01-17 23:20:05 +02:00
|
|
|
|
enqueueMsg(buf.s);
|
2018-09-24 14:53:44 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void stopActivity(ActivityId act) override
|
|
|
|
|
{
|
|
|
|
|
if (GET_PROTOCOL_MINOR(clientVersion) < 20) return;
|
|
|
|
|
StringSink buf;
|
|
|
|
|
buf << STDERR_STOP_ACTIVITY << act;
|
2022-01-17 23:20:05 +02:00
|
|
|
|
enqueueMsg(buf.s);
|
2018-09-24 14:53:44 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void result(ActivityId act, ResultType type, const Fields & fields) override
|
|
|
|
|
{
|
|
|
|
|
if (GET_PROTOCOL_MINOR(clientVersion) < 20) return;
|
|
|
|
|
StringSink buf;
|
|
|
|
|
buf << STDERR_RESULT << act << type << fields;
|
2022-01-17 23:20:05 +02:00
|
|
|
|
enqueueMsg(buf.s);
|
2018-09-24 14:53:44 +03:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct TunnelSink : Sink
|
|
|
|
|
{
|
|
|
|
|
Sink & to;
|
|
|
|
|
TunnelSink(Sink & to) : to(to) { }
|
2020-12-02 15:00:43 +02:00
|
|
|
|
void operator () (std::string_view data)
|
2018-09-24 14:53:44 +03:00
|
|
|
|
{
|
|
|
|
|
to << STDERR_WRITE;
|
2020-12-02 15:00:43 +02:00
|
|
|
|
writeString(data, to);
|
2018-09-24 14:53:44 +03:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct TunnelSource : BufferedSource
|
|
|
|
|
{
|
|
|
|
|
Source & from;
|
|
|
|
|
BufferedSink & to;
|
|
|
|
|
TunnelSource(Source & from, BufferedSink & to) : from(from), to(to) { }
|
2020-12-02 15:10:56 +02:00
|
|
|
|
size_t readUnbuffered(char * data, size_t len) override
|
2018-09-24 14:53:44 +03:00
|
|
|
|
{
|
|
|
|
|
to << STDERR_READ << len;
|
|
|
|
|
to.flush();
|
|
|
|
|
size_t n = readString(data, len, from);
|
|
|
|
|
if (n == 0) throw EndOfFile("unexpected end-of-file");
|
|
|
|
|
return n;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
Recursive Nix support
This allows Nix builders to call Nix to build derivations, with some
limitations.
Example:
let nixpkgs = fetchTarball channel:nixos-18.03; in
with import <nixpkgs> {};
runCommand "foo"
{
buildInputs = [ nix jq ];
NIX_PATH = "nixpkgs=${nixpkgs}";
}
''
hello=$(nix-build -E '(import <nixpkgs> {}).hello.overrideDerivation (args: { name = "hello-3.5"; })')
$hello/bin/hello
mkdir -p $out/bin
ln -s $hello/bin/hello $out/bin/hello
nix path-info -r --json $hello | jq .
''
This derivation makes a recursive Nix call to build GNU Hello and
symlinks it from its $out, i.e.
# ll ./result/bin/
lrwxrwxrwx 1 root root 63 Jan 1 1970 hello -> /nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5/bin/hello
# nix-store -qR ./result
/nix/store/hwwqshlmazzjzj7yhrkyjydxamvvkfd3-glibc-2.26-131
/nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5
/nix/store/sgmvvyw8vhfqdqb619bxkcpfn9lvd8ss-foo
This is implemented as follows:
* Before running the outer builder, Nix creates a Unix domain socket
'.nix-socket' in the builder's temporary directory and sets
$NIX_REMOTE to point to it. It starts a thread to process
connections to this socket. (Thus you don't need to have nix-daemon
running.)
* The daemon thread uses a wrapper store (RestrictedStore) to keep
track of paths added through recursive Nix calls, to implement some
restrictions (see below), and to do some censorship (e.g. for
purity, queryPathInfo() won't return impure information such as
signatures and timestamps).
* After the build finishes, the output paths are scanned for
references to the paths added through recursive Nix calls (in
addition to the inputs closure). Thus, in the example above, $out
has a reference to $hello.
The main restriction on recursive Nix calls is that they cannot do
arbitrary substitutions. For example, doing
nix-store -r /nix/store/kmwd1hq55akdb9sc7l3finr175dajlby-hello-2.10
is forbidden unless /nix/store/kmwd... is in the inputs closure or
previously built by a recursive Nix call. This is to prevent
irreproducible derivations that have hidden dependencies on
substituters or the current store contents. Building a derivation is
fine, however, and Nix will use substitutes if available. In other
words, the builder has to present proof that it knows how to build a
desired store path from scratch by constructing a derivation graph for
that path.
Probably we should also disallow instantiating/building fixed-output
derivations (specifically, those that access the network, but
currently we have no way to mark fixed-output derivations that don't
access the network). Otherwise sandboxed derivations can bypass
sandbox restrictions and access the network.
When sandboxing is enabled, we make paths appear in the sandbox of the
builder by entering the mount namespace of the builder and
bind-mounting each path. This is tricky because we do a pivot_root()
in the builder to change the root directory of its mount namespace,
and thus the host /nix/store is not visible in the mount namespace of
the builder. To get around this, just before doing pivot_root(), we
branch a second mount namespace that shares its /nix/store mountpoint
with the parent.
Recursive Nix currently doesn't work on macOS in sandboxed mode
(because we can't change the sandbox policy of a running build) and in
non-root mode (because setns() barfs).
2018-10-02 17:01:26 +03:00
|
|
|
|
struct ClientSettings
|
|
|
|
|
{
|
|
|
|
|
bool keepFailed;
|
|
|
|
|
bool keepGoing;
|
|
|
|
|
bool tryFallback;
|
|
|
|
|
Verbosity verbosity;
|
|
|
|
|
unsigned int maxBuildJobs;
|
|
|
|
|
time_t maxSilentTime;
|
|
|
|
|
bool verboseBuild;
|
|
|
|
|
unsigned int buildCores;
|
|
|
|
|
bool useSubstitutes;
|
|
|
|
|
StringMap overrides;
|
|
|
|
|
|
|
|
|
|
void apply(TrustedFlag trusted)
|
|
|
|
|
{
|
|
|
|
|
settings.keepFailed = keepFailed;
|
|
|
|
|
settings.keepGoing = keepGoing;
|
|
|
|
|
settings.tryFallback = tryFallback;
|
|
|
|
|
nix::verbosity = verbosity;
|
|
|
|
|
settings.maxBuildJobs.assign(maxBuildJobs);
|
|
|
|
|
settings.maxSilentTime = maxSilentTime;
|
|
|
|
|
settings.verboseBuild = verboseBuild;
|
|
|
|
|
settings.buildCores = buildCores;
|
|
|
|
|
settings.useSubstitutes = useSubstitutes;
|
|
|
|
|
|
|
|
|
|
for (auto & i : overrides) {
|
|
|
|
|
auto & name(i.first);
|
|
|
|
|
auto & value(i.second);
|
|
|
|
|
|
|
|
|
|
auto setSubstituters = [&](Setting<Strings> & res) {
|
|
|
|
|
if (name != res.name && res.aliases.count(name) == 0)
|
|
|
|
|
return false;
|
|
|
|
|
StringSet trusted = settings.trustedSubstituters;
|
|
|
|
|
for (auto & s : settings.substituters.get())
|
|
|
|
|
trusted.insert(s);
|
|
|
|
|
Strings subs;
|
|
|
|
|
auto ss = tokenizeString<Strings>(value);
|
|
|
|
|
for (auto & s : ss)
|
|
|
|
|
if (trusted.count(s))
|
|
|
|
|
subs.push_back(s);
|
2020-12-05 06:17:19 +02:00
|
|
|
|
else if (!hasSuffix(s, "/") && trusted.count(s + "/"))
|
|
|
|
|
subs.push_back(s + "/");
|
Recursive Nix support
This allows Nix builders to call Nix to build derivations, with some
limitations.
Example:
let nixpkgs = fetchTarball channel:nixos-18.03; in
with import <nixpkgs> {};
runCommand "foo"
{
buildInputs = [ nix jq ];
NIX_PATH = "nixpkgs=${nixpkgs}";
}
''
hello=$(nix-build -E '(import <nixpkgs> {}).hello.overrideDerivation (args: { name = "hello-3.5"; })')
$hello/bin/hello
mkdir -p $out/bin
ln -s $hello/bin/hello $out/bin/hello
nix path-info -r --json $hello | jq .
''
This derivation makes a recursive Nix call to build GNU Hello and
symlinks it from its $out, i.e.
# ll ./result/bin/
lrwxrwxrwx 1 root root 63 Jan 1 1970 hello -> /nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5/bin/hello
# nix-store -qR ./result
/nix/store/hwwqshlmazzjzj7yhrkyjydxamvvkfd3-glibc-2.26-131
/nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5
/nix/store/sgmvvyw8vhfqdqb619bxkcpfn9lvd8ss-foo
This is implemented as follows:
* Before running the outer builder, Nix creates a Unix domain socket
'.nix-socket' in the builder's temporary directory and sets
$NIX_REMOTE to point to it. It starts a thread to process
connections to this socket. (Thus you don't need to have nix-daemon
running.)
* The daemon thread uses a wrapper store (RestrictedStore) to keep
track of paths added through recursive Nix calls, to implement some
restrictions (see below), and to do some censorship (e.g. for
purity, queryPathInfo() won't return impure information such as
signatures and timestamps).
* After the build finishes, the output paths are scanned for
references to the paths added through recursive Nix calls (in
addition to the inputs closure). Thus, in the example above, $out
has a reference to $hello.
The main restriction on recursive Nix calls is that they cannot do
arbitrary substitutions. For example, doing
nix-store -r /nix/store/kmwd1hq55akdb9sc7l3finr175dajlby-hello-2.10
is forbidden unless /nix/store/kmwd... is in the inputs closure or
previously built by a recursive Nix call. This is to prevent
irreproducible derivations that have hidden dependencies on
substituters or the current store contents. Building a derivation is
fine, however, and Nix will use substitutes if available. In other
words, the builder has to present proof that it knows how to build a
desired store path from scratch by constructing a derivation graph for
that path.
Probably we should also disallow instantiating/building fixed-output
derivations (specifically, those that access the network, but
currently we have no way to mark fixed-output derivations that don't
access the network). Otherwise sandboxed derivations can bypass
sandbox restrictions and access the network.
When sandboxing is enabled, we make paths appear in the sandbox of the
builder by entering the mount namespace of the builder and
bind-mounting each path. This is tricky because we do a pivot_root()
in the builder to change the root directory of its mount namespace,
and thus the host /nix/store is not visible in the mount namespace of
the builder. To get around this, just before doing pivot_root(), we
branch a second mount namespace that shares its /nix/store mountpoint
with the parent.
Recursive Nix currently doesn't work on macOS in sandboxed mode
(because we can't change the sandbox policy of a running build) and in
non-root mode (because setns() barfs).
2018-10-02 17:01:26 +03:00
|
|
|
|
else
|
2022-12-14 15:01:29 +02:00
|
|
|
|
warn("ignoring untrusted substituter '%s', you are not a trusted user.\n"
|
2023-01-03 16:35:28 +02:00
|
|
|
|
"Run `man nix.conf` for more information on the `substituters` configuration option.", s);
|
Recursive Nix support
This allows Nix builders to call Nix to build derivations, with some
limitations.
Example:
let nixpkgs = fetchTarball channel:nixos-18.03; in
with import <nixpkgs> {};
runCommand "foo"
{
buildInputs = [ nix jq ];
NIX_PATH = "nixpkgs=${nixpkgs}";
}
''
hello=$(nix-build -E '(import <nixpkgs> {}).hello.overrideDerivation (args: { name = "hello-3.5"; })')
$hello/bin/hello
mkdir -p $out/bin
ln -s $hello/bin/hello $out/bin/hello
nix path-info -r --json $hello | jq .
''
This derivation makes a recursive Nix call to build GNU Hello and
symlinks it from its $out, i.e.
# ll ./result/bin/
lrwxrwxrwx 1 root root 63 Jan 1 1970 hello -> /nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5/bin/hello
# nix-store -qR ./result
/nix/store/hwwqshlmazzjzj7yhrkyjydxamvvkfd3-glibc-2.26-131
/nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5
/nix/store/sgmvvyw8vhfqdqb619bxkcpfn9lvd8ss-foo
This is implemented as follows:
* Before running the outer builder, Nix creates a Unix domain socket
'.nix-socket' in the builder's temporary directory and sets
$NIX_REMOTE to point to it. It starts a thread to process
connections to this socket. (Thus you don't need to have nix-daemon
running.)
* The daemon thread uses a wrapper store (RestrictedStore) to keep
track of paths added through recursive Nix calls, to implement some
restrictions (see below), and to do some censorship (e.g. for
purity, queryPathInfo() won't return impure information such as
signatures and timestamps).
* After the build finishes, the output paths are scanned for
references to the paths added through recursive Nix calls (in
addition to the inputs closure). Thus, in the example above, $out
has a reference to $hello.
The main restriction on recursive Nix calls is that they cannot do
arbitrary substitutions. For example, doing
nix-store -r /nix/store/kmwd1hq55akdb9sc7l3finr175dajlby-hello-2.10
is forbidden unless /nix/store/kmwd... is in the inputs closure or
previously built by a recursive Nix call. This is to prevent
irreproducible derivations that have hidden dependencies on
substituters or the current store contents. Building a derivation is
fine, however, and Nix will use substitutes if available. In other
words, the builder has to present proof that it knows how to build a
desired store path from scratch by constructing a derivation graph for
that path.
Probably we should also disallow instantiating/building fixed-output
derivations (specifically, those that access the network, but
currently we have no way to mark fixed-output derivations that don't
access the network). Otherwise sandboxed derivations can bypass
sandbox restrictions and access the network.
When sandboxing is enabled, we make paths appear in the sandbox of the
builder by entering the mount namespace of the builder and
bind-mounting each path. This is tricky because we do a pivot_root()
in the builder to change the root directory of its mount namespace,
and thus the host /nix/store is not visible in the mount namespace of
the builder. To get around this, just before doing pivot_root(), we
branch a second mount namespace that shares its /nix/store mountpoint
with the parent.
Recursive Nix currently doesn't work on macOS in sandboxed mode
(because we can't change the sandbox policy of a running build) and in
non-root mode (because setns() barfs).
2018-10-02 17:01:26 +03:00
|
|
|
|
res = subs;
|
|
|
|
|
return true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
if (name == "ssh-auth-sock") // obsolete
|
|
|
|
|
;
|
2023-03-17 16:33:48 +02:00
|
|
|
|
else if (name == experimentalFeatureSettings.experimentalFeatures.name) {
|
2021-08-30 11:39:31 +03:00
|
|
|
|
// We don’t want to forward the experimental features to
|
|
|
|
|
// the daemon, as that could cause some pretty weird stuff
|
2023-03-17 16:33:48 +02:00
|
|
|
|
if (parseFeatures(tokenizeString<StringSet>(value)) != experimentalFeatureSettings.experimentalFeatures.get())
|
2021-08-30 11:39:31 +03:00
|
|
|
|
debug("Ignoring the client-specified experimental features");
|
2023-02-02 13:03:45 +02:00
|
|
|
|
} else if (name == settings.pluginFiles.name) {
|
|
|
|
|
if (tokenizeString<Paths>(value) != settings.pluginFiles.get())
|
|
|
|
|
warn("Ignoring the client-specified plugin-files.\n"
|
|
|
|
|
"The client specifying plugins to the daemon never made sense, and was removed in Nix >=2.14.");
|
2021-08-30 11:39:31 +03:00
|
|
|
|
}
|
Recursive Nix support
This allows Nix builders to call Nix to build derivations, with some
limitations.
Example:
let nixpkgs = fetchTarball channel:nixos-18.03; in
with import <nixpkgs> {};
runCommand "foo"
{
buildInputs = [ nix jq ];
NIX_PATH = "nixpkgs=${nixpkgs}";
}
''
hello=$(nix-build -E '(import <nixpkgs> {}).hello.overrideDerivation (args: { name = "hello-3.5"; })')
$hello/bin/hello
mkdir -p $out/bin
ln -s $hello/bin/hello $out/bin/hello
nix path-info -r --json $hello | jq .
''
This derivation makes a recursive Nix call to build GNU Hello and
symlinks it from its $out, i.e.
# ll ./result/bin/
lrwxrwxrwx 1 root root 63 Jan 1 1970 hello -> /nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5/bin/hello
# nix-store -qR ./result
/nix/store/hwwqshlmazzjzj7yhrkyjydxamvvkfd3-glibc-2.26-131
/nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5
/nix/store/sgmvvyw8vhfqdqb619bxkcpfn9lvd8ss-foo
This is implemented as follows:
* Before running the outer builder, Nix creates a Unix domain socket
'.nix-socket' in the builder's temporary directory and sets
$NIX_REMOTE to point to it. It starts a thread to process
connections to this socket. (Thus you don't need to have nix-daemon
running.)
* The daemon thread uses a wrapper store (RestrictedStore) to keep
track of paths added through recursive Nix calls, to implement some
restrictions (see below), and to do some censorship (e.g. for
purity, queryPathInfo() won't return impure information such as
signatures and timestamps).
* After the build finishes, the output paths are scanned for
references to the paths added through recursive Nix calls (in
addition to the inputs closure). Thus, in the example above, $out
has a reference to $hello.
The main restriction on recursive Nix calls is that they cannot do
arbitrary substitutions. For example, doing
nix-store -r /nix/store/kmwd1hq55akdb9sc7l3finr175dajlby-hello-2.10
is forbidden unless /nix/store/kmwd... is in the inputs closure or
previously built by a recursive Nix call. This is to prevent
irreproducible derivations that have hidden dependencies on
substituters or the current store contents. Building a derivation is
fine, however, and Nix will use substitutes if available. In other
words, the builder has to present proof that it knows how to build a
desired store path from scratch by constructing a derivation graph for
that path.
Probably we should also disallow instantiating/building fixed-output
derivations (specifically, those that access the network, but
currently we have no way to mark fixed-output derivations that don't
access the network). Otherwise sandboxed derivations can bypass
sandbox restrictions and access the network.
When sandboxing is enabled, we make paths appear in the sandbox of the
builder by entering the mount namespace of the builder and
bind-mounting each path. This is tricky because we do a pivot_root()
in the builder to change the root directory of its mount namespace,
and thus the host /nix/store is not visible in the mount namespace of
the builder. To get around this, just before doing pivot_root(), we
branch a second mount namespace that shares its /nix/store mountpoint
with the parent.
Recursive Nix currently doesn't work on macOS in sandboxed mode
(because we can't change the sandbox policy of a running build) and in
non-root mode (because setns() barfs).
2018-10-02 17:01:26 +03:00
|
|
|
|
else if (trusted
|
|
|
|
|
|| name == settings.buildTimeout.name
|
2022-09-22 21:59:16 +03:00
|
|
|
|
|| name == settings.maxSilentTime.name
|
|
|
|
|
|| name == settings.pollInterval.name
|
Recursive Nix support
This allows Nix builders to call Nix to build derivations, with some
limitations.
Example:
let nixpkgs = fetchTarball channel:nixos-18.03; in
with import <nixpkgs> {};
runCommand "foo"
{
buildInputs = [ nix jq ];
NIX_PATH = "nixpkgs=${nixpkgs}";
}
''
hello=$(nix-build -E '(import <nixpkgs> {}).hello.overrideDerivation (args: { name = "hello-3.5"; })')
$hello/bin/hello
mkdir -p $out/bin
ln -s $hello/bin/hello $out/bin/hello
nix path-info -r --json $hello | jq .
''
This derivation makes a recursive Nix call to build GNU Hello and
symlinks it from its $out, i.e.
# ll ./result/bin/
lrwxrwxrwx 1 root root 63 Jan 1 1970 hello -> /nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5/bin/hello
# nix-store -qR ./result
/nix/store/hwwqshlmazzjzj7yhrkyjydxamvvkfd3-glibc-2.26-131
/nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5
/nix/store/sgmvvyw8vhfqdqb619bxkcpfn9lvd8ss-foo
This is implemented as follows:
* Before running the outer builder, Nix creates a Unix domain socket
'.nix-socket' in the builder's temporary directory and sets
$NIX_REMOTE to point to it. It starts a thread to process
connections to this socket. (Thus you don't need to have nix-daemon
running.)
* The daemon thread uses a wrapper store (RestrictedStore) to keep
track of paths added through recursive Nix calls, to implement some
restrictions (see below), and to do some censorship (e.g. for
purity, queryPathInfo() won't return impure information such as
signatures and timestamps).
* After the build finishes, the output paths are scanned for
references to the paths added through recursive Nix calls (in
addition to the inputs closure). Thus, in the example above, $out
has a reference to $hello.
The main restriction on recursive Nix calls is that they cannot do
arbitrary substitutions. For example, doing
nix-store -r /nix/store/kmwd1hq55akdb9sc7l3finr175dajlby-hello-2.10
is forbidden unless /nix/store/kmwd... is in the inputs closure or
previously built by a recursive Nix call. This is to prevent
irreproducible derivations that have hidden dependencies on
substituters or the current store contents. Building a derivation is
fine, however, and Nix will use substitutes if available. In other
words, the builder has to present proof that it knows how to build a
desired store path from scratch by constructing a derivation graph for
that path.
Probably we should also disallow instantiating/building fixed-output
derivations (specifically, those that access the network, but
currently we have no way to mark fixed-output derivations that don't
access the network). Otherwise sandboxed derivations can bypass
sandbox restrictions and access the network.
When sandboxing is enabled, we make paths appear in the sandbox of the
builder by entering the mount namespace of the builder and
bind-mounting each path. This is tricky because we do a pivot_root()
in the builder to change the root directory of its mount namespace,
and thus the host /nix/store is not visible in the mount namespace of
the builder. To get around this, just before doing pivot_root(), we
branch a second mount namespace that shares its /nix/store mountpoint
with the parent.
Recursive Nix currently doesn't work on macOS in sandboxed mode
(because we can't change the sandbox policy of a running build) and in
non-root mode (because setns() barfs).
2018-10-02 17:01:26 +03:00
|
|
|
|
|| name == "connect-timeout"
|
|
|
|
|
|| (name == "builders" && value == ""))
|
|
|
|
|
settings.set(name, value);
|
|
|
|
|
else if (setSubstituters(settings.substituters))
|
|
|
|
|
;
|
|
|
|
|
else
|
2019-06-08 01:41:19 +03:00
|
|
|
|
warn("ignoring the client-specified setting '%s', because it is a restricted setting and you are not a trusted user", name);
|
Recursive Nix support
This allows Nix builders to call Nix to build derivations, with some
limitations.
Example:
let nixpkgs = fetchTarball channel:nixos-18.03; in
with import <nixpkgs> {};
runCommand "foo"
{
buildInputs = [ nix jq ];
NIX_PATH = "nixpkgs=${nixpkgs}";
}
''
hello=$(nix-build -E '(import <nixpkgs> {}).hello.overrideDerivation (args: { name = "hello-3.5"; })')
$hello/bin/hello
mkdir -p $out/bin
ln -s $hello/bin/hello $out/bin/hello
nix path-info -r --json $hello | jq .
''
This derivation makes a recursive Nix call to build GNU Hello and
symlinks it from its $out, i.e.
# ll ./result/bin/
lrwxrwxrwx 1 root root 63 Jan 1 1970 hello -> /nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5/bin/hello
# nix-store -qR ./result
/nix/store/hwwqshlmazzjzj7yhrkyjydxamvvkfd3-glibc-2.26-131
/nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5
/nix/store/sgmvvyw8vhfqdqb619bxkcpfn9lvd8ss-foo
This is implemented as follows:
* Before running the outer builder, Nix creates a Unix domain socket
'.nix-socket' in the builder's temporary directory and sets
$NIX_REMOTE to point to it. It starts a thread to process
connections to this socket. (Thus you don't need to have nix-daemon
running.)
* The daemon thread uses a wrapper store (RestrictedStore) to keep
track of paths added through recursive Nix calls, to implement some
restrictions (see below), and to do some censorship (e.g. for
purity, queryPathInfo() won't return impure information such as
signatures and timestamps).
* After the build finishes, the output paths are scanned for
references to the paths added through recursive Nix calls (in
addition to the inputs closure). Thus, in the example above, $out
has a reference to $hello.
The main restriction on recursive Nix calls is that they cannot do
arbitrary substitutions. For example, doing
nix-store -r /nix/store/kmwd1hq55akdb9sc7l3finr175dajlby-hello-2.10
is forbidden unless /nix/store/kmwd... is in the inputs closure or
previously built by a recursive Nix call. This is to prevent
irreproducible derivations that have hidden dependencies on
substituters or the current store contents. Building a derivation is
fine, however, and Nix will use substitutes if available. In other
words, the builder has to present proof that it knows how to build a
desired store path from scratch by constructing a derivation graph for
that path.
Probably we should also disallow instantiating/building fixed-output
derivations (specifically, those that access the network, but
currently we have no way to mark fixed-output derivations that don't
access the network). Otherwise sandboxed derivations can bypass
sandbox restrictions and access the network.
When sandboxing is enabled, we make paths appear in the sandbox of the
builder by entering the mount namespace of the builder and
bind-mounting each path. This is tricky because we do a pivot_root()
in the builder to change the root directory of its mount namespace,
and thus the host /nix/store is not visible in the mount namespace of
the builder. To get around this, just before doing pivot_root(), we
branch a second mount namespace that shares its /nix/store mountpoint
with the parent.
Recursive Nix currently doesn't work on macOS in sandboxed mode
(because we can't change the sandbox policy of a running build) and in
non-root mode (because setns() barfs).
2018-10-02 17:01:26 +03:00
|
|
|
|
} catch (UsageError & e) {
|
|
|
|
|
warn(e.what());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2018-09-24 14:53:44 +03:00
|
|
|
|
static void performOp(TunnelLogger * logger, ref<Store> store,
|
2023-10-17 18:15:36 +03:00
|
|
|
|
TrustedFlag trusted, RecursiveFlag recursive, WorkerProto::Version clientVersion,
|
2023-05-26 18:07:25 +03:00
|
|
|
|
Source & from, BufferedSink & to, WorkerProto::Op op)
|
2018-09-24 14:53:44 +03:00
|
|
|
|
{
|
2022-03-25 06:40:49 +02:00
|
|
|
|
WorkerProto::ReadConn rconn {
|
|
|
|
|
.from = from,
|
|
|
|
|
.version = clientVersion,
|
|
|
|
|
};
|
|
|
|
|
WorkerProto::WriteConn wconn {
|
|
|
|
|
.to = to,
|
|
|
|
|
.version = clientVersion,
|
|
|
|
|
};
|
2023-04-17 20:40:46 +03:00
|
|
|
|
|
2018-09-24 14:53:44 +03:00
|
|
|
|
switch (op) {
|
|
|
|
|
|
2023-05-26 18:07:25 +03:00
|
|
|
|
case WorkerProto::Op::IsValidPath: {
|
2019-12-05 20:11:09 +02:00
|
|
|
|
auto path = store->parseStorePath(readString(from));
|
|
|
|
|
logger->startWork();
|
2018-09-24 14:53:44 +03:00
|
|
|
|
bool result = store->isValidPath(path);
|
|
|
|
|
logger->stopWork();
|
|
|
|
|
to << result;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-26 18:07:25 +03:00
|
|
|
|
case WorkerProto::Op::QueryValidPaths: {
|
2023-04-17 20:40:46 +03:00
|
|
|
|
auto paths = WorkerProto::Serialise<StorePathSet>::read(*store, rconn);
|
2020-10-21 22:31:19 +03:00
|
|
|
|
|
|
|
|
|
SubstituteFlag substitute = NoSubstitute;
|
|
|
|
|
if (GET_PROTOCOL_MINOR(clientVersion) >= 27) {
|
|
|
|
|
substitute = readInt(from) ? Substitute : NoSubstitute;
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-24 14:53:44 +03:00
|
|
|
|
logger->startWork();
|
2020-10-21 22:31:19 +03:00
|
|
|
|
if (substitute) {
|
|
|
|
|
store->substitutePaths(paths);
|
|
|
|
|
}
|
|
|
|
|
auto res = store->queryValidPaths(paths, substitute);
|
2018-09-24 14:53:44 +03:00
|
|
|
|
logger->stopWork();
|
2023-04-17 20:40:46 +03:00
|
|
|
|
WorkerProto::write(*store, wconn, res);
|
2018-09-24 14:53:44 +03:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-26 18:07:25 +03:00
|
|
|
|
case WorkerProto::Op::HasSubstitutes: {
|
2019-12-05 20:11:09 +02:00
|
|
|
|
auto path = store->parseStorePath(readString(from));
|
2018-09-24 14:53:44 +03:00
|
|
|
|
logger->startWork();
|
2019-12-05 20:11:09 +02:00
|
|
|
|
StorePathSet paths; // FIXME
|
2020-06-16 23:20:18 +03:00
|
|
|
|
paths.insert(path);
|
2019-12-05 20:11:09 +02:00
|
|
|
|
auto res = store->querySubstitutablePaths(paths);
|
2018-09-24 14:53:44 +03:00
|
|
|
|
logger->stopWork();
|
2019-12-05 20:11:09 +02:00
|
|
|
|
to << (res.count(path) != 0);
|
2018-09-24 14:53:44 +03:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-26 18:07:25 +03:00
|
|
|
|
case WorkerProto::Op::QuerySubstitutablePaths: {
|
2023-04-17 20:40:46 +03:00
|
|
|
|
auto paths = WorkerProto::Serialise<StorePathSet>::read(*store, rconn);
|
2018-09-24 14:53:44 +03:00
|
|
|
|
logger->startWork();
|
2019-12-05 20:11:09 +02:00
|
|
|
|
auto res = store->querySubstitutablePaths(paths);
|
2018-09-24 14:53:44 +03:00
|
|
|
|
logger->stopWork();
|
2023-04-17 20:40:46 +03:00
|
|
|
|
WorkerProto::write(*store, wconn, res);
|
2018-09-24 14:53:44 +03:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-26 18:07:25 +03:00
|
|
|
|
case WorkerProto::Op::QueryPathHash: {
|
2019-12-05 20:11:09 +02:00
|
|
|
|
auto path = store->parseStorePath(readString(from));
|
2018-09-24 14:53:44 +03:00
|
|
|
|
logger->startWork();
|
|
|
|
|
auto hash = store->queryPathInfo(path)->narHash;
|
|
|
|
|
logger->stopWork();
|
2023-10-13 04:48:15 +03:00
|
|
|
|
to << hash.to_string(HashFormat::Base16, false);
|
2018-09-24 14:53:44 +03:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-26 18:07:25 +03:00
|
|
|
|
case WorkerProto::Op::QueryReferences:
|
|
|
|
|
case WorkerProto::Op::QueryReferrers:
|
|
|
|
|
case WorkerProto::Op::QueryValidDerivers:
|
|
|
|
|
case WorkerProto::Op::QueryDerivationOutputs: {
|
2019-12-05 20:11:09 +02:00
|
|
|
|
auto path = store->parseStorePath(readString(from));
|
2018-09-24 14:53:44 +03:00
|
|
|
|
logger->startWork();
|
2019-12-05 20:11:09 +02:00
|
|
|
|
StorePathSet paths;
|
2023-05-26 18:07:25 +03:00
|
|
|
|
if (op == WorkerProto::Op::QueryReferences)
|
2023-01-14 23:38:43 +02:00
|
|
|
|
for (auto & i : store->queryPathInfo(path)->references)
|
2020-06-16 23:20:18 +03:00
|
|
|
|
paths.insert(i);
|
2023-05-26 18:07:25 +03:00
|
|
|
|
else if (op == WorkerProto::Op::QueryReferrers)
|
2018-09-24 14:53:44 +03:00
|
|
|
|
store->queryReferrers(path, paths);
|
2023-05-26 18:07:25 +03:00
|
|
|
|
else if (op == WorkerProto::Op::QueryValidDerivers)
|
2018-09-24 14:53:44 +03:00
|
|
|
|
paths = store->queryValidDerivers(path);
|
|
|
|
|
else paths = store->queryDerivationOutputs(path);
|
|
|
|
|
logger->stopWork();
|
2023-04-17 20:40:46 +03:00
|
|
|
|
WorkerProto::write(*store, wconn, paths);
|
2018-09-24 14:53:44 +03:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-26 18:07:25 +03:00
|
|
|
|
case WorkerProto::Op::QueryDerivationOutputNames: {
|
2019-12-05 20:11:09 +02:00
|
|
|
|
auto path = store->parseStorePath(readString(from));
|
2018-09-24 14:53:44 +03:00
|
|
|
|
logger->startWork();
|
2020-06-12 14:04:52 +03:00
|
|
|
|
auto names = store->readDerivation(path).outputNames();
|
2018-09-24 14:53:44 +03:00
|
|
|
|
logger->stopWork();
|
|
|
|
|
to << names;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-26 18:07:25 +03:00
|
|
|
|
case WorkerProto::Op::QueryDerivationOutputMap: {
|
2020-06-10 12:20:52 +03:00
|
|
|
|
auto path = store->parseStorePath(readString(from));
|
|
|
|
|
logger->startWork();
|
2020-08-20 17:12:51 +03:00
|
|
|
|
auto outputs = store->queryPartialDerivationOutputMap(path);
|
2020-06-10 12:20:52 +03:00
|
|
|
|
logger->stopWork();
|
2023-04-17 20:40:46 +03:00
|
|
|
|
WorkerProto::write(*store, wconn, outputs);
|
2020-06-10 12:20:52 +03:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-26 18:07:25 +03:00
|
|
|
|
case WorkerProto::Op::QueryDeriver: {
|
2019-12-05 20:11:09 +02:00
|
|
|
|
auto path = store->parseStorePath(readString(from));
|
2018-09-24 14:53:44 +03:00
|
|
|
|
logger->startWork();
|
2019-12-05 20:11:09 +02:00
|
|
|
|
auto info = store->queryPathInfo(path);
|
2018-09-24 14:53:44 +03:00
|
|
|
|
logger->stopWork();
|
2019-12-05 20:11:09 +02:00
|
|
|
|
to << (info->deriver ? store->printStorePath(*info->deriver) : "");
|
2018-09-24 14:53:44 +03:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-26 18:07:25 +03:00
|
|
|
|
case WorkerProto::Op::QueryPathFromHashPart: {
|
2019-12-05 20:11:09 +02:00
|
|
|
|
auto hashPart = readString(from);
|
2018-09-24 14:53:44 +03:00
|
|
|
|
logger->startWork();
|
2019-12-05 20:11:09 +02:00
|
|
|
|
auto path = store->queryPathFromHashPart(hashPart);
|
2018-09-24 14:53:44 +03:00
|
|
|
|
logger->stopWork();
|
2019-12-05 20:11:09 +02:00
|
|
|
|
to << (path ? store->printStorePath(*path) : "");
|
2018-09-24 14:53:44 +03:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-26 18:07:25 +03:00
|
|
|
|
case WorkerProto::Op::AddToStore: {
|
2020-09-17 20:27:11 +03:00
|
|
|
|
if (GET_PROTOCOL_MINOR(clientVersion) >= 25) {
|
|
|
|
|
auto name = readString(from);
|
|
|
|
|
auto camStr = readString(from);
|
2023-04-17 20:40:46 +03:00
|
|
|
|
auto refs = WorkerProto::Serialise<StorePathSet>::read(*store, rconn);
|
2020-09-18 11:06:34 +03:00
|
|
|
|
bool repairBool;
|
|
|
|
|
from >> repairBool;
|
|
|
|
|
auto repair = RepairFlag{repairBool};
|
2020-09-17 20:27:11 +03:00
|
|
|
|
|
|
|
|
|
logger->startWork();
|
2020-09-18 12:22:13 +03:00
|
|
|
|
auto pathInfo = [&]() {
|
2020-09-17 20:27:11 +03:00
|
|
|
|
// NB: FramedSource must be out of scope before logger->stopWork();
|
2024-01-19 06:57:26 +02:00
|
|
|
|
auto [contentAddressMethod, hashAlgo] = ContentAddressMethod::parseWithAlgo(camStr);
|
2020-09-17 20:27:11 +03:00
|
|
|
|
FramedSource source(from);
|
2024-01-19 06:57:26 +02:00
|
|
|
|
FileSerialisationMethod dumpMethod;
|
|
|
|
|
switch (contentAddressMethod.getFileIngestionMethod()) {
|
|
|
|
|
case FileIngestionMethod::Flat:
|
|
|
|
|
dumpMethod = FileSerialisationMethod::Flat;
|
|
|
|
|
break;
|
|
|
|
|
case FileIngestionMethod::Recursive:
|
|
|
|
|
dumpMethod = FileSerialisationMethod::Recursive;
|
|
|
|
|
break;
|
|
|
|
|
case FileIngestionMethod::Git:
|
|
|
|
|
// Use NAR; Git is not a serialization method
|
|
|
|
|
dumpMethod = FileSerialisationMethod::Recursive;
|
|
|
|
|
break;
|
2024-02-28 23:59:20 +02:00
|
|
|
|
default:
|
|
|
|
|
assert(false);
|
2024-01-19 06:57:26 +02:00
|
|
|
|
}
|
2023-11-04 22:25:41 +02:00
|
|
|
|
// TODO these two steps are essentially RemoteStore::addCAToStore. Move it up to Store.
|
2024-01-19 06:57:26 +02:00
|
|
|
|
auto path = store->addToStoreFromDump(source, name, dumpMethod, contentAddressMethod, hashAlgo, refs, repair);
|
2023-11-04 22:25:41 +02:00
|
|
|
|
return store->queryPathInfo(path);
|
2020-09-17 20:27:11 +03:00
|
|
|
|
}();
|
|
|
|
|
logger->stopWork();
|
|
|
|
|
|
2022-03-09 01:09:26 +02:00
|
|
|
|
WorkerProto::Serialise<ValidPathInfo>::write(*store, wconn, *pathInfo);
|
2020-09-17 20:27:11 +03:00
|
|
|
|
} else {
|
2023-11-28 15:20:27 +02:00
|
|
|
|
HashAlgorithm hashAlgo;
|
2020-09-17 20:27:11 +03:00
|
|
|
|
std::string baseName;
|
|
|
|
|
FileIngestionMethod method;
|
|
|
|
|
{
|
|
|
|
|
bool fixed;
|
|
|
|
|
uint8_t recursive;
|
|
|
|
|
std::string hashAlgoRaw;
|
|
|
|
|
from >> baseName >> fixed /* obsolete */ >> recursive >> hashAlgoRaw;
|
|
|
|
|
if (recursive > (uint8_t) FileIngestionMethod::Recursive)
|
|
|
|
|
throw Error("unsupported FileIngestionMethod with value of %i; you may need to upgrade nix-daemon", recursive);
|
|
|
|
|
method = FileIngestionMethod { recursive };
|
|
|
|
|
/* Compatibility hack. */
|
|
|
|
|
if (!fixed) {
|
|
|
|
|
hashAlgoRaw = "sha256";
|
|
|
|
|
method = FileIngestionMethod::Recursive;
|
|
|
|
|
}
|
2023-11-28 15:20:27 +02:00
|
|
|
|
hashAlgo = parseHashAlgo(hashAlgoRaw);
|
2020-03-29 08:04:55 +03:00
|
|
|
|
}
|
2018-09-24 14:53:44 +03:00
|
|
|
|
|
2024-01-19 06:57:26 +02:00
|
|
|
|
// Old protocol always sends NAR, regardless of hashing method
|
2021-11-30 22:53:10 +02:00
|
|
|
|
auto dumpSource = sinkToSource([&](Sink & saved) {
|
2024-01-19 06:57:26 +02:00
|
|
|
|
/* We parse the NAR dump through into `saved` unmodified,
|
|
|
|
|
so why all this extra work? We still parse the NAR so
|
|
|
|
|
that we aren't sending arbitrary data to `saved`
|
|
|
|
|
unwittingly`, and we know when the NAR ends so we don't
|
|
|
|
|
consume the rest of `from` and can't parse another
|
|
|
|
|
command. (We don't trust `addToStoreFromDump` to not
|
|
|
|
|
eagerly consume the entire stream it's given, past the
|
|
|
|
|
length of the Nar. */
|
|
|
|
|
TeeSource savedNARSource(from, saved);
|
|
|
|
|
NullFileSystemObjectSink sink; /* just parse the NAR */
|
|
|
|
|
parseDump(sink, savedNARSource);
|
2021-11-30 22:53:10 +02:00
|
|
|
|
});
|
2020-09-17 20:27:11 +03:00
|
|
|
|
logger->startWork();
|
2024-01-19 06:57:26 +02:00
|
|
|
|
auto path = store->addToStoreFromDump(
|
|
|
|
|
*dumpSource, baseName, FileSerialisationMethod::Recursive, method, hashAlgo);
|
2020-09-17 20:27:11 +03:00
|
|
|
|
logger->stopWork();
|
2018-09-24 14:53:44 +03:00
|
|
|
|
|
2020-09-17 20:27:11 +03:00
|
|
|
|
to << store->printStorePath(path);
|
|
|
|
|
}
|
2018-09-24 14:53:44 +03:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-26 18:07:25 +03:00
|
|
|
|
case WorkerProto::Op::AddMultipleToStore: {
|
2021-07-26 14:31:09 +03:00
|
|
|
|
bool repair, dontCheckSigs;
|
|
|
|
|
from >> repair >> dontCheckSigs;
|
|
|
|
|
if (!trusted && dontCheckSigs)
|
|
|
|
|
dontCheckSigs = false;
|
|
|
|
|
|
|
|
|
|
logger->startWork();
|
2022-01-17 20:45:21 +02:00
|
|
|
|
{
|
|
|
|
|
FramedSource source(from);
|
|
|
|
|
store->addMultipleToStore(source,
|
|
|
|
|
RepairFlag{repair},
|
|
|
|
|
dontCheckSigs ? NoCheckSigs : CheckSigs);
|
|
|
|
|
}
|
2021-07-26 14:31:09 +03:00
|
|
|
|
logger->stopWork();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-26 18:07:25 +03:00
|
|
|
|
case WorkerProto::Op::AddTextToStore: {
|
2022-02-25 17:00:00 +02:00
|
|
|
|
std::string suffix = readString(from);
|
|
|
|
|
std::string s = readString(from);
|
2023-04-17 20:40:46 +03:00
|
|
|
|
auto refs = WorkerProto::Serialise<StorePathSet>::read(*store, rconn);
|
2018-09-24 14:53:44 +03:00
|
|
|
|
logger->startWork();
|
2023-11-09 04:11:48 +02:00
|
|
|
|
auto path = ({
|
|
|
|
|
StringSource source { s };
|
2024-01-19 06:57:26 +02:00
|
|
|
|
store->addToStoreFromDump(source, suffix, FileSerialisationMethod::Flat, TextIngestionMethod {}, HashAlgorithm::SHA256, refs, NoRepair);
|
2023-11-09 04:11:48 +02:00
|
|
|
|
});
|
2018-09-24 14:53:44 +03:00
|
|
|
|
logger->stopWork();
|
2019-12-05 20:11:09 +02:00
|
|
|
|
to << store->printStorePath(path);
|
2018-09-24 14:53:44 +03:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-26 18:07:25 +03:00
|
|
|
|
case WorkerProto::Op::ExportPath: {
|
2019-12-05 20:11:09 +02:00
|
|
|
|
auto path = store->parseStorePath(readString(from));
|
2018-09-24 14:53:44 +03:00
|
|
|
|
readInt(from); // obsolete
|
|
|
|
|
logger->startWork();
|
|
|
|
|
TunnelSink sink(to);
|
|
|
|
|
store->exportPath(path, sink);
|
|
|
|
|
logger->stopWork();
|
|
|
|
|
to << 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-26 18:07:25 +03:00
|
|
|
|
case WorkerProto::Op::ImportPaths: {
|
2018-09-24 14:53:44 +03:00
|
|
|
|
logger->startWork();
|
|
|
|
|
TunnelSource source(from, to);
|
2020-07-13 18:37:44 +03:00
|
|
|
|
auto paths = store->importPaths(source,
|
2018-09-24 14:53:44 +03:00
|
|
|
|
trusted ? NoCheckSigs : CheckSigs);
|
|
|
|
|
logger->stopWork();
|
2019-12-05 20:11:09 +02:00
|
|
|
|
Strings paths2;
|
|
|
|
|
for (auto & i : paths) paths2.push_back(store->printStorePath(i));
|
|
|
|
|
to << paths2;
|
2018-09-24 14:53:44 +03:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-26 18:07:25 +03:00
|
|
|
|
case WorkerProto::Op::BuildPaths: {
|
2023-05-26 21:48:11 +03:00
|
|
|
|
auto drvs = WorkerProto::Serialise<DerivedPaths>::read(*store, rconn);
|
2018-09-24 14:53:44 +03:00
|
|
|
|
BuildMode mode = bmNormal;
|
|
|
|
|
if (GET_PROTOCOL_MINOR(clientVersion) >= 15) {
|
|
|
|
|
mode = (BuildMode) readInt(from);
|
|
|
|
|
|
|
|
|
|
/* Repairing is not atomic, so disallowed for "untrusted"
|
Move `trustedUsers` and `allowedUsers` to separate config struct
These settings are not needed for libstore at all, they are just used by
the nix daemon *command* for authorization on unix domain sockets. My
moving them to a new configuration struct just in that file, we avoid
them leaking anywhere else.
Also, it is good to break up the mammoth `Settings` struct in general.
Issue #5638 tracks this.
The message is not changed because I do not want to regress in
convenience to the user. Just saying "this connection is not trusted"
doesn't tell them out to fix the issue. The ideal thing to do would be
to somehow parameterize `processCommand` on how the error should be
displayed, so different sorts of connections can display different
information to the user based on how authentication is performed for the
connection in question. This, however, is a good bit more work, so it is
left for the future.
This came up with me thinking about the tcp:// store (#5265). The larger
project is not TCP *per se*, but the idea that it should be possible for
something else to manage access control to services like the Nix Daemon,
and those services simply trust or trust the incoming connection as they
are told. This is a more capability-oriented way of thinking about trust
than "every server implements its own auth separately" as we are used to today.
Its very great that libstore itself already implements just this model,
and so via this refactor I basically want to "enshrine" that so it
continues to be the case.
2023-02-02 20:20:44 +02:00
|
|
|
|
clients.
|
|
|
|
|
|
|
|
|
|
FIXME: layer violation in this message: the daemon code (i.e.
|
|
|
|
|
this file) knows whether a client/connection is trusted, but it
|
|
|
|
|
does not how how the client was authenticated. The mechanism
|
|
|
|
|
need not be getting the UID of the other end of a Unix Domain
|
|
|
|
|
Socket.
|
|
|
|
|
*/
|
2018-09-24 14:53:44 +03:00
|
|
|
|
if (mode == bmRepair && !trusted)
|
|
|
|
|
throw Error("repairing is not allowed because you are not in 'trusted-users'");
|
|
|
|
|
}
|
|
|
|
|
logger->startWork();
|
|
|
|
|
store->buildPaths(drvs, mode);
|
|
|
|
|
logger->stopWork();
|
|
|
|
|
to << 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-26 18:07:25 +03:00
|
|
|
|
case WorkerProto::Op::BuildPathsWithResults: {
|
2023-05-26 21:48:11 +03:00
|
|
|
|
auto drvs = WorkerProto::Serialise<DerivedPaths>::read(*store, rconn);
|
2022-03-08 20:50:46 +02:00
|
|
|
|
BuildMode mode = bmNormal;
|
|
|
|
|
mode = (BuildMode) readInt(from);
|
|
|
|
|
|
|
|
|
|
/* Repairing is not atomic, so disallowed for "untrusted"
|
Move `trustedUsers` and `allowedUsers` to separate config struct
These settings are not needed for libstore at all, they are just used by
the nix daemon *command* for authorization on unix domain sockets. My
moving them to a new configuration struct just in that file, we avoid
them leaking anywhere else.
Also, it is good to break up the mammoth `Settings` struct in general.
Issue #5638 tracks this.
The message is not changed because I do not want to regress in
convenience to the user. Just saying "this connection is not trusted"
doesn't tell them out to fix the issue. The ideal thing to do would be
to somehow parameterize `processCommand` on how the error should be
displayed, so different sorts of connections can display different
information to the user based on how authentication is performed for the
connection in question. This, however, is a good bit more work, so it is
left for the future.
This came up with me thinking about the tcp:// store (#5265). The larger
project is not TCP *per se*, but the idea that it should be possible for
something else to manage access control to services like the Nix Daemon,
and those services simply trust or trust the incoming connection as they
are told. This is a more capability-oriented way of thinking about trust
than "every server implements its own auth separately" as we are used to today.
Its very great that libstore itself already implements just this model,
and so via this refactor I basically want to "enshrine" that so it
continues to be the case.
2023-02-02 20:20:44 +02:00
|
|
|
|
clients.
|
|
|
|
|
|
|
|
|
|
FIXME: layer violation; see above. */
|
2022-03-08 20:50:46 +02:00
|
|
|
|
if (mode == bmRepair && !trusted)
|
|
|
|
|
throw Error("repairing is not allowed because you are not in 'trusted-users'");
|
|
|
|
|
|
|
|
|
|
logger->startWork();
|
|
|
|
|
auto results = store->buildPathsWithResults(drvs, mode);
|
|
|
|
|
logger->stopWork();
|
|
|
|
|
|
2023-04-17 20:40:46 +03:00
|
|
|
|
WorkerProto::write(*store, wconn, results);
|
2022-03-08 20:50:46 +02:00
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-26 18:07:25 +03:00
|
|
|
|
case WorkerProto::Op::BuildDerivation: {
|
2019-12-05 20:11:09 +02:00
|
|
|
|
auto drvPath = store->parseStorePath(readString(from));
|
2018-09-24 14:53:44 +03:00
|
|
|
|
BasicDerivation drv;
|
2023-12-10 23:58:35 +02:00
|
|
|
|
/*
|
|
|
|
|
* Note: unlike wopEnsurePath, this operation reads a
|
|
|
|
|
* derivation-to-be-realized from the client with
|
|
|
|
|
* readDerivation(Source,Store) rather than reading it from
|
|
|
|
|
* the local store with Store::readDerivation(). Since the
|
|
|
|
|
* derivation-to-be-realized is not registered in the store
|
|
|
|
|
* it cannot be trusted that its outPath was calculated
|
|
|
|
|
* correctly.
|
|
|
|
|
*/
|
2020-07-12 18:26:30 +03:00
|
|
|
|
readDerivation(from, *store, drv, Derivation::nameFromPath(drvPath));
|
2018-09-24 14:53:44 +03:00
|
|
|
|
BuildMode buildMode = (BuildMode) readInt(from);
|
|
|
|
|
logger->startWork();
|
2020-08-11 16:43:29 +03:00
|
|
|
|
|
2022-03-18 02:36:52 +02:00
|
|
|
|
auto drvType = drv.type();
|
|
|
|
|
|
2020-08-11 16:43:29 +03:00
|
|
|
|
/* Content-addressed derivations are trustless because their output paths
|
|
|
|
|
are verified by their content alone, so any derivation is free to
|
|
|
|
|
try to produce such a path.
|
|
|
|
|
|
|
|
|
|
Input-addressed derivation output paths, however, are calculated
|
|
|
|
|
from the derivation closure that produced them---even knowing the
|
|
|
|
|
root derivation is not enough. That the output data actually came
|
|
|
|
|
from those derivations is fundamentally unverifiable, but the daemon
|
|
|
|
|
trusts itself on that matter. The question instead is whether the
|
|
|
|
|
submitted plan has rights to the output paths it wants to fill, and
|
|
|
|
|
at least the derivation closure proves that.
|
|
|
|
|
|
|
|
|
|
It would have been nice if input-address algorithm merely depended
|
|
|
|
|
on the build time closure, rather than depending on the derivation
|
|
|
|
|
closure. That would mean input-addressed paths used at build time
|
|
|
|
|
would just be trusted and not need their own evidence. This is in
|
|
|
|
|
fact fine as the same guarantees would hold *inductively*: either
|
|
|
|
|
the remote builder has those paths and already trusts them, or it
|
|
|
|
|
needs to build them too and thus their evidence must be provided in
|
|
|
|
|
turn. The advantage of this variant algorithm is that the evidence
|
|
|
|
|
for input-addressed paths which the remote builder already has
|
|
|
|
|
doesn't need to be sent again.
|
|
|
|
|
|
|
|
|
|
That said, now that we have floating CA derivations, it is better
|
|
|
|
|
that people just migrate to those which also solve this problem, and
|
|
|
|
|
others. It's the same migration difficulty with strictly more
|
|
|
|
|
benefit.
|
|
|
|
|
|
|
|
|
|
Lastly, do note that when we parse fixed-output content-addressed
|
|
|
|
|
derivations, we throw out the precomputed output paths and just
|
|
|
|
|
store the hashes, so there aren't two competing sources of truth an
|
|
|
|
|
attacker could exploit. */
|
2022-03-18 02:36:52 +02:00
|
|
|
|
if (!(drvType.isCA() || trusted))
|
2020-08-11 16:43:29 +03:00
|
|
|
|
throw Error("you are not privileged to build input-addressed derivations");
|
|
|
|
|
|
|
|
|
|
/* Make sure that the non-input-addressed derivations that got this far
|
|
|
|
|
are in fact content-addressed if we don't trust them. */
|
2022-03-18 02:36:52 +02:00
|
|
|
|
assert(drvType.isCA() || trusted);
|
2020-08-11 16:43:29 +03:00
|
|
|
|
|
2020-08-22 23:44:47 +03:00
|
|
|
|
/* Recompute the derivation path when we cannot trust the original. */
|
|
|
|
|
if (!trusted) {
|
|
|
|
|
/* Recomputing the derivation path for input-address derivations
|
|
|
|
|
makes it harder to audit them after the fact, since we need the
|
|
|
|
|
original not-necessarily-resolved derivation to verify the drv
|
|
|
|
|
derivation as adequate claim to the input-addressed output
|
|
|
|
|
paths. */
|
2022-03-18 02:36:52 +02:00
|
|
|
|
assert(drvType.isCA());
|
2020-08-22 23:44:47 +03:00
|
|
|
|
|
|
|
|
|
Derivation drv2;
|
|
|
|
|
static_cast<BasicDerivation &>(drv2) = drv;
|
|
|
|
|
drvPath = writeDerivation(*store, Derivation { drv2 });
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-24 14:53:44 +03:00
|
|
|
|
auto res = store->buildDerivation(drvPath, drv, buildMode);
|
|
|
|
|
logger->stopWork();
|
2021-02-28 20:42:46 +02:00
|
|
|
|
WorkerProto::write(*store, wconn, res);
|
2018-09-24 14:53:44 +03:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-26 18:07:25 +03:00
|
|
|
|
case WorkerProto::Op::EnsurePath: {
|
2019-12-05 20:11:09 +02:00
|
|
|
|
auto path = store->parseStorePath(readString(from));
|
2018-09-24 14:53:44 +03:00
|
|
|
|
logger->startWork();
|
|
|
|
|
store->ensurePath(path);
|
|
|
|
|
logger->stopWork();
|
|
|
|
|
to << 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-26 18:07:25 +03:00
|
|
|
|
case WorkerProto::Op::AddTempRoot: {
|
2019-12-05 20:11:09 +02:00
|
|
|
|
auto path = store->parseStorePath(readString(from));
|
2018-09-24 14:53:44 +03:00
|
|
|
|
logger->startWork();
|
|
|
|
|
store->addTempRoot(path);
|
|
|
|
|
logger->stopWork();
|
|
|
|
|
to << 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-17 19:04:18 +03:00
|
|
|
|
case WorkerProto::Op::AddPermRoot: {
|
|
|
|
|
if (!trusted)
|
|
|
|
|
throw Error(
|
|
|
|
|
"you are not privileged to create perm roots\n\n"
|
|
|
|
|
"hint: you can just do this client-side without special privileges, and probably want to do that instead.");
|
|
|
|
|
auto storePath = WorkerProto::Serialise<StorePath>::read(*store, rconn);
|
|
|
|
|
Path gcRoot = absPath(readString(from));
|
|
|
|
|
logger->startWork();
|
|
|
|
|
auto & localFSStore = require<LocalFSStore>(*store);
|
|
|
|
|
localFSStore.addPermRoot(storePath, gcRoot);
|
|
|
|
|
logger->stopWork();
|
|
|
|
|
to << gcRoot;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-26 18:07:25 +03:00
|
|
|
|
case WorkerProto::Op::AddIndirectRoot: {
|
2018-09-24 14:53:44 +03:00
|
|
|
|
Path path = absPath(readString(from));
|
2022-03-01 20:31:36 +02:00
|
|
|
|
|
2018-09-24 14:53:44 +03:00
|
|
|
|
logger->startWork();
|
2023-03-23 16:06:45 +02:00
|
|
|
|
auto & indirectRootStore = require<IndirectRootStore>(*store);
|
|
|
|
|
indirectRootStore.addIndirectRoot(path);
|
2018-09-24 14:53:44 +03:00
|
|
|
|
logger->stopWork();
|
2022-03-01 20:31:36 +02:00
|
|
|
|
|
2018-09-24 14:53:44 +03:00
|
|
|
|
to << 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-17 19:53:14 +03:00
|
|
|
|
// Obsolete.
|
2023-05-26 18:07:25 +03:00
|
|
|
|
case WorkerProto::Op::SyncWithGC: {
|
2018-09-24 14:53:44 +03:00
|
|
|
|
logger->startWork();
|
|
|
|
|
logger->stopWork();
|
|
|
|
|
to << 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-26 18:07:25 +03:00
|
|
|
|
case WorkerProto::Op::FindRoots: {
|
2018-09-24 14:53:44 +03:00
|
|
|
|
logger->startWork();
|
2022-03-09 17:27:48 +02:00
|
|
|
|
auto & gcStore = require<GcStore>(*store);
|
2022-03-01 20:31:36 +02:00
|
|
|
|
Roots roots = gcStore.findRoots(!trusted);
|
2018-09-24 14:53:44 +03:00
|
|
|
|
logger->stopWork();
|
|
|
|
|
|
|
|
|
|
size_t size = 0;
|
|
|
|
|
for (auto & i : roots)
|
|
|
|
|
size += i.second.size();
|
|
|
|
|
|
|
|
|
|
to << size;
|
|
|
|
|
|
|
|
|
|
for (auto & [target, links] : roots)
|
|
|
|
|
for (auto & link : links)
|
2019-12-05 20:11:09 +02:00
|
|
|
|
to << link << store->printStorePath(target);
|
2018-09-24 14:53:44 +03:00
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-26 18:07:25 +03:00
|
|
|
|
case WorkerProto::Op::CollectGarbage: {
|
2018-09-24 14:53:44 +03:00
|
|
|
|
GCOptions options;
|
|
|
|
|
options.action = (GCOptions::GCAction) readInt(from);
|
2023-04-17 20:40:46 +03:00
|
|
|
|
options.pathsToDelete = WorkerProto::Serialise<StorePathSet>::read(*store, rconn);
|
2018-09-24 14:53:44 +03:00
|
|
|
|
from >> options.ignoreLiveness >> options.maxFreed;
|
|
|
|
|
// obsolete fields
|
|
|
|
|
readInt(from);
|
|
|
|
|
readInt(from);
|
|
|
|
|
readInt(from);
|
|
|
|
|
|
|
|
|
|
GCResults results;
|
|
|
|
|
|
|
|
|
|
logger->startWork();
|
|
|
|
|
if (options.ignoreLiveness)
|
|
|
|
|
throw Error("you are not allowed to ignore liveness");
|
2022-03-09 17:27:48 +02:00
|
|
|
|
auto & gcStore = require<GcStore>(*store);
|
2022-03-01 20:31:36 +02:00
|
|
|
|
gcStore.collectGarbage(options, results);
|
2018-09-24 14:53:44 +03:00
|
|
|
|
logger->stopWork();
|
|
|
|
|
|
|
|
|
|
to << results.paths << results.bytesFreed << 0 /* obsolete */;
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-26 18:07:25 +03:00
|
|
|
|
case WorkerProto::Op::SetOptions: {
|
Recursive Nix support
This allows Nix builders to call Nix to build derivations, with some
limitations.
Example:
let nixpkgs = fetchTarball channel:nixos-18.03; in
with import <nixpkgs> {};
runCommand "foo"
{
buildInputs = [ nix jq ];
NIX_PATH = "nixpkgs=${nixpkgs}";
}
''
hello=$(nix-build -E '(import <nixpkgs> {}).hello.overrideDerivation (args: { name = "hello-3.5"; })')
$hello/bin/hello
mkdir -p $out/bin
ln -s $hello/bin/hello $out/bin/hello
nix path-info -r --json $hello | jq .
''
This derivation makes a recursive Nix call to build GNU Hello and
symlinks it from its $out, i.e.
# ll ./result/bin/
lrwxrwxrwx 1 root root 63 Jan 1 1970 hello -> /nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5/bin/hello
# nix-store -qR ./result
/nix/store/hwwqshlmazzjzj7yhrkyjydxamvvkfd3-glibc-2.26-131
/nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5
/nix/store/sgmvvyw8vhfqdqb619bxkcpfn9lvd8ss-foo
This is implemented as follows:
* Before running the outer builder, Nix creates a Unix domain socket
'.nix-socket' in the builder's temporary directory and sets
$NIX_REMOTE to point to it. It starts a thread to process
connections to this socket. (Thus you don't need to have nix-daemon
running.)
* The daemon thread uses a wrapper store (RestrictedStore) to keep
track of paths added through recursive Nix calls, to implement some
restrictions (see below), and to do some censorship (e.g. for
purity, queryPathInfo() won't return impure information such as
signatures and timestamps).
* After the build finishes, the output paths are scanned for
references to the paths added through recursive Nix calls (in
addition to the inputs closure). Thus, in the example above, $out
has a reference to $hello.
The main restriction on recursive Nix calls is that they cannot do
arbitrary substitutions. For example, doing
nix-store -r /nix/store/kmwd1hq55akdb9sc7l3finr175dajlby-hello-2.10
is forbidden unless /nix/store/kmwd... is in the inputs closure or
previously built by a recursive Nix call. This is to prevent
irreproducible derivations that have hidden dependencies on
substituters or the current store contents. Building a derivation is
fine, however, and Nix will use substitutes if available. In other
words, the builder has to present proof that it knows how to build a
desired store path from scratch by constructing a derivation graph for
that path.
Probably we should also disallow instantiating/building fixed-output
derivations (specifically, those that access the network, but
currently we have no way to mark fixed-output derivations that don't
access the network). Otherwise sandboxed derivations can bypass
sandbox restrictions and access the network.
When sandboxing is enabled, we make paths appear in the sandbox of the
builder by entering the mount namespace of the builder and
bind-mounting each path. This is tricky because we do a pivot_root()
in the builder to change the root directory of its mount namespace,
and thus the host /nix/store is not visible in the mount namespace of
the builder. To get around this, just before doing pivot_root(), we
branch a second mount namespace that shares its /nix/store mountpoint
with the parent.
Recursive Nix currently doesn't work on macOS in sandboxed mode
(because we can't change the sandbox policy of a running build) and in
non-root mode (because setns() barfs).
2018-10-02 17:01:26 +03:00
|
|
|
|
|
|
|
|
|
ClientSettings clientSettings;
|
|
|
|
|
|
|
|
|
|
clientSettings.keepFailed = readInt(from);
|
|
|
|
|
clientSettings.keepGoing = readInt(from);
|
|
|
|
|
clientSettings.tryFallback = readInt(from);
|
|
|
|
|
clientSettings.verbosity = (Verbosity) readInt(from);
|
|
|
|
|
clientSettings.maxBuildJobs = readInt(from);
|
|
|
|
|
clientSettings.maxSilentTime = readInt(from);
|
2018-09-24 14:53:44 +03:00
|
|
|
|
readInt(from); // obsolete useBuildHook
|
Recursive Nix support
This allows Nix builders to call Nix to build derivations, with some
limitations.
Example:
let nixpkgs = fetchTarball channel:nixos-18.03; in
with import <nixpkgs> {};
runCommand "foo"
{
buildInputs = [ nix jq ];
NIX_PATH = "nixpkgs=${nixpkgs}";
}
''
hello=$(nix-build -E '(import <nixpkgs> {}).hello.overrideDerivation (args: { name = "hello-3.5"; })')
$hello/bin/hello
mkdir -p $out/bin
ln -s $hello/bin/hello $out/bin/hello
nix path-info -r --json $hello | jq .
''
This derivation makes a recursive Nix call to build GNU Hello and
symlinks it from its $out, i.e.
# ll ./result/bin/
lrwxrwxrwx 1 root root 63 Jan 1 1970 hello -> /nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5/bin/hello
# nix-store -qR ./result
/nix/store/hwwqshlmazzjzj7yhrkyjydxamvvkfd3-glibc-2.26-131
/nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5
/nix/store/sgmvvyw8vhfqdqb619bxkcpfn9lvd8ss-foo
This is implemented as follows:
* Before running the outer builder, Nix creates a Unix domain socket
'.nix-socket' in the builder's temporary directory and sets
$NIX_REMOTE to point to it. It starts a thread to process
connections to this socket. (Thus you don't need to have nix-daemon
running.)
* The daemon thread uses a wrapper store (RestrictedStore) to keep
track of paths added through recursive Nix calls, to implement some
restrictions (see below), and to do some censorship (e.g. for
purity, queryPathInfo() won't return impure information such as
signatures and timestamps).
* After the build finishes, the output paths are scanned for
references to the paths added through recursive Nix calls (in
addition to the inputs closure). Thus, in the example above, $out
has a reference to $hello.
The main restriction on recursive Nix calls is that they cannot do
arbitrary substitutions. For example, doing
nix-store -r /nix/store/kmwd1hq55akdb9sc7l3finr175dajlby-hello-2.10
is forbidden unless /nix/store/kmwd... is in the inputs closure or
previously built by a recursive Nix call. This is to prevent
irreproducible derivations that have hidden dependencies on
substituters or the current store contents. Building a derivation is
fine, however, and Nix will use substitutes if available. In other
words, the builder has to present proof that it knows how to build a
desired store path from scratch by constructing a derivation graph for
that path.
Probably we should also disallow instantiating/building fixed-output
derivations (specifically, those that access the network, but
currently we have no way to mark fixed-output derivations that don't
access the network). Otherwise sandboxed derivations can bypass
sandbox restrictions and access the network.
When sandboxing is enabled, we make paths appear in the sandbox of the
builder by entering the mount namespace of the builder and
bind-mounting each path. This is tricky because we do a pivot_root()
in the builder to change the root directory of its mount namespace,
and thus the host /nix/store is not visible in the mount namespace of
the builder. To get around this, just before doing pivot_root(), we
branch a second mount namespace that shares its /nix/store mountpoint
with the parent.
Recursive Nix currently doesn't work on macOS in sandboxed mode
(because we can't change the sandbox policy of a running build) and in
non-root mode (because setns() barfs).
2018-10-02 17:01:26 +03:00
|
|
|
|
clientSettings.verboseBuild = lvlError == (Verbosity) readInt(from);
|
2018-09-24 14:53:44 +03:00
|
|
|
|
readInt(from); // obsolete logType
|
|
|
|
|
readInt(from); // obsolete printBuildTrace
|
Recursive Nix support
This allows Nix builders to call Nix to build derivations, with some
limitations.
Example:
let nixpkgs = fetchTarball channel:nixos-18.03; in
with import <nixpkgs> {};
runCommand "foo"
{
buildInputs = [ nix jq ];
NIX_PATH = "nixpkgs=${nixpkgs}";
}
''
hello=$(nix-build -E '(import <nixpkgs> {}).hello.overrideDerivation (args: { name = "hello-3.5"; })')
$hello/bin/hello
mkdir -p $out/bin
ln -s $hello/bin/hello $out/bin/hello
nix path-info -r --json $hello | jq .
''
This derivation makes a recursive Nix call to build GNU Hello and
symlinks it from its $out, i.e.
# ll ./result/bin/
lrwxrwxrwx 1 root root 63 Jan 1 1970 hello -> /nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5/bin/hello
# nix-store -qR ./result
/nix/store/hwwqshlmazzjzj7yhrkyjydxamvvkfd3-glibc-2.26-131
/nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5
/nix/store/sgmvvyw8vhfqdqb619bxkcpfn9lvd8ss-foo
This is implemented as follows:
* Before running the outer builder, Nix creates a Unix domain socket
'.nix-socket' in the builder's temporary directory and sets
$NIX_REMOTE to point to it. It starts a thread to process
connections to this socket. (Thus you don't need to have nix-daemon
running.)
* The daemon thread uses a wrapper store (RestrictedStore) to keep
track of paths added through recursive Nix calls, to implement some
restrictions (see below), and to do some censorship (e.g. for
purity, queryPathInfo() won't return impure information such as
signatures and timestamps).
* After the build finishes, the output paths are scanned for
references to the paths added through recursive Nix calls (in
addition to the inputs closure). Thus, in the example above, $out
has a reference to $hello.
The main restriction on recursive Nix calls is that they cannot do
arbitrary substitutions. For example, doing
nix-store -r /nix/store/kmwd1hq55akdb9sc7l3finr175dajlby-hello-2.10
is forbidden unless /nix/store/kmwd... is in the inputs closure or
previously built by a recursive Nix call. This is to prevent
irreproducible derivations that have hidden dependencies on
substituters or the current store contents. Building a derivation is
fine, however, and Nix will use substitutes if available. In other
words, the builder has to present proof that it knows how to build a
desired store path from scratch by constructing a derivation graph for
that path.
Probably we should also disallow instantiating/building fixed-output
derivations (specifically, those that access the network, but
currently we have no way to mark fixed-output derivations that don't
access the network). Otherwise sandboxed derivations can bypass
sandbox restrictions and access the network.
When sandboxing is enabled, we make paths appear in the sandbox of the
builder by entering the mount namespace of the builder and
bind-mounting each path. This is tricky because we do a pivot_root()
in the builder to change the root directory of its mount namespace,
and thus the host /nix/store is not visible in the mount namespace of
the builder. To get around this, just before doing pivot_root(), we
branch a second mount namespace that shares its /nix/store mountpoint
with the parent.
Recursive Nix currently doesn't work on macOS in sandboxed mode
(because we can't change the sandbox policy of a running build) and in
non-root mode (because setns() barfs).
2018-10-02 17:01:26 +03:00
|
|
|
|
clientSettings.buildCores = readInt(from);
|
|
|
|
|
clientSettings.useSubstitutes = readInt(from);
|
2018-09-24 14:53:44 +03:00
|
|
|
|
|
|
|
|
|
if (GET_PROTOCOL_MINOR(clientVersion) >= 12) {
|
|
|
|
|
unsigned int n = readInt(from);
|
|
|
|
|
for (unsigned int i = 0; i < n; i++) {
|
2022-02-25 17:00:00 +02:00
|
|
|
|
auto name = readString(from);
|
|
|
|
|
auto value = readString(from);
|
Recursive Nix support
This allows Nix builders to call Nix to build derivations, with some
limitations.
Example:
let nixpkgs = fetchTarball channel:nixos-18.03; in
with import <nixpkgs> {};
runCommand "foo"
{
buildInputs = [ nix jq ];
NIX_PATH = "nixpkgs=${nixpkgs}";
}
''
hello=$(nix-build -E '(import <nixpkgs> {}).hello.overrideDerivation (args: { name = "hello-3.5"; })')
$hello/bin/hello
mkdir -p $out/bin
ln -s $hello/bin/hello $out/bin/hello
nix path-info -r --json $hello | jq .
''
This derivation makes a recursive Nix call to build GNU Hello and
symlinks it from its $out, i.e.
# ll ./result/bin/
lrwxrwxrwx 1 root root 63 Jan 1 1970 hello -> /nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5/bin/hello
# nix-store -qR ./result
/nix/store/hwwqshlmazzjzj7yhrkyjydxamvvkfd3-glibc-2.26-131
/nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5
/nix/store/sgmvvyw8vhfqdqb619bxkcpfn9lvd8ss-foo
This is implemented as follows:
* Before running the outer builder, Nix creates a Unix domain socket
'.nix-socket' in the builder's temporary directory and sets
$NIX_REMOTE to point to it. It starts a thread to process
connections to this socket. (Thus you don't need to have nix-daemon
running.)
* The daemon thread uses a wrapper store (RestrictedStore) to keep
track of paths added through recursive Nix calls, to implement some
restrictions (see below), and to do some censorship (e.g. for
purity, queryPathInfo() won't return impure information such as
signatures and timestamps).
* After the build finishes, the output paths are scanned for
references to the paths added through recursive Nix calls (in
addition to the inputs closure). Thus, in the example above, $out
has a reference to $hello.
The main restriction on recursive Nix calls is that they cannot do
arbitrary substitutions. For example, doing
nix-store -r /nix/store/kmwd1hq55akdb9sc7l3finr175dajlby-hello-2.10
is forbidden unless /nix/store/kmwd... is in the inputs closure or
previously built by a recursive Nix call. This is to prevent
irreproducible derivations that have hidden dependencies on
substituters or the current store contents. Building a derivation is
fine, however, and Nix will use substitutes if available. In other
words, the builder has to present proof that it knows how to build a
desired store path from scratch by constructing a derivation graph for
that path.
Probably we should also disallow instantiating/building fixed-output
derivations (specifically, those that access the network, but
currently we have no way to mark fixed-output derivations that don't
access the network). Otherwise sandboxed derivations can bypass
sandbox restrictions and access the network.
When sandboxing is enabled, we make paths appear in the sandbox of the
builder by entering the mount namespace of the builder and
bind-mounting each path. This is tricky because we do a pivot_root()
in the builder to change the root directory of its mount namespace,
and thus the host /nix/store is not visible in the mount namespace of
the builder. To get around this, just before doing pivot_root(), we
branch a second mount namespace that shares its /nix/store mountpoint
with the parent.
Recursive Nix currently doesn't work on macOS in sandboxed mode
(because we can't change the sandbox policy of a running build) and in
non-root mode (because setns() barfs).
2018-10-02 17:01:26 +03:00
|
|
|
|
clientSettings.overrides.emplace(name, value);
|
2018-09-24 14:53:44 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
logger->startWork();
|
|
|
|
|
|
Recursive Nix support
This allows Nix builders to call Nix to build derivations, with some
limitations.
Example:
let nixpkgs = fetchTarball channel:nixos-18.03; in
with import <nixpkgs> {};
runCommand "foo"
{
buildInputs = [ nix jq ];
NIX_PATH = "nixpkgs=${nixpkgs}";
}
''
hello=$(nix-build -E '(import <nixpkgs> {}).hello.overrideDerivation (args: { name = "hello-3.5"; })')
$hello/bin/hello
mkdir -p $out/bin
ln -s $hello/bin/hello $out/bin/hello
nix path-info -r --json $hello | jq .
''
This derivation makes a recursive Nix call to build GNU Hello and
symlinks it from its $out, i.e.
# ll ./result/bin/
lrwxrwxrwx 1 root root 63 Jan 1 1970 hello -> /nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5/bin/hello
# nix-store -qR ./result
/nix/store/hwwqshlmazzjzj7yhrkyjydxamvvkfd3-glibc-2.26-131
/nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5
/nix/store/sgmvvyw8vhfqdqb619bxkcpfn9lvd8ss-foo
This is implemented as follows:
* Before running the outer builder, Nix creates a Unix domain socket
'.nix-socket' in the builder's temporary directory and sets
$NIX_REMOTE to point to it. It starts a thread to process
connections to this socket. (Thus you don't need to have nix-daemon
running.)
* The daemon thread uses a wrapper store (RestrictedStore) to keep
track of paths added through recursive Nix calls, to implement some
restrictions (see below), and to do some censorship (e.g. for
purity, queryPathInfo() won't return impure information such as
signatures and timestamps).
* After the build finishes, the output paths are scanned for
references to the paths added through recursive Nix calls (in
addition to the inputs closure). Thus, in the example above, $out
has a reference to $hello.
The main restriction on recursive Nix calls is that they cannot do
arbitrary substitutions. For example, doing
nix-store -r /nix/store/kmwd1hq55akdb9sc7l3finr175dajlby-hello-2.10
is forbidden unless /nix/store/kmwd... is in the inputs closure or
previously built by a recursive Nix call. This is to prevent
irreproducible derivations that have hidden dependencies on
substituters or the current store contents. Building a derivation is
fine, however, and Nix will use substitutes if available. In other
words, the builder has to present proof that it knows how to build a
desired store path from scratch by constructing a derivation graph for
that path.
Probably we should also disallow instantiating/building fixed-output
derivations (specifically, those that access the network, but
currently we have no way to mark fixed-output derivations that don't
access the network). Otherwise sandboxed derivations can bypass
sandbox restrictions and access the network.
When sandboxing is enabled, we make paths appear in the sandbox of the
builder by entering the mount namespace of the builder and
bind-mounting each path. This is tricky because we do a pivot_root()
in the builder to change the root directory of its mount namespace,
and thus the host /nix/store is not visible in the mount namespace of
the builder. To get around this, just before doing pivot_root(), we
branch a second mount namespace that shares its /nix/store mountpoint
with the parent.
Recursive Nix currently doesn't work on macOS in sandboxed mode
(because we can't change the sandbox policy of a running build) and in
non-root mode (because setns() barfs).
2018-10-02 17:01:26 +03:00
|
|
|
|
// FIXME: use some setting in recursive mode. Will need to use
|
|
|
|
|
// non-global variables.
|
|
|
|
|
if (!recursive)
|
|
|
|
|
clientSettings.apply(trusted);
|
2018-09-24 14:53:44 +03:00
|
|
|
|
|
|
|
|
|
logger->stopWork();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-26 18:07:25 +03:00
|
|
|
|
case WorkerProto::Op::QuerySubstitutablePathInfo: {
|
2019-12-05 20:11:09 +02:00
|
|
|
|
auto path = store->parseStorePath(readString(from));
|
2018-09-24 14:53:44 +03:00
|
|
|
|
logger->startWork();
|
|
|
|
|
SubstitutablePathInfos infos;
|
2020-06-17 22:03:05 +03:00
|
|
|
|
store->querySubstitutablePathInfos({{path, std::nullopt}}, infos);
|
2018-09-24 14:53:44 +03:00
|
|
|
|
logger->stopWork();
|
2019-12-05 20:11:09 +02:00
|
|
|
|
auto i = infos.find(path);
|
2018-09-24 14:53:44 +03:00
|
|
|
|
if (i == infos.end())
|
|
|
|
|
to << 0;
|
|
|
|
|
else {
|
2019-12-05 20:11:09 +02:00
|
|
|
|
to << 1
|
|
|
|
|
<< (i->second.deriver ? store->printStorePath(*i->second.deriver) : "");
|
2023-04-17 20:40:46 +03:00
|
|
|
|
WorkerProto::write(*store, wconn, i->second.references);
|
2019-12-05 20:11:09 +02:00
|
|
|
|
to << i->second.downloadSize
|
|
|
|
|
<< i->second.narSize;
|
2018-09-24 14:53:44 +03:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-26 18:07:25 +03:00
|
|
|
|
case WorkerProto::Op::QuerySubstitutablePathInfos: {
|
2018-09-24 14:53:44 +03:00
|
|
|
|
SubstitutablePathInfos infos;
|
2020-06-17 22:03:05 +03:00
|
|
|
|
StorePathCAMap pathsMap = {};
|
2020-06-20 01:06:19 +03:00
|
|
|
|
if (GET_PROTOCOL_MINOR(clientVersion) < 22) {
|
2023-04-17 20:40:46 +03:00
|
|
|
|
auto paths = WorkerProto::Serialise<StorePathSet>::read(*store, rconn);
|
2020-06-20 01:06:19 +03:00
|
|
|
|
for (auto & path : paths)
|
|
|
|
|
pathsMap.emplace(path, std::nullopt);
|
|
|
|
|
} else
|
2023-04-17 20:40:46 +03:00
|
|
|
|
pathsMap = WorkerProto::Serialise<StorePathCAMap>::read(*store, rconn);
|
2020-06-20 01:06:19 +03:00
|
|
|
|
logger->startWork();
|
2020-06-17 22:03:05 +03:00
|
|
|
|
store->querySubstitutablePathInfos(pathsMap, infos);
|
2018-09-24 14:53:44 +03:00
|
|
|
|
logger->stopWork();
|
|
|
|
|
to << infos.size();
|
|
|
|
|
for (auto & i : infos) {
|
2019-12-05 20:11:09 +02:00
|
|
|
|
to << store->printStorePath(i.first)
|
|
|
|
|
<< (i.second.deriver ? store->printStorePath(*i.second.deriver) : "");
|
2023-04-17 20:40:46 +03:00
|
|
|
|
WorkerProto::write(*store, wconn, i.second.references);
|
2019-12-05 20:11:09 +02:00
|
|
|
|
to << i.second.downloadSize << i.second.narSize;
|
2018-09-24 14:53:44 +03:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-26 18:07:25 +03:00
|
|
|
|
case WorkerProto::Op::QueryAllValidPaths: {
|
2018-09-24 14:53:44 +03:00
|
|
|
|
logger->startWork();
|
2019-12-05 20:11:09 +02:00
|
|
|
|
auto paths = store->queryAllValidPaths();
|
2018-09-24 14:53:44 +03:00
|
|
|
|
logger->stopWork();
|
2023-04-17 20:40:46 +03:00
|
|
|
|
WorkerProto::write(*store, wconn, paths);
|
2018-09-24 14:53:44 +03:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-26 18:07:25 +03:00
|
|
|
|
case WorkerProto::Op::QueryPathInfo: {
|
2019-12-05 20:11:09 +02:00
|
|
|
|
auto path = store->parseStorePath(readString(from));
|
2018-09-24 14:53:44 +03:00
|
|
|
|
std::shared_ptr<const ValidPathInfo> info;
|
|
|
|
|
logger->startWork();
|
|
|
|
|
try {
|
|
|
|
|
info = store->queryPathInfo(path);
|
|
|
|
|
} catch (InvalidPath &) {
|
|
|
|
|
if (GET_PROTOCOL_MINOR(clientVersion) < 17) throw;
|
|
|
|
|
}
|
|
|
|
|
logger->stopWork();
|
|
|
|
|
if (info) {
|
|
|
|
|
if (GET_PROTOCOL_MINOR(clientVersion) >= 17)
|
|
|
|
|
to << 1;
|
2023-04-18 00:13:44 +03:00
|
|
|
|
WorkerProto::write(*store, wconn, static_cast<const UnkeyedValidPathInfo &>(*info));
|
2018-09-24 14:53:44 +03:00
|
|
|
|
} else {
|
|
|
|
|
assert(GET_PROTOCOL_MINOR(clientVersion) >= 17);
|
|
|
|
|
to << 0;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-26 18:07:25 +03:00
|
|
|
|
case WorkerProto::Op::OptimiseStore:
|
2018-09-24 14:53:44 +03:00
|
|
|
|
logger->startWork();
|
|
|
|
|
store->optimiseStore();
|
|
|
|
|
logger->stopWork();
|
|
|
|
|
to << 1;
|
|
|
|
|
break;
|
|
|
|
|
|
2023-05-26 18:07:25 +03:00
|
|
|
|
case WorkerProto::Op::VerifyStore: {
|
2018-09-24 14:53:44 +03:00
|
|
|
|
bool checkContents, repair;
|
|
|
|
|
from >> checkContents >> repair;
|
|
|
|
|
logger->startWork();
|
|
|
|
|
if (repair && !trusted)
|
|
|
|
|
throw Error("you are not privileged to repair paths");
|
|
|
|
|
bool errors = store->verifyStore(checkContents, (RepairFlag) repair);
|
|
|
|
|
logger->stopWork();
|
|
|
|
|
to << errors;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-26 18:07:25 +03:00
|
|
|
|
case WorkerProto::Op::AddSignatures: {
|
2019-12-05 20:11:09 +02:00
|
|
|
|
auto path = store->parseStorePath(readString(from));
|
2018-09-24 14:53:44 +03:00
|
|
|
|
StringSet sigs = readStrings<StringSet>(from);
|
|
|
|
|
logger->startWork();
|
|
|
|
|
store->addSignatures(path, sigs);
|
|
|
|
|
logger->stopWork();
|
|
|
|
|
to << 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-26 18:07:25 +03:00
|
|
|
|
case WorkerProto::Op::NarFromPath: {
|
2019-12-05 20:11:09 +02:00
|
|
|
|
auto path = store->parseStorePath(readString(from));
|
2018-09-24 14:53:44 +03:00
|
|
|
|
logger->startWork();
|
|
|
|
|
logger->stopWork();
|
2020-08-15 00:59:31 +03:00
|
|
|
|
dumpPath(store->toRealPath(path), to);
|
2018-09-24 14:53:44 +03:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-26 18:07:25 +03:00
|
|
|
|
case WorkerProto::Op::AddToStoreNar: {
|
2018-09-24 14:53:44 +03:00
|
|
|
|
bool repair, dontCheckSigs;
|
2020-08-06 21:31:48 +03:00
|
|
|
|
auto path = store->parseStorePath(readString(from));
|
2019-12-05 20:11:09 +02:00
|
|
|
|
auto deriver = readString(from);
|
2023-11-28 15:20:27 +02:00
|
|
|
|
auto narHash = Hash::parseAny(readString(from), HashAlgorithm::SHA256);
|
2020-08-06 21:31:48 +03:00
|
|
|
|
ValidPathInfo info { path, narHash };
|
2019-12-05 20:11:09 +02:00
|
|
|
|
if (deriver != "")
|
|
|
|
|
info.deriver = store->parseStorePath(deriver);
|
2023-04-17 20:40:46 +03:00
|
|
|
|
info.references = WorkerProto::Serialise<StorePathSet>::read(*store, rconn);
|
2018-09-24 14:53:44 +03:00
|
|
|
|
from >> info.registrationTime >> info.narSize >> info.ultimate;
|
|
|
|
|
info.sigs = readStrings<StringSet>(from);
|
2023-03-31 00:12:49 +03:00
|
|
|
|
info.ca = ContentAddress::parseOpt(readString(from));
|
2020-06-02 03:37:43 +03:00
|
|
|
|
from >> repair >> dontCheckSigs;
|
2018-09-24 14:53:44 +03:00
|
|
|
|
if (!trusted && dontCheckSigs)
|
|
|
|
|
dontCheckSigs = false;
|
|
|
|
|
if (!trusted)
|
|
|
|
|
info.ultimate = false;
|
|
|
|
|
|
2020-07-29 01:48:39 +03:00
|
|
|
|
if (GET_PROTOCOL_MINOR(clientVersion) >= 23) {
|
|
|
|
|
logger->startWork();
|
|
|
|
|
{
|
|
|
|
|
FramedSource source(from);
|
|
|
|
|
store->addToStore(info, source, (RepairFlag) repair,
|
|
|
|
|
dontCheckSigs ? NoCheckSigs : CheckSigs);
|
|
|
|
|
}
|
|
|
|
|
logger->stopWork();
|
2018-09-24 14:53:44 +03:00
|
|
|
|
}
|
|
|
|
|
|
2020-07-29 01:48:39 +03:00
|
|
|
|
else {
|
|
|
|
|
std::unique_ptr<Source> source;
|
2022-01-17 23:20:05 +02:00
|
|
|
|
StringSink saved;
|
2020-07-29 01:48:39 +03:00
|
|
|
|
if (GET_PROTOCOL_MINOR(clientVersion) >= 21)
|
|
|
|
|
source = std::make_unique<TunnelSource>(from, to);
|
|
|
|
|
else {
|
|
|
|
|
TeeSource tee { from, saved };
|
2024-01-23 00:59:34 +02:00
|
|
|
|
NullFileSystemObjectSink ether;
|
2020-07-29 01:48:39 +03:00
|
|
|
|
parseDump(ether, tee);
|
2022-01-17 23:20:05 +02:00
|
|
|
|
source = std::make_unique<StringSource>(saved.s);
|
2020-07-29 01:48:39 +03:00
|
|
|
|
}
|
2018-09-24 14:53:44 +03:00
|
|
|
|
|
2020-07-29 01:48:39 +03:00
|
|
|
|
logger->startWork();
|
|
|
|
|
|
|
|
|
|
// FIXME: race if addToStore doesn't read source?
|
|
|
|
|
store->addToStore(info, *source, (RepairFlag) repair,
|
|
|
|
|
dontCheckSigs ? NoCheckSigs : CheckSigs);
|
|
|
|
|
|
|
|
|
|
logger->stopWork();
|
|
|
|
|
}
|
2018-09-24 14:53:44 +03:00
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-26 18:07:25 +03:00
|
|
|
|
case WorkerProto::Op::QueryMissing: {
|
2023-05-26 21:48:11 +03:00
|
|
|
|
auto targets = WorkerProto::Serialise<DerivedPaths>::read(*store, rconn);
|
2018-09-24 14:53:44 +03:00
|
|
|
|
logger->startWork();
|
2019-12-05 20:11:09 +02:00
|
|
|
|
StorePathSet willBuild, willSubstitute, unknown;
|
2020-07-30 14:10:49 +03:00
|
|
|
|
uint64_t downloadSize, narSize;
|
2018-09-24 14:53:44 +03:00
|
|
|
|
store->queryMissing(targets, willBuild, willSubstitute, unknown, downloadSize, narSize);
|
|
|
|
|
logger->stopWork();
|
2023-04-17 20:40:46 +03:00
|
|
|
|
WorkerProto::write(*store, wconn, willBuild);
|
|
|
|
|
WorkerProto::write(*store, wconn, willSubstitute);
|
|
|
|
|
WorkerProto::write(*store, wconn, unknown);
|
2019-12-05 20:11:09 +02:00
|
|
|
|
to << downloadSize << narSize;
|
2018-09-24 14:53:44 +03:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-26 18:07:25 +03:00
|
|
|
|
case WorkerProto::Op::RegisterDrvOutput: {
|
2020-10-08 18:36:51 +03:00
|
|
|
|
logger->startWork();
|
2021-05-07 12:06:17 +03:00
|
|
|
|
if (GET_PROTOCOL_MINOR(clientVersion) < 31) {
|
|
|
|
|
auto outputId = DrvOutput::parse(readString(from));
|
|
|
|
|
auto outputPath = StorePath(readString(from));
|
|
|
|
|
store->registerDrvOutput(Realisation{
|
|
|
|
|
.id = outputId, .outPath = outputPath});
|
|
|
|
|
} else {
|
2023-04-17 20:40:46 +03:00
|
|
|
|
auto realisation = WorkerProto::Serialise<Realisation>::read(*store, rconn);
|
2021-05-07 12:06:17 +03:00
|
|
|
|
store->registerDrvOutput(realisation);
|
|
|
|
|
}
|
2020-10-08 18:36:51 +03:00
|
|
|
|
logger->stopWork();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-26 18:07:25 +03:00
|
|
|
|
case WorkerProto::Op::QueryRealisation: {
|
2020-10-08 18:36:51 +03:00
|
|
|
|
logger->startWork();
|
|
|
|
|
auto outputId = DrvOutput::parse(readString(from));
|
|
|
|
|
auto info = store->queryRealisation(outputId);
|
|
|
|
|
logger->stopWork();
|
2021-05-07 12:06:17 +03:00
|
|
|
|
if (GET_PROTOCOL_MINOR(clientVersion) < 31) {
|
|
|
|
|
std::set<StorePath> outPaths;
|
|
|
|
|
if (info) outPaths.insert(info->outPath);
|
2023-04-17 20:40:46 +03:00
|
|
|
|
WorkerProto::write(*store, wconn, outPaths);
|
2021-05-07 12:06:17 +03:00
|
|
|
|
} else {
|
|
|
|
|
std::set<Realisation> realisations;
|
|
|
|
|
if (info) realisations.insert(*info);
|
2023-04-17 20:40:46 +03:00
|
|
|
|
WorkerProto::write(*store, wconn, realisations);
|
2021-05-07 12:06:17 +03:00
|
|
|
|
}
|
2020-10-08 18:36:51 +03:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-26 18:07:25 +03:00
|
|
|
|
case WorkerProto::Op::AddBuildLog: {
|
2022-01-17 20:45:21 +02:00
|
|
|
|
StorePath path{readString(from)};
|
|
|
|
|
logger->startWork();
|
|
|
|
|
if (!trusted)
|
|
|
|
|
throw Error("you are not privileged to add logs");
|
2022-03-09 17:27:48 +02:00
|
|
|
|
auto & logStore = require<LogStore>(*store);
|
2022-01-17 20:45:21 +02:00
|
|
|
|
{
|
|
|
|
|
FramedSource source(from);
|
|
|
|
|
StringSink sink;
|
|
|
|
|
source.drainInto(sink);
|
2022-03-08 20:20:39 +02:00
|
|
|
|
logStore.addBuildLog(path, sink.s);
|
2022-01-17 20:45:21 +02:00
|
|
|
|
}
|
|
|
|
|
logger->stopWork();
|
|
|
|
|
to << 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-26 18:07:25 +03:00
|
|
|
|
case WorkerProto::Op::QueryFailedPaths:
|
|
|
|
|
case WorkerProto::Op::ClearFailedPaths:
|
|
|
|
|
throw Error("Removed operation %1%", op);
|
|
|
|
|
|
2018-09-24 14:53:44 +03:00
|
|
|
|
default:
|
2020-04-22 02:07:07 +03:00
|
|
|
|
throw Error("invalid operation %1%", op);
|
2018-09-24 14:53:44 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void processConnection(
|
2018-09-25 13:49:20 +03:00
|
|
|
|
ref<Store> store,
|
2018-09-24 14:53:44 +03:00
|
|
|
|
FdSource & from,
|
|
|
|
|
FdSink & to,
|
Recursive Nix support
This allows Nix builders to call Nix to build derivations, with some
limitations.
Example:
let nixpkgs = fetchTarball channel:nixos-18.03; in
with import <nixpkgs> {};
runCommand "foo"
{
buildInputs = [ nix jq ];
NIX_PATH = "nixpkgs=${nixpkgs}";
}
''
hello=$(nix-build -E '(import <nixpkgs> {}).hello.overrideDerivation (args: { name = "hello-3.5"; })')
$hello/bin/hello
mkdir -p $out/bin
ln -s $hello/bin/hello $out/bin/hello
nix path-info -r --json $hello | jq .
''
This derivation makes a recursive Nix call to build GNU Hello and
symlinks it from its $out, i.e.
# ll ./result/bin/
lrwxrwxrwx 1 root root 63 Jan 1 1970 hello -> /nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5/bin/hello
# nix-store -qR ./result
/nix/store/hwwqshlmazzjzj7yhrkyjydxamvvkfd3-glibc-2.26-131
/nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5
/nix/store/sgmvvyw8vhfqdqb619bxkcpfn9lvd8ss-foo
This is implemented as follows:
* Before running the outer builder, Nix creates a Unix domain socket
'.nix-socket' in the builder's temporary directory and sets
$NIX_REMOTE to point to it. It starts a thread to process
connections to this socket. (Thus you don't need to have nix-daemon
running.)
* The daemon thread uses a wrapper store (RestrictedStore) to keep
track of paths added through recursive Nix calls, to implement some
restrictions (see below), and to do some censorship (e.g. for
purity, queryPathInfo() won't return impure information such as
signatures and timestamps).
* After the build finishes, the output paths are scanned for
references to the paths added through recursive Nix calls (in
addition to the inputs closure). Thus, in the example above, $out
has a reference to $hello.
The main restriction on recursive Nix calls is that they cannot do
arbitrary substitutions. For example, doing
nix-store -r /nix/store/kmwd1hq55akdb9sc7l3finr175dajlby-hello-2.10
is forbidden unless /nix/store/kmwd... is in the inputs closure or
previously built by a recursive Nix call. This is to prevent
irreproducible derivations that have hidden dependencies on
substituters or the current store contents. Building a derivation is
fine, however, and Nix will use substitutes if available. In other
words, the builder has to present proof that it knows how to build a
desired store path from scratch by constructing a derivation graph for
that path.
Probably we should also disallow instantiating/building fixed-output
derivations (specifically, those that access the network, but
currently we have no way to mark fixed-output derivations that don't
access the network). Otherwise sandboxed derivations can bypass
sandbox restrictions and access the network.
When sandboxing is enabled, we make paths appear in the sandbox of the
builder by entering the mount namespace of the builder and
bind-mounting each path. This is tricky because we do a pivot_root()
in the builder to change the root directory of its mount namespace,
and thus the host /nix/store is not visible in the mount namespace of
the builder. To get around this, just before doing pivot_root(), we
branch a second mount namespace that shares its /nix/store mountpoint
with the parent.
Recursive Nix currently doesn't work on macOS in sandboxed mode
(because we can't change the sandbox policy of a running build) and in
non-root mode (because setns() barfs).
2018-10-02 17:01:26 +03:00
|
|
|
|
TrustedFlag trusted,
|
2023-02-02 19:02:03 +02:00
|
|
|
|
RecursiveFlag recursive)
|
2018-09-24 14:53:44 +03:00
|
|
|
|
{
|
2023-09-03 00:35:16 +03:00
|
|
|
|
#ifndef _WIN32 // TODO need graceful async exit support on Windows?
|
Recursive Nix support
This allows Nix builders to call Nix to build derivations, with some
limitations.
Example:
let nixpkgs = fetchTarball channel:nixos-18.03; in
with import <nixpkgs> {};
runCommand "foo"
{
buildInputs = [ nix jq ];
NIX_PATH = "nixpkgs=${nixpkgs}";
}
''
hello=$(nix-build -E '(import <nixpkgs> {}).hello.overrideDerivation (args: { name = "hello-3.5"; })')
$hello/bin/hello
mkdir -p $out/bin
ln -s $hello/bin/hello $out/bin/hello
nix path-info -r --json $hello | jq .
''
This derivation makes a recursive Nix call to build GNU Hello and
symlinks it from its $out, i.e.
# ll ./result/bin/
lrwxrwxrwx 1 root root 63 Jan 1 1970 hello -> /nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5/bin/hello
# nix-store -qR ./result
/nix/store/hwwqshlmazzjzj7yhrkyjydxamvvkfd3-glibc-2.26-131
/nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5
/nix/store/sgmvvyw8vhfqdqb619bxkcpfn9lvd8ss-foo
This is implemented as follows:
* Before running the outer builder, Nix creates a Unix domain socket
'.nix-socket' in the builder's temporary directory and sets
$NIX_REMOTE to point to it. It starts a thread to process
connections to this socket. (Thus you don't need to have nix-daemon
running.)
* The daemon thread uses a wrapper store (RestrictedStore) to keep
track of paths added through recursive Nix calls, to implement some
restrictions (see below), and to do some censorship (e.g. for
purity, queryPathInfo() won't return impure information such as
signatures and timestamps).
* After the build finishes, the output paths are scanned for
references to the paths added through recursive Nix calls (in
addition to the inputs closure). Thus, in the example above, $out
has a reference to $hello.
The main restriction on recursive Nix calls is that they cannot do
arbitrary substitutions. For example, doing
nix-store -r /nix/store/kmwd1hq55akdb9sc7l3finr175dajlby-hello-2.10
is forbidden unless /nix/store/kmwd... is in the inputs closure or
previously built by a recursive Nix call. This is to prevent
irreproducible derivations that have hidden dependencies on
substituters or the current store contents. Building a derivation is
fine, however, and Nix will use substitutes if available. In other
words, the builder has to present proof that it knows how to build a
desired store path from scratch by constructing a derivation graph for
that path.
Probably we should also disallow instantiating/building fixed-output
derivations (specifically, those that access the network, but
currently we have no way to mark fixed-output derivations that don't
access the network). Otherwise sandboxed derivations can bypass
sandbox restrictions and access the network.
When sandboxing is enabled, we make paths appear in the sandbox of the
builder by entering the mount namespace of the builder and
bind-mounting each path. This is tricky because we do a pivot_root()
in the builder to change the root directory of its mount namespace,
and thus the host /nix/store is not visible in the mount namespace of
the builder. To get around this, just before doing pivot_root(), we
branch a second mount namespace that shares its /nix/store mountpoint
with the parent.
Recursive Nix currently doesn't work on macOS in sandboxed mode
(because we can't change the sandbox policy of a running build) and in
non-root mode (because setns() barfs).
2018-10-02 17:01:26 +03:00
|
|
|
|
auto monitor = !recursive ? std::make_unique<MonitorFdHup>(from.fd) : nullptr;
|
2023-09-03 00:35:16 +03:00
|
|
|
|
#endif
|
2018-09-24 14:53:44 +03:00
|
|
|
|
|
|
|
|
|
/* Exchange the greeting. */
|
|
|
|
|
unsigned int magic = readInt(from);
|
|
|
|
|
if (magic != WORKER_MAGIC_1) throw Error("protocol mismatch");
|
|
|
|
|
to << WORKER_MAGIC_2 << PROTOCOL_VERSION;
|
|
|
|
|
to.flush();
|
2023-10-17 18:15:36 +03:00
|
|
|
|
WorkerProto::Version clientVersion = readInt(from);
|
2018-09-24 14:53:44 +03:00
|
|
|
|
|
|
|
|
|
if (clientVersion < 0x10a)
|
|
|
|
|
throw Error("the Nix client version is too old");
|
|
|
|
|
|
|
|
|
|
auto tunnelLogger = new TunnelLogger(to, clientVersion);
|
|
|
|
|
auto prevLogger = nix::logger;
|
Recursive Nix support
This allows Nix builders to call Nix to build derivations, with some
limitations.
Example:
let nixpkgs = fetchTarball channel:nixos-18.03; in
with import <nixpkgs> {};
runCommand "foo"
{
buildInputs = [ nix jq ];
NIX_PATH = "nixpkgs=${nixpkgs}";
}
''
hello=$(nix-build -E '(import <nixpkgs> {}).hello.overrideDerivation (args: { name = "hello-3.5"; })')
$hello/bin/hello
mkdir -p $out/bin
ln -s $hello/bin/hello $out/bin/hello
nix path-info -r --json $hello | jq .
''
This derivation makes a recursive Nix call to build GNU Hello and
symlinks it from its $out, i.e.
# ll ./result/bin/
lrwxrwxrwx 1 root root 63 Jan 1 1970 hello -> /nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5/bin/hello
# nix-store -qR ./result
/nix/store/hwwqshlmazzjzj7yhrkyjydxamvvkfd3-glibc-2.26-131
/nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5
/nix/store/sgmvvyw8vhfqdqb619bxkcpfn9lvd8ss-foo
This is implemented as follows:
* Before running the outer builder, Nix creates a Unix domain socket
'.nix-socket' in the builder's temporary directory and sets
$NIX_REMOTE to point to it. It starts a thread to process
connections to this socket. (Thus you don't need to have nix-daemon
running.)
* The daemon thread uses a wrapper store (RestrictedStore) to keep
track of paths added through recursive Nix calls, to implement some
restrictions (see below), and to do some censorship (e.g. for
purity, queryPathInfo() won't return impure information such as
signatures and timestamps).
* After the build finishes, the output paths are scanned for
references to the paths added through recursive Nix calls (in
addition to the inputs closure). Thus, in the example above, $out
has a reference to $hello.
The main restriction on recursive Nix calls is that they cannot do
arbitrary substitutions. For example, doing
nix-store -r /nix/store/kmwd1hq55akdb9sc7l3finr175dajlby-hello-2.10
is forbidden unless /nix/store/kmwd... is in the inputs closure or
previously built by a recursive Nix call. This is to prevent
irreproducible derivations that have hidden dependencies on
substituters or the current store contents. Building a derivation is
fine, however, and Nix will use substitutes if available. In other
words, the builder has to present proof that it knows how to build a
desired store path from scratch by constructing a derivation graph for
that path.
Probably we should also disallow instantiating/building fixed-output
derivations (specifically, those that access the network, but
currently we have no way to mark fixed-output derivations that don't
access the network). Otherwise sandboxed derivations can bypass
sandbox restrictions and access the network.
When sandboxing is enabled, we make paths appear in the sandbox of the
builder by entering the mount namespace of the builder and
bind-mounting each path. This is tricky because we do a pivot_root()
in the builder to change the root directory of its mount namespace,
and thus the host /nix/store is not visible in the mount namespace of
the builder. To get around this, just before doing pivot_root(), we
branch a second mount namespace that shares its /nix/store mountpoint
with the parent.
Recursive Nix currently doesn't work on macOS in sandboxed mode
(because we can't change the sandbox policy of a running build) and in
non-root mode (because setns() barfs).
2018-10-02 17:01:26 +03:00
|
|
|
|
// FIXME
|
|
|
|
|
if (!recursive)
|
|
|
|
|
logger = tunnelLogger;
|
2018-09-24 14:53:44 +03:00
|
|
|
|
|
|
|
|
|
unsigned int opCount = 0;
|
|
|
|
|
|
|
|
|
|
Finally finally([&]() {
|
2024-04-04 19:25:01 +03:00
|
|
|
|
setInterrupted(false);
|
2021-11-30 22:23:13 +02:00
|
|
|
|
printMsgUsing(prevLogger, lvlDebug, "%d operations", opCount);
|
2018-09-24 14:53:44 +03:00
|
|
|
|
});
|
|
|
|
|
|
Recursive Nix support
This allows Nix builders to call Nix to build derivations, with some
limitations.
Example:
let nixpkgs = fetchTarball channel:nixos-18.03; in
with import <nixpkgs> {};
runCommand "foo"
{
buildInputs = [ nix jq ];
NIX_PATH = "nixpkgs=${nixpkgs}";
}
''
hello=$(nix-build -E '(import <nixpkgs> {}).hello.overrideDerivation (args: { name = "hello-3.5"; })')
$hello/bin/hello
mkdir -p $out/bin
ln -s $hello/bin/hello $out/bin/hello
nix path-info -r --json $hello | jq .
''
This derivation makes a recursive Nix call to build GNU Hello and
symlinks it from its $out, i.e.
# ll ./result/bin/
lrwxrwxrwx 1 root root 63 Jan 1 1970 hello -> /nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5/bin/hello
# nix-store -qR ./result
/nix/store/hwwqshlmazzjzj7yhrkyjydxamvvkfd3-glibc-2.26-131
/nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5
/nix/store/sgmvvyw8vhfqdqb619bxkcpfn9lvd8ss-foo
This is implemented as follows:
* Before running the outer builder, Nix creates a Unix domain socket
'.nix-socket' in the builder's temporary directory and sets
$NIX_REMOTE to point to it. It starts a thread to process
connections to this socket. (Thus you don't need to have nix-daemon
running.)
* The daemon thread uses a wrapper store (RestrictedStore) to keep
track of paths added through recursive Nix calls, to implement some
restrictions (see below), and to do some censorship (e.g. for
purity, queryPathInfo() won't return impure information such as
signatures and timestamps).
* After the build finishes, the output paths are scanned for
references to the paths added through recursive Nix calls (in
addition to the inputs closure). Thus, in the example above, $out
has a reference to $hello.
The main restriction on recursive Nix calls is that they cannot do
arbitrary substitutions. For example, doing
nix-store -r /nix/store/kmwd1hq55akdb9sc7l3finr175dajlby-hello-2.10
is forbidden unless /nix/store/kmwd... is in the inputs closure or
previously built by a recursive Nix call. This is to prevent
irreproducible derivations that have hidden dependencies on
substituters or the current store contents. Building a derivation is
fine, however, and Nix will use substitutes if available. In other
words, the builder has to present proof that it knows how to build a
desired store path from scratch by constructing a derivation graph for
that path.
Probably we should also disallow instantiating/building fixed-output
derivations (specifically, those that access the network, but
currently we have no way to mark fixed-output derivations that don't
access the network). Otherwise sandboxed derivations can bypass
sandbox restrictions and access the network.
When sandboxing is enabled, we make paths appear in the sandbox of the
builder by entering the mount namespace of the builder and
bind-mounting each path. This is tricky because we do a pivot_root()
in the builder to change the root directory of its mount namespace,
and thus the host /nix/store is not visible in the mount namespace of
the builder. To get around this, just before doing pivot_root(), we
branch a second mount namespace that shares its /nix/store mountpoint
with the parent.
Recursive Nix currently doesn't work on macOS in sandboxed mode
(because we can't change the sandbox policy of a running build) and in
non-root mode (because setns() barfs).
2018-10-02 17:01:26 +03:00
|
|
|
|
if (GET_PROTOCOL_MINOR(clientVersion) >= 14 && readInt(from)) {
|
2021-12-22 16:49:51 +02:00
|
|
|
|
// Obsolete CPU affinity.
|
|
|
|
|
readInt(from);
|
Recursive Nix support
This allows Nix builders to call Nix to build derivations, with some
limitations.
Example:
let nixpkgs = fetchTarball channel:nixos-18.03; in
with import <nixpkgs> {};
runCommand "foo"
{
buildInputs = [ nix jq ];
NIX_PATH = "nixpkgs=${nixpkgs}";
}
''
hello=$(nix-build -E '(import <nixpkgs> {}).hello.overrideDerivation (args: { name = "hello-3.5"; })')
$hello/bin/hello
mkdir -p $out/bin
ln -s $hello/bin/hello $out/bin/hello
nix path-info -r --json $hello | jq .
''
This derivation makes a recursive Nix call to build GNU Hello and
symlinks it from its $out, i.e.
# ll ./result/bin/
lrwxrwxrwx 1 root root 63 Jan 1 1970 hello -> /nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5/bin/hello
# nix-store -qR ./result
/nix/store/hwwqshlmazzjzj7yhrkyjydxamvvkfd3-glibc-2.26-131
/nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5
/nix/store/sgmvvyw8vhfqdqb619bxkcpfn9lvd8ss-foo
This is implemented as follows:
* Before running the outer builder, Nix creates a Unix domain socket
'.nix-socket' in the builder's temporary directory and sets
$NIX_REMOTE to point to it. It starts a thread to process
connections to this socket. (Thus you don't need to have nix-daemon
running.)
* The daemon thread uses a wrapper store (RestrictedStore) to keep
track of paths added through recursive Nix calls, to implement some
restrictions (see below), and to do some censorship (e.g. for
purity, queryPathInfo() won't return impure information such as
signatures and timestamps).
* After the build finishes, the output paths are scanned for
references to the paths added through recursive Nix calls (in
addition to the inputs closure). Thus, in the example above, $out
has a reference to $hello.
The main restriction on recursive Nix calls is that they cannot do
arbitrary substitutions. For example, doing
nix-store -r /nix/store/kmwd1hq55akdb9sc7l3finr175dajlby-hello-2.10
is forbidden unless /nix/store/kmwd... is in the inputs closure or
previously built by a recursive Nix call. This is to prevent
irreproducible derivations that have hidden dependencies on
substituters or the current store contents. Building a derivation is
fine, however, and Nix will use substitutes if available. In other
words, the builder has to present proof that it knows how to build a
desired store path from scratch by constructing a derivation graph for
that path.
Probably we should also disallow instantiating/building fixed-output
derivations (specifically, those that access the network, but
currently we have no way to mark fixed-output derivations that don't
access the network). Otherwise sandboxed derivations can bypass
sandbox restrictions and access the network.
When sandboxing is enabled, we make paths appear in the sandbox of the
builder by entering the mount namespace of the builder and
bind-mounting each path. This is tricky because we do a pivot_root()
in the builder to change the root directory of its mount namespace,
and thus the host /nix/store is not visible in the mount namespace of
the builder. To get around this, just before doing pivot_root(), we
branch a second mount namespace that shares its /nix/store mountpoint
with the parent.
Recursive Nix currently doesn't work on macOS in sandboxed mode
(because we can't change the sandbox policy of a running build) and in
non-root mode (because setns() barfs).
2018-10-02 17:01:26 +03:00
|
|
|
|
}
|
2018-09-24 14:53:44 +03:00
|
|
|
|
|
2022-01-25 22:14:27 +02:00
|
|
|
|
if (GET_PROTOCOL_MINOR(clientVersion) >= 11)
|
|
|
|
|
readInt(from); // obsolete reserveSpace
|
|
|
|
|
|
|
|
|
|
if (GET_PROTOCOL_MINOR(clientVersion) >= 33)
|
|
|
|
|
to << nixVersion;
|
2018-09-24 14:53:44 +03:00
|
|
|
|
|
2022-12-26 22:21:08 +02:00
|
|
|
|
if (GET_PROTOCOL_MINOR(clientVersion) >= 35) {
|
|
|
|
|
// We and the underlying store both need to trust the client for
|
|
|
|
|
// it to be trusted.
|
|
|
|
|
auto temp = trusted
|
|
|
|
|
? store->isTrustedClient()
|
|
|
|
|
: std::optional { NotTrusted };
|
2022-03-25 06:40:49 +02:00
|
|
|
|
WorkerProto::WriteConn wconn {
|
|
|
|
|
.to = to,
|
|
|
|
|
.version = clientVersion,
|
|
|
|
|
};
|
2023-04-17 20:40:46 +03:00
|
|
|
|
WorkerProto::write(*store, wconn, temp);
|
2022-12-26 22:21:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-09-24 14:53:44 +03:00
|
|
|
|
/* Send startup error messages to the client. */
|
|
|
|
|
tunnelLogger->startWork();
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
|
|
tunnelLogger->stopWork();
|
|
|
|
|
to.flush();
|
|
|
|
|
|
|
|
|
|
/* Process client requests. */
|
|
|
|
|
while (true) {
|
2023-05-26 18:07:25 +03:00
|
|
|
|
WorkerProto::Op op;
|
2018-09-24 14:53:44 +03:00
|
|
|
|
try {
|
2023-05-26 18:07:25 +03:00
|
|
|
|
op = (enum WorkerProto::Op) readInt(from);
|
2018-09-24 14:53:44 +03:00
|
|
|
|
} catch (Interrupted & e) {
|
|
|
|
|
break;
|
|
|
|
|
} catch (EndOfFile & e) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-30 22:23:13 +02:00
|
|
|
|
printMsgUsing(prevLogger, lvlDebug, "received daemon op %d", op);
|
|
|
|
|
|
2018-09-24 14:53:44 +03:00
|
|
|
|
opCount++;
|
|
|
|
|
|
2023-04-17 16:41:39 +03:00
|
|
|
|
debug("performing daemon worker op: %d", op);
|
|
|
|
|
|
2018-09-24 14:53:44 +03:00
|
|
|
|
try {
|
Recursive Nix support
This allows Nix builders to call Nix to build derivations, with some
limitations.
Example:
let nixpkgs = fetchTarball channel:nixos-18.03; in
with import <nixpkgs> {};
runCommand "foo"
{
buildInputs = [ nix jq ];
NIX_PATH = "nixpkgs=${nixpkgs}";
}
''
hello=$(nix-build -E '(import <nixpkgs> {}).hello.overrideDerivation (args: { name = "hello-3.5"; })')
$hello/bin/hello
mkdir -p $out/bin
ln -s $hello/bin/hello $out/bin/hello
nix path-info -r --json $hello | jq .
''
This derivation makes a recursive Nix call to build GNU Hello and
symlinks it from its $out, i.e.
# ll ./result/bin/
lrwxrwxrwx 1 root root 63 Jan 1 1970 hello -> /nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5/bin/hello
# nix-store -qR ./result
/nix/store/hwwqshlmazzjzj7yhrkyjydxamvvkfd3-glibc-2.26-131
/nix/store/s0awxrs71gickhaqdwxl506hzccb30y5-hello-3.5
/nix/store/sgmvvyw8vhfqdqb619bxkcpfn9lvd8ss-foo
This is implemented as follows:
* Before running the outer builder, Nix creates a Unix domain socket
'.nix-socket' in the builder's temporary directory and sets
$NIX_REMOTE to point to it. It starts a thread to process
connections to this socket. (Thus you don't need to have nix-daemon
running.)
* The daemon thread uses a wrapper store (RestrictedStore) to keep
track of paths added through recursive Nix calls, to implement some
restrictions (see below), and to do some censorship (e.g. for
purity, queryPathInfo() won't return impure information such as
signatures and timestamps).
* After the build finishes, the output paths are scanned for
references to the paths added through recursive Nix calls (in
addition to the inputs closure). Thus, in the example above, $out
has a reference to $hello.
The main restriction on recursive Nix calls is that they cannot do
arbitrary substitutions. For example, doing
nix-store -r /nix/store/kmwd1hq55akdb9sc7l3finr175dajlby-hello-2.10
is forbidden unless /nix/store/kmwd... is in the inputs closure or
previously built by a recursive Nix call. This is to prevent
irreproducible derivations that have hidden dependencies on
substituters or the current store contents. Building a derivation is
fine, however, and Nix will use substitutes if available. In other
words, the builder has to present proof that it knows how to build a
desired store path from scratch by constructing a derivation graph for
that path.
Probably we should also disallow instantiating/building fixed-output
derivations (specifically, those that access the network, but
currently we have no way to mark fixed-output derivations that don't
access the network). Otherwise sandboxed derivations can bypass
sandbox restrictions and access the network.
When sandboxing is enabled, we make paths appear in the sandbox of the
builder by entering the mount namespace of the builder and
bind-mounting each path. This is tricky because we do a pivot_root()
in the builder to change the root directory of its mount namespace,
and thus the host /nix/store is not visible in the mount namespace of
the builder. To get around this, just before doing pivot_root(), we
branch a second mount namespace that shares its /nix/store mountpoint
with the parent.
Recursive Nix currently doesn't work on macOS in sandboxed mode
(because we can't change the sandbox policy of a running build) and in
non-root mode (because setns() barfs).
2018-10-02 17:01:26 +03:00
|
|
|
|
performOp(tunnelLogger, store, trusted, recursive, clientVersion, from, to, op);
|
2018-09-24 14:53:44 +03:00
|
|
|
|
} catch (Error & e) {
|
|
|
|
|
/* If we're not in a state where we can send replies, then
|
|
|
|
|
something went wrong processing the input of the
|
|
|
|
|
client. This can happen especially if I/O errors occur
|
|
|
|
|
during addTextToStore() / importPath(). If that
|
|
|
|
|
happens, just send the error message and exit. */
|
|
|
|
|
bool errorAllowed = tunnelLogger->state_.lock()->canSendStderr;
|
2020-10-07 18:13:54 +03:00
|
|
|
|
tunnelLogger->stopWork(&e);
|
2018-09-24 14:53:44 +03:00
|
|
|
|
if (!errorAllowed) throw;
|
|
|
|
|
} catch (std::bad_alloc & e) {
|
2020-10-07 18:13:54 +03:00
|
|
|
|
auto ex = Error("Nix daemon out of memory");
|
|
|
|
|
tunnelLogger->stopWork(&ex);
|
2018-09-24 14:53:44 +03:00
|
|
|
|
throw;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
to.flush();
|
|
|
|
|
|
|
|
|
|
assert(!tunnelLogger->state_.lock()->canSendStderr);
|
|
|
|
|
};
|
|
|
|
|
|
2020-10-07 18:13:54 +03:00
|
|
|
|
} catch (Error & e) {
|
|
|
|
|
tunnelLogger->stopWork(&e);
|
|
|
|
|
to.flush();
|
|
|
|
|
return;
|
2018-09-24 14:53:44 +03:00
|
|
|
|
} catch (std::exception & e) {
|
2020-10-07 18:13:54 +03:00
|
|
|
|
auto ex = Error(e.what());
|
|
|
|
|
tunnelLogger->stopWork(&ex);
|
2018-09-24 14:53:44 +03:00
|
|
|
|
to.flush();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|