aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/manual/src/release-notes/rl-next.md6
-rw-r--r--src/libstore/build/local-derivation-goal.cc5
-rw-r--r--src/libstore/daemon.cc9
-rw-r--r--src/libstore/daemon.hh1
-rw-r--r--src/libstore/dummy-store.cc8
-rw-r--r--src/libstore/http-binary-cache-store.cc12
-rw-r--r--src/libstore/legacy-ssh-store.cc9
-rw-r--r--src/libstore/local-binary-cache-store.cc4
-rw-r--r--src/libstore/local-store.cc5
-rw-r--r--src/libstore/local-store.hh2
-rw-r--r--src/libstore/remote-store.cc46
-rw-r--r--src/libstore/remote-store.hh3
-rw-r--r--src/libstore/s3-binary-cache-store.cc10
-rw-r--r--src/libstore/store-api.hh12
-rw-r--r--src/libstore/worker-protocol.hh3
-rw-r--r--src/nix/doctor.cc13
-rw-r--r--src/nix/ping-store.cc5
-rw-r--r--tests/legacy-ssh-store.sh4
-rw-r--r--tests/local-store.sh3
-rw-r--r--tests/local.mk1
-rw-r--r--tests/remote-store.sh11
21 files changed, 169 insertions, 3 deletions
diff --git a/doc/manual/src/release-notes/rl-next.md b/doc/manual/src/release-notes/rl-next.md
index ae159de8f..af1850bdc 100644
--- a/doc/manual/src/release-notes/rl-next.md
+++ b/doc/manual/src/release-notes/rl-next.md
@@ -39,3 +39,9 @@
* `man nix-store-<operation>` and `man nix-env-<operation>`
* `nix-store --help --<operation>` and `nix-env --help --<operation>`.
+
+* Nix when used as a client now checks whether the store (the server) trusts the client.
+ (The store always had to check whether it trusts the client, but now the client is informed of the store's decision.)
+ This is useful for scripting interactions with (non-legacy-ssh) remote Nix stores.
+
+ `nix store ping` and `nix doctor` now display this information.
diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc
index e22180670..58d6901d3 100644
--- a/src/libstore/build/local-derivation-goal.cc
+++ b/src/libstore/build/local-derivation-goal.cc
@@ -1415,6 +1415,9 @@ struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual Lo
virtual void addBuildLog(const StorePath & path, std::string_view log) override
{ unsupported("addBuildLog"); }
+
+ std::optional<TrustedFlag> isTrustedClient() override
+ { return NotTrusted; }
};
@@ -1467,7 +1470,7 @@ void LocalDerivationGoal::startDaemon()
FdSink to(remote.get());
try {
daemon::processConnection(store, from, to,
- daemon::NotTrusted, daemon::Recursive);
+ NotTrusted, daemon::Recursive);
debug("terminated daemon connection");
} catch (SysError &) {
ignoreException();
diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc
index 656ad4587..63898f8dc 100644
--- a/src/libstore/daemon.cc
+++ b/src/libstore/daemon.cc
@@ -1032,6 +1032,15 @@ void processConnection(
if (GET_PROTOCOL_MINOR(clientVersion) >= 33)
to << nixVersion;
+ 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 };
+ worker_proto::write(*store, to, temp);
+ }
+
/* Send startup error messages to the client. */
tunnelLogger->startWork();
diff --git a/src/libstore/daemon.hh b/src/libstore/daemon.hh
index 67340a05b..1964c0d99 100644
--- a/src/libstore/daemon.hh
+++ b/src/libstore/daemon.hh
@@ -6,7 +6,6 @@
namespace nix::daemon {
-enum TrustedFlag : bool { NotTrusted = false, Trusted = true };
enum RecursiveFlag : bool { NotRecursive = false, Recursive = true };
void processConnection(
diff --git a/src/libstore/dummy-store.cc b/src/libstore/dummy-store.cc
index 16e5fafd7..ae2777d0c 100644
--- a/src/libstore/dummy-store.cc
+++ b/src/libstore/dummy-store.cc
@@ -39,6 +39,14 @@ struct DummyStore : public virtual DummyStoreConfig, public virtual Store
callback(nullptr);
}
+ /**
+ * The dummy store is incapable of *not* trusting! :)
+ */
+ virtual std::optional<TrustedFlag> isTrustedClient() override
+ {
+ return Trusted;
+ }
+
static std::set<std::string> uriSchemes() {
return {"dummy"};
}
diff --git a/src/libstore/http-binary-cache-store.cc b/src/libstore/http-binary-cache-store.cc
index 238fd1d98..85c5eed4c 100644
--- a/src/libstore/http-binary-cache-store.cc
+++ b/src/libstore/http-binary-cache-store.cc
@@ -194,6 +194,18 @@ protected:
}});
}
+ /**
+ * This isn't actually necessary read only. We support "upsert" now, so we
+ * have a notion of authentication via HTTP POST/PUT.
+ *
+ * For now, we conservatively say we don't know.
+ *
+ * \todo try to expose our HTTP authentication status.
+ */
+ std::optional<TrustedFlag> isTrustedClient() override
+ {
+ return std::nullopt;
+ }
};
static RegisterStoreImplementation<HttpBinaryCacheStore, HttpBinaryCacheStoreConfig> regHttpBinaryCacheStore;
diff --git a/src/libstore/legacy-ssh-store.cc b/src/libstore/legacy-ssh-store.cc
index 98322b045..eb471d8fc 100644
--- a/src/libstore/legacy-ssh-store.cc
+++ b/src/libstore/legacy-ssh-store.cc
@@ -389,6 +389,15 @@ public:
return conn->remoteVersion;
}
+ /**
+ * The legacy ssh protocol doesn't support checking for trusted-user.
+ * Try using ssh-ng:// instead if you want to know.
+ */
+ std::optional<TrustedFlag> isTrustedClient() override
+ {
+ return std::nullopt;
+ }
+
void queryRealisationUncached(const DrvOutput &,
Callback<std::shared_ptr<const Realisation>> callback) noexcept override
// TODO: Implement
diff --git a/src/libstore/local-binary-cache-store.cc b/src/libstore/local-binary-cache-store.cc
index e5ee6fc15..5481dd762 100644
--- a/src/libstore/local-binary-cache-store.cc
+++ b/src/libstore/local-binary-cache-store.cc
@@ -95,6 +95,10 @@ protected:
return paths;
}
+ std::optional<TrustedFlag> isTrustedClient() override
+ {
+ return Trusted;
+ }
};
void LocalBinaryCacheStore::init()
diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc
index f58d90895..e0ad50f6d 100644
--- a/src/libstore/local-store.cc
+++ b/src/libstore/local-store.cc
@@ -1685,6 +1685,11 @@ unsigned int LocalStore::getProtocol()
return PROTOCOL_VERSION;
}
+std::optional<TrustedFlag> LocalStore::isTrustedClient()
+{
+ return Trusted;
+}
+
#if defined(FS_IOC_SETFLAGS) && defined(FS_IOC_GETFLAGS) && defined(FS_IMMUTABLE_FL)
diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh
index 1b668b6fd..6deaa051f 100644
--- a/src/libstore/local-store.hh
+++ b/src/libstore/local-store.hh
@@ -204,6 +204,8 @@ public:
unsigned int getProtocol() override;
+ std::optional<TrustedFlag> isTrustedClient() override;
+
void vacuumDB();
void repairPath(const StorePath & path) override;
diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc
index d24d83117..e128c3a29 100644
--- a/src/libstore/remote-store.cc
+++ b/src/libstore/remote-store.cc
@@ -42,6 +42,40 @@ void write(const Store & store, Sink & out, const StorePath & storePath)
}
+std::optional<TrustedFlag> read(const Store & store, Source & from, Phantom<std::optional<TrustedFlag>> _)
+{
+ auto temp = readNum<uint8_t>(from);
+ switch (temp) {
+ case 0:
+ return std::nullopt;
+ case 1:
+ return { Trusted };
+ case 2:
+ return { NotTrusted };
+ default:
+ throw Error("Invalid trusted status from remote");
+ }
+}
+
+void write(const Store & store, Sink & out, const std::optional<TrustedFlag> & optTrusted)
+{
+ if (!optTrusted)
+ out << (uint8_t)0;
+ else {
+ switch (*optTrusted) {
+ case Trusted:
+ out << (uint8_t)1;
+ break;
+ case NotTrusted:
+ out << (uint8_t)2;
+ break;
+ default:
+ assert(false);
+ };
+ }
+}
+
+
ContentAddress read(const Store & store, Source & from, Phantom<ContentAddress> _)
{
return parseContentAddress(readString(from));
@@ -226,6 +260,13 @@ void RemoteStore::initConnection(Connection & conn)
conn.daemonNixVersion = readString(conn.from);
}
+ if (GET_PROTOCOL_MINOR(conn.daemonVersion) >= 35) {
+ conn.remoteTrustsUs = worker_proto::read(*this, conn.from, Phantom<std::optional<TrustedFlag>> {});
+ } else {
+ // We don't know the answer; protocol to old.
+ conn.remoteTrustsUs = std::nullopt;
+ }
+
auto ex = conn.processStderr();
if (ex) std::rethrow_exception(ex);
}
@@ -1082,6 +1123,11 @@ unsigned int RemoteStore::getProtocol()
return conn->daemonVersion;
}
+std::optional<TrustedFlag> RemoteStore::isTrustedClient()
+{
+ auto conn(getConnection());
+ return conn->remoteTrustsUs;
+}
void RemoteStore::flushBadConnections()
{
diff --git a/src/libstore/remote-store.hh b/src/libstore/remote-store.hh
index f5f45f853..f220dbc20 100644
--- a/src/libstore/remote-store.hh
+++ b/src/libstore/remote-store.hh
@@ -144,6 +144,8 @@ public:
unsigned int getProtocol() override;
+ std::optional<TrustedFlag> isTrustedClient() override;
+
void flushBadConnections();
struct Connection
@@ -151,6 +153,7 @@ public:
FdSink to;
FdSource from;
unsigned int daemonVersion;
+ std::optional<TrustedFlag> remoteTrustsUs;
std::optional<std::string> daemonNixVersion;
std::chrono::time_point<std::chrono::steady_clock> startTime;
diff --git a/src/libstore/s3-binary-cache-store.cc b/src/libstore/s3-binary-cache-store.cc
index ac82147ee..d2fc6abaf 100644
--- a/src/libstore/s3-binary-cache-store.cc
+++ b/src/libstore/s3-binary-cache-store.cc
@@ -509,6 +509,16 @@ struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStoreConfig, public virtual
return paths;
}
+ /**
+ * For now, we conservatively say we don't know.
+ *
+ * \todo try to expose our S3 authentication status.
+ */
+ std::optional<TrustedFlag> isTrustedClient() override
+ {
+ return std::nullopt;
+ }
+
static std::set<std::string> uriSchemes() { return {"s3"}; }
};
diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh
index 4d1047380..9c6e80486 100644
--- a/src/libstore/store-api.hh
+++ b/src/libstore/store-api.hh
@@ -89,6 +89,7 @@ const uint32_t exportMagic = 0x4558494e;
enum BuildMode { bmNormal, bmRepair, bmCheck };
+enum TrustedFlag : bool { NotTrusted = false, Trusted = true };
struct BuildResult;
@@ -815,6 +816,17 @@ public:
return 0;
};
+ /**
+ * @return/ whether store trusts *us*.
+ *
+ * `std::nullopt` means we do not know.
+ *
+ * @note This is the opposite of the StoreConfig::isTrusted
+ * store setting. That is about whether *we* trust the store.
+ */
+ virtual std::optional<TrustedFlag> isTrustedClient() = 0;
+
+
virtual Path toRealPath(const Path & storePath)
{
return storePath;
diff --git a/src/libstore/worker-protocol.hh b/src/libstore/worker-protocol.hh
index 697dd2b8c..c7a6f8688 100644
--- a/src/libstore/worker-protocol.hh
+++ b/src/libstore/worker-protocol.hh
@@ -10,7 +10,7 @@ namespace nix {
#define WORKER_MAGIC_1 0x6e697863
#define WORKER_MAGIC_2 0x6478696f
-#define PROTOCOL_VERSION (1 << 8 | 34)
+#define PROTOCOL_VERSION (1 << 8 | 35)
#define GET_PROTOCOL_MAJOR(x) ((x) & 0xff00)
#define GET_PROTOCOL_MINOR(x) ((x) & 0x00ff)
@@ -103,6 +103,7 @@ MAKE_WORKER_PROTO(, DerivedPath);
MAKE_WORKER_PROTO(, Realisation);
MAKE_WORKER_PROTO(, DrvOutput);
MAKE_WORKER_PROTO(, BuildResult);
+MAKE_WORKER_PROTO(, std::optional<TrustedFlag>);
MAKE_WORKER_PROTO(template<typename T>, std::vector<T>);
MAKE_WORKER_PROTO(template<typename T>, std::set<T>);
diff --git a/src/nix/doctor.cc b/src/nix/doctor.cc
index 354b03cf6..1aa6831d3 100644
--- a/src/nix/doctor.cc
+++ b/src/nix/doctor.cc
@@ -33,6 +33,10 @@ bool checkFail(const std::string & msg) {
return false;
}
+void checkInfo(const std::string & msg) {
+ notice(ANSI_BLUE "[INFO] " ANSI_NORMAL + msg);
+}
+
}
struct CmdDoctor : StoreCommand
@@ -63,6 +67,7 @@ struct CmdDoctor : StoreCommand
success &= checkProfileRoots(store);
}
success &= checkStoreProtocol(store->getProtocol());
+ checkTrustedUser(store);
if (!success)
throw Exit(2);
@@ -138,6 +143,14 @@ struct CmdDoctor : StoreCommand
return checkPass("Client protocol matches store protocol.");
}
+
+ void checkTrustedUser(ref<Store> store)
+ {
+ std::string_view trusted = store->isTrustedClient()
+ ? "trusted"
+ : "not trusted";
+ checkInfo(fmt("You are %s by store uri: %s", trusted, store->getUri()));
+ }
};
static auto rCmdDoctor = registerCommand<CmdDoctor>("doctor");
diff --git a/src/nix/ping-store.cc b/src/nix/ping-store.cc
index 5c44510ab..ec450e8e0 100644
--- a/src/nix/ping-store.cc
+++ b/src/nix/ping-store.cc
@@ -28,15 +28,20 @@ struct CmdPingStore : StoreCommand, MixJSON
store->connect();
if (auto version = store->getVersion())
notice("Version: %s", *version);
+ if (auto trusted = store->isTrustedClient())
+ notice("Trusted: %s", *trusted);
} else {
nlohmann::json res;
Finally printRes([&]() {
logger->cout("%s", res);
});
+
res["url"] = store->getUri();
store->connect();
if (auto version = store->getVersion())
res["version"] = *version;
+ if (auto trusted = store->isTrustedClient())
+ res["trusted"] = *trusted;
}
}
};
diff --git a/tests/legacy-ssh-store.sh b/tests/legacy-ssh-store.sh
new file mode 100644
index 000000000..71b716b84
--- /dev/null
+++ b/tests/legacy-ssh-store.sh
@@ -0,0 +1,4 @@
+source common.sh
+
+# Check that store ping trusted doesn't yet work with ssh://
+nix --store ssh://localhost?remote-store=$TEST_ROOT/other-store store ping --json | jq -e 'has("trusted") | not'
diff --git a/tests/local-store.sh b/tests/local-store.sh
index 0247346f1..89502f864 100644
--- a/tests/local-store.sh
+++ b/tests/local-store.sh
@@ -17,3 +17,6 @@ PATH2=$(nix path-info --store "$PWD/x" $CORRECT_PATH)
PATH3=$(nix path-info --store "local?root=$PWD/x" $CORRECT_PATH)
[ $CORRECT_PATH == $PATH3 ]
+
+# Ensure store ping trusted works with local store
+nix --store ./x store ping --json | jq -e '.trusted'
diff --git a/tests/local.mk b/tests/local.mk
index ccd76eeac..7d54d94e5 100644
--- a/tests/local.mk
+++ b/tests/local.mk
@@ -17,6 +17,7 @@ nix_tests = \
ca/gc.sh \
gc.sh \
remote-store.sh \
+ legacy-ssh-store.sh \
lang.sh \
experimental-features.sh \
fetchMercurial.sh \
diff --git a/tests/remote-store.sh b/tests/remote-store.sh
index 1ae126794..ea32a20d3 100644
--- a/tests/remote-store.sh
+++ b/tests/remote-store.sh
@@ -5,8 +5,19 @@ clearStore
# Ensure "fake ssh" remote store works just as legacy fake ssh would.
nix --store ssh-ng://localhost?remote-store=$TEST_ROOT/other-store doctor
+# Ensure that store ping trusted works with ssh-ng://
+nix --store ssh-ng://localhost?remote-store=$TEST_ROOT/other-store store ping --json | jq -e '.trusted'
+
startDaemon
+if isDaemonNewer "2.15pre0"; then
+ # Ensure that ping works trusted with new daemon
+ nix store ping --json | jq -e '.trusted'
+else
+ # And the the field is absent with the old daemon
+ nix store ping --json | jq -e 'has("trusted") | not'
+fi
+
# Test import-from-derivation through the daemon.
[[ $(nix eval --impure --raw --expr '
with import ./config.nix;