1
0
Fork 0
mirror of https://github.com/NixOS/nix synced 2025-07-08 02:43:54 +02:00

Add Store::isTrustedClient()

This function returns true or false depending on whether the Nix client
is trusted or not. Mostly relevant when speaking to a remote store with
a daemon.

We include this information in `nix ping store` and `nix doctor`

Co-Authored-By: John Ericson <John.Ericson@Obsidian.Systems>
This commit is contained in:
matthewcroughan 2022-12-26 20:21:08 +00:00 committed by John Ericson
parent 9185639631
commit 9207f94582
21 changed files with 169 additions and 3 deletions

View file

@ -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();

View file

@ -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();

View file

@ -6,7 +6,6 @@
namespace nix::daemon {
enum TrustedFlag : bool { NotTrusted = false, Trusted = true };
enum RecursiveFlag : bool { NotRecursive = false, Recursive = true };
void processConnection(

View file

@ -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"};
}

View file

@ -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;

View file

@ -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

View file

@ -95,6 +95,10 @@ protected:
return paths;
}
std::optional<TrustedFlag> isTrustedClient() override
{
return Trusted;
}
};
void LocalBinaryCacheStore::init()

View file

@ -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)

View file

@ -204,6 +204,8 @@ public:
unsigned int getProtocol() override;
std::optional<TrustedFlag> isTrustedClient() override;
void vacuumDB();
void repairPath(const StorePath & path) override;

View file

@ -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()
{

View file

@ -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;

View file

@ -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"}; }
};

View file

@ -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;

View file

@ -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>);

View file

@ -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");

View file

@ -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;
}
}
};