2023-03-22 15:23:36 +02:00
|
|
|
#include "ssh-store-config.hh"
|
2016-09-12 15:03:29 +03:00
|
|
|
#include "store-api.hh"
|
2023-03-23 16:06:45 +02:00
|
|
|
#include "local-fs-store.hh"
|
2016-09-12 15:03:29 +03:00
|
|
|
#include "remote-store.hh"
|
2023-04-17 20:40:46 +03:00
|
|
|
#include "remote-store-connection.hh"
|
2023-04-15 13:02:41 +03:00
|
|
|
#include "source-accessor.hh"
|
2016-09-02 21:31:38 +03:00
|
|
|
#include "archive.hh"
|
|
|
|
#include "worker-protocol.hh"
|
2023-04-15 13:02:41 +03:00
|
|
|
#include "worker-protocol-impl.hh"
|
2016-09-02 21:31:38 +03:00
|
|
|
#include "pool.hh"
|
2017-03-03 20:05:50 +02:00
|
|
|
#include "ssh.hh"
|
2016-09-02 21:31:38 +03:00
|
|
|
|
|
|
|
namespace nix {
|
|
|
|
|
2023-03-22 15:23:36 +02:00
|
|
|
struct SSHStoreConfig : virtual RemoteStoreConfig, virtual CommonSSHStoreConfig
|
2016-09-12 15:03:29 +03:00
|
|
|
{
|
2020-09-10 11:55:51 +03:00
|
|
|
using RemoteStoreConfig::RemoteStoreConfig;
|
2023-03-23 10:11:15 +02:00
|
|
|
using CommonSSHStoreConfig::CommonSSHStoreConfig;
|
2020-09-11 12:06:18 +03:00
|
|
|
|
2024-01-22 22:50:00 +02:00
|
|
|
const Setting<Strings> remoteProgram{this, {"nix-daemon"}, "remote-program",
|
2023-03-23 10:35:35 +02:00
|
|
|
"Path to the `nix-daemon` executable on the remote machine."};
|
|
|
|
|
2023-03-21 15:03:05 +02:00
|
|
|
const std::string name() override { return "Experimental SSH Store"; }
|
|
|
|
|
|
|
|
std::string doc() override
|
|
|
|
{
|
|
|
|
return
|
|
|
|
#include "ssh-store.md"
|
|
|
|
;
|
|
|
|
}
|
2020-09-10 11:55:51 +03:00
|
|
|
};
|
2017-02-07 20:20:15 +02:00
|
|
|
|
2020-12-20 17:33:12 +02:00
|
|
|
class SSHStore : public virtual SSHStoreConfig, public virtual RemoteStore
|
2016-09-12 15:03:29 +03:00
|
|
|
{
|
2024-05-23 19:14:53 +03:00
|
|
|
public:
|
|
|
|
|
2024-01-25 17:31:52 +02:00
|
|
|
SSHStore(
|
|
|
|
std::string_view scheme,
|
2024-05-23 19:14:53 +03:00
|
|
|
std::string_view host,
|
2024-01-25 17:31:52 +02:00
|
|
|
const Params & params)
|
2020-09-11 12:06:18 +03:00
|
|
|
: StoreConfig(params)
|
2020-12-20 17:33:12 +02:00
|
|
|
, RemoteStoreConfig(params)
|
2024-05-23 19:14:53 +03:00
|
|
|
, CommonSSHStoreConfig(scheme, host, params)
|
2020-12-20 17:33:12 +02:00
|
|
|
, SSHStoreConfig(params)
|
2020-09-11 12:06:18 +03:00
|
|
|
, Store(params)
|
2017-03-03 20:05:50 +02:00
|
|
|
, RemoteStore(params)
|
2024-05-23 19:14:53 +03:00
|
|
|
, master(createSSHMaster(
|
2017-03-03 20:05:50 +02:00
|
|
|
// Use SSH master only if using more than 1 connection.
|
2024-05-23 19:14:53 +03:00
|
|
|
connections->capacity() > 1))
|
2024-01-25 17:31:52 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2020-09-11 12:11:05 +03:00
|
|
|
static std::set<std::string> uriSchemes() { return {"ssh-ng"}; }
|
2020-09-08 15:50:23 +03:00
|
|
|
|
2017-03-03 20:05:50 +02:00
|
|
|
std::string getUri() override
|
|
|
|
{
|
2020-09-11 12:11:05 +03:00
|
|
|
return *uriSchemes().begin() + "://" + host;
|
2017-03-03 20:05:50 +02:00
|
|
|
}
|
2016-09-12 15:03:29 +03:00
|
|
|
|
2022-03-08 20:20:39 +02:00
|
|
|
// FIXME extend daemon protocol, move implementation to RemoteStore
|
2022-12-15 22:58:54 +02:00
|
|
|
std::optional<std::string> getBuildLogExact(const StorePath & path) override
|
|
|
|
{ unsupported("getBuildLogExact"); }
|
2022-03-08 20:20:39 +02:00
|
|
|
|
2023-03-23 16:06:45 +02:00
|
|
|
protected:
|
2016-09-12 15:03:29 +03:00
|
|
|
|
|
|
|
struct Connection : RemoteStore::Connection
|
|
|
|
{
|
2017-03-03 20:05:50 +02:00
|
|
|
std::unique_ptr<SSHMaster::Connection> sshConn;
|
2021-09-23 19:01:04 +03:00
|
|
|
|
|
|
|
void closeWrite() override
|
|
|
|
{
|
|
|
|
sshConn->in.close();
|
|
|
|
}
|
2016-09-12 15:03:29 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
ref<RemoteStore::Connection> openConnection() override;
|
|
|
|
|
2017-03-03 20:05:50 +02:00
|
|
|
std::string host;
|
2016-08-10 17:44:39 +03:00
|
|
|
|
2023-04-15 13:02:41 +03:00
|
|
|
std::vector<std::string> extraRemoteProgramArgs;
|
|
|
|
|
2017-03-03 20:05:50 +02:00
|
|
|
SSHMaster master;
|
2018-03-05 14:42:15 +02:00
|
|
|
|
|
|
|
void setOptions(RemoteStore::Connection & conn) override
|
|
|
|
{
|
|
|
|
/* TODO Add a way to explicitly ask for some options to be
|
|
|
|
forwarded. One option: A way to query the daemon for its
|
|
|
|
settings, and then a series of params to SSHStore like
|
|
|
|
forward-cores or forward-overridden-cores that only
|
|
|
|
override the requested settings.
|
|
|
|
*/
|
|
|
|
};
|
2016-09-12 15:03:29 +03:00
|
|
|
};
|
|
|
|
|
2023-04-15 13:02:41 +03:00
|
|
|
struct MountedSSHStoreConfig : virtual SSHStoreConfig, virtual LocalFSStoreConfig
|
|
|
|
{
|
|
|
|
using SSHStoreConfig::SSHStoreConfig;
|
|
|
|
using LocalFSStoreConfig::LocalFSStoreConfig;
|
|
|
|
|
|
|
|
MountedSSHStoreConfig(StringMap params)
|
|
|
|
: StoreConfig(params)
|
|
|
|
, RemoteStoreConfig(params)
|
|
|
|
, CommonSSHStoreConfig(params)
|
|
|
|
, SSHStoreConfig(params)
|
|
|
|
, LocalFSStoreConfig(params)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2024-05-23 19:14:53 +03:00
|
|
|
MountedSSHStoreConfig(std::string_view scheme, std::string_view host, StringMap params)
|
|
|
|
: StoreConfig(params)
|
|
|
|
, RemoteStoreConfig(params)
|
|
|
|
, CommonSSHStoreConfig(scheme, host, params)
|
|
|
|
, SSHStoreConfig(params)
|
|
|
|
, LocalFSStoreConfig(params)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2024-05-08 01:28:50 +03:00
|
|
|
const std::string name() override { return "Experimental SSH Store with filesystem mounted"; }
|
2023-04-15 13:02:41 +03:00
|
|
|
|
|
|
|
std::string doc() override
|
|
|
|
{
|
|
|
|
return
|
|
|
|
#include "mounted-ssh-store.md"
|
|
|
|
;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::optional<ExperimentalFeature> experimentalFeature() const override
|
|
|
|
{
|
|
|
|
return ExperimentalFeature::MountedSSHStore;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The mounted ssh store assumes that filesystems on the remote host are
|
|
|
|
* shared with the local host. This means that the remote nix store is
|
|
|
|
* available locally and is therefore treated as a local filesystem
|
|
|
|
* store.
|
|
|
|
*
|
|
|
|
* MountedSSHStore is very similar to UDSRemoteStore --- ignoring the
|
|
|
|
* superficial differnce of SSH vs Unix domain sockets, they both are
|
|
|
|
* accessing remote stores, and they both assume the store will be
|
|
|
|
* mounted in the local filesystem.
|
|
|
|
*
|
|
|
|
* The difference lies in how they manage GC roots. See addPermRoot
|
|
|
|
* below for details.
|
|
|
|
*/
|
|
|
|
class MountedSSHStore : public virtual MountedSSHStoreConfig, public virtual SSHStore, public virtual LocalFSStore
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
|
2024-01-25 17:31:52 +02:00
|
|
|
MountedSSHStore(
|
|
|
|
std::string_view scheme,
|
|
|
|
std::string_view host,
|
|
|
|
const Params & params)
|
2023-04-15 13:02:41 +03:00
|
|
|
: StoreConfig(params)
|
|
|
|
, RemoteStoreConfig(params)
|
2024-05-23 19:14:53 +03:00
|
|
|
, CommonSSHStoreConfig(scheme, host, params)
|
2023-04-15 13:02:41 +03:00
|
|
|
, SSHStoreConfig(params)
|
|
|
|
, LocalFSStoreConfig(params)
|
|
|
|
, MountedSSHStoreConfig(params)
|
|
|
|
, Store(params)
|
|
|
|
, RemoteStore(params)
|
|
|
|
, SSHStore(scheme, host, params)
|
|
|
|
, LocalFSStore(params)
|
|
|
|
{
|
|
|
|
extraRemoteProgramArgs = {
|
|
|
|
"--process-ops",
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
static std::set<std::string> uriSchemes()
|
|
|
|
{
|
|
|
|
return {"mounted-ssh-ng"};
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string getUri() override
|
|
|
|
{
|
|
|
|
return *uriSchemes().begin() + "://" + host;
|
|
|
|
}
|
|
|
|
|
|
|
|
void narFromPath(const StorePath & path, Sink & sink) override
|
|
|
|
{
|
|
|
|
return LocalFSStore::narFromPath(path, sink);
|
|
|
|
}
|
|
|
|
|
|
|
|
ref<SourceAccessor> getFSAccessor(bool requireValidPath) override
|
|
|
|
{
|
|
|
|
return LocalFSStore::getFSAccessor(requireValidPath);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::optional<std::string> getBuildLogExact(const StorePath & path) override
|
|
|
|
{
|
|
|
|
return LocalFSStore::getBuildLogExact(path);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This is the key difference from UDSRemoteStore: UDSRemote store
|
|
|
|
* has the client create the direct root, and the remote side create
|
|
|
|
* the indirect root.
|
|
|
|
*
|
|
|
|
* We could also do that, but the race conditions (will the remote
|
|
|
|
* side see the direct root the client made?) seems bigger.
|
|
|
|
*
|
|
|
|
* In addition, the remote-side will have a process associated with
|
|
|
|
* the authenticating user handling the connection (even if there
|
|
|
|
* is a system-wide daemon or similar). This process can safely make
|
|
|
|
* the direct and indirect roots without there being such a risk of
|
|
|
|
* privilege escalation / symlinks in directories owned by the
|
|
|
|
* originating requester that they cannot delete.
|
|
|
|
*/
|
|
|
|
Path addPermRoot(const StorePath & path, const Path & gcRoot) override
|
|
|
|
{
|
|
|
|
auto conn(getConnection());
|
|
|
|
conn->to << WorkerProto::Op::AddPermRoot;
|
|
|
|
WorkerProto::write(*this, *conn, path);
|
|
|
|
WorkerProto::write(*this, *conn, gcRoot);
|
|
|
|
conn.processStderr();
|
|
|
|
return readString(conn->from);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-09-02 21:31:38 +03:00
|
|
|
ref<RemoteStore::Connection> SSHStore::openConnection()
|
|
|
|
{
|
|
|
|
auto conn = make_ref<Connection>();
|
2024-01-22 22:50:00 +02:00
|
|
|
Strings command = remoteProgram.get();
|
|
|
|
command.push_back("--stdio");
|
|
|
|
if (remoteStore.get() != "") {
|
|
|
|
command.push_back("--store");
|
|
|
|
command.push_back(remoteStore.get());
|
|
|
|
}
|
|
|
|
command.insert(command.end(),
|
|
|
|
extraRemoteProgramArgs.begin(), extraRemoteProgramArgs.end());
|
|
|
|
conn->sshConn = master.startCommand(std::move(command));
|
2017-03-03 20:05:50 +02:00
|
|
|
conn->to = FdSink(conn->sshConn->in.get());
|
|
|
|
conn->from = FdSource(conn->sshConn->out.get());
|
2016-09-02 21:31:38 +03:00
|
|
|
return conn;
|
|
|
|
}
|
|
|
|
|
2020-10-06 14:36:55 +03:00
|
|
|
static RegisterStoreImplementation<SSHStore, SSHStoreConfig> regSSHStore;
|
2023-04-15 13:02:41 +03:00
|
|
|
static RegisterStoreImplementation<MountedSSHStore, MountedSSHStoreConfig> regMountedSSHStore;
|
2016-09-02 21:31:38 +03:00
|
|
|
|
|
|
|
}
|