libstore: allow specifying port for ssh[-ng] stores

This also adds documentation for it in distributed-builds.md as that's
possibly the most common use-case for remote ssh stores

Support for `std::optional<uint16_t>` in Setting is also added since
setting a port is optional.

The line `#include "lix/libutil/strings.hh"` fixes that templates instanciations
in lix/libutil/config-impl.hh were using string utils without including
the header (why are they even there btw)

Change-Id: Id806c117c48cdf158d9d1cb1e639b0df31d9bf11
This commit is contained in:
Sebastian Widua
2025-02-22 20:37:45 +01:00
parent 044b4c4500
commit 82c7e76c9c
12 changed files with 50 additions and 5 deletions
+3
View File
@@ -153,6 +153,9 @@ roberth:
display_name: Robert Hensing
github: roberth
seppel3210:
github: Seppel3210
teofilc:
forgejo: teofilc
github: TeofilC
+10
View File
@@ -0,0 +1,10 @@
---
synopsis: 'Allow specifying ports for remote ssh[-ng] stores'
issues: []
cls: [2432]
category: Improvements
credits: [seppel3210]
---
You can now specify which port should be used for a remote ssh store (e.g. for remote/distributed builds) through a uri parameter.
E.g., when a remote builder `foo` is listening on port `1234` instead of the default, it can be specified like this `ssh://foo?port=1234`.
@@ -75,7 +75,9 @@ by spaces. Only the first element is required. To leave a field at its
default, set it to `-`.
1. The URI of the remote store in the format
`ssh://[username@]hostname`, e.g. `ssh://nix@mac` or `ssh://mac`.
`ssh://[username@]hostname[?port=<port>]`, e.g. `ssh://nix@mac` or `ssh://mac`.
If the ssh server is not listening on port 22 (e.g. port 1337 in this case)
the URI would be `ssh://nix@mac?port=1337`
For backward compatibility, `ssh://` may be omitted. The hostname
may be an alias defined in your `~/.ssh/config`.
+3
View File
@@ -12,6 +12,8 @@
#include "lix/libutil/result.hh"
#include "lix/libutil/strings.hh"
#include "lix/libstore/derivations.hh"
#include "lix/libutil/config-impl.hh"
#include "lix/libutil/abstract-setting-to-json.hh"
namespace nix {
@@ -113,6 +115,7 @@ struct LegacySSHStore final : public Store
))
, master(
host,
config_.port,
config_.sshKey,
config_.sshPublicHostKey,
// Use SSH master only if using more than 1 connection.
+1 -1
View File
@@ -1,6 +1,6 @@
R"(
**Store URL format**: `ssh://[username@]hostname`
**Store URL format**: `ssh://[username@]hostname[?port=42069]`
This store type allows limited access to a remote store on another
machine via SSH.
+1
View File
@@ -39,6 +39,7 @@ public:
, host(host)
, master(
host,
config_.port,
config_.sshKey,
config_.sshPublicHostKey,
// Use SSH master only if using more than 1 connection.
+4
View File
@@ -2,6 +2,7 @@
///@file
#include "lix/libstore/store-api.hh"
#include <cstdint>
namespace nix {
@@ -9,6 +10,9 @@ struct CommonSSHStoreConfig : virtual StoreConfig
{
using StoreConfig::StoreConfig;
const Setting<std::optional<uint16_t>> port{this, std::nullopt, "port",
"Port that should be used instead of the default on the remote machine."};
const Setting<Path> sshKey{this, "", "ssh-key",
"Path to the SSH private key used to authenticate to the remote machine."};
+1 -1
View File
@@ -1,6 +1,6 @@
R"(
**Store URL format**: `ssh-ng://[username@]hostname`
**Store URL format**: `ssh-ng://[username@]hostname[?port=42069]`
Experimental store type that allows full access to a Nix store on a
remote machine.
+4 -1
View File
@@ -8,8 +8,9 @@
namespace nix {
SSHMaster::SSHMaster(const std::string & host, const std::string & keyFile, const std::string & sshPublicHostKey, bool useMaster, bool compress, int logFD)
SSHMaster::SSHMaster(const std::string & host, const std::optional<uint16_t> port, const std::string & keyFile, const std::string & sshPublicHostKey, bool useMaster, bool compress, int logFD)
: host(host)
, port(port)
, fakeSSH(host == "localhost")
, keyFile(keyFile)
, sshPublicHostKey(sshPublicHostKey)
@@ -28,6 +29,8 @@ void SSHMaster::addCommonSSHOpts(Strings & args)
{
auto state(state_.lock());
if (port.has_value())
args.insert(args.end(), {"-p", std::to_string(*port)});
for (auto & i : tokenizeString<Strings>(getEnv("NIX_SSHOPTS").value_or("")))
args.push_back(i);
if (!keyFile.empty())
+3 -1
View File
@@ -4,6 +4,7 @@
#include "lix/libutil/file-system.hh"
#include "lix/libutil/processes.hh"
#include "lix/libutil/sync.hh"
#include <cstdint>
namespace nix {
@@ -12,6 +13,7 @@ class SSHMaster
private:
const std::string host;
const std::optional<uint16_t> port;
bool fakeSSH;
const std::string keyFile;
const std::string sshPublicHostKey;
@@ -33,7 +35,7 @@ private:
public:
SSHMaster(const std::string & host, const std::string & keyFile, const std::string & sshPublicHostKey, bool useMaster, bool compress, int logFD = -1);
SSHMaster(const std::string & host, const std::optional<uint16_t> port, const std::string & keyFile, const std::string & sshPublicHostKey, bool useMaster, bool compress, int logFD = -1);
struct Connection
{
+2
View File
@@ -15,6 +15,7 @@
#include "lix/libutil/args.hh"
#include "lix/libutil/config.hh"
#include "lix/libutil/logging.hh"
#include "lix/libutil/strings.hh"
namespace nix {
@@ -123,6 +124,7 @@ void BaseSetting<T>::convertToArg(Args & args, const std::string & category)
DECLARE_CONFIG_SERIALISER(std::string)
DECLARE_CONFIG_SERIALISER(std::optional<std::string>)
DECLARE_CONFIG_SERIALISER(std::optional<uint16_t>)
DECLARE_CONFIG_SERIALISER(bool)
DECLARE_CONFIG_SERIALISER(Strings)
DECLARE_CONFIG_SERIALISER(StringSet)
+15
View File
@@ -270,6 +270,21 @@ template<> std::string BaseSetting<std::optional<std::string>>::to_string() cons
return value ? *value : "";
}
template<> std::optional<uint16_t> BaseSetting<std::optional<uint16_t>>::parse(const std::string & str, const ApplyConfigOptions & options) const
{
if (str == "")
return std::nullopt;
else if (auto n = string2Int<uint16_t>(str))
return n;
else
throw UsageError("setting '%s' has invalid value '%s'", name, str);
}
template<> std::string BaseSetting<std::optional<uint16_t>>::to_string() const
{
return value ? std::to_string(*value) : "";
}
template<> bool BaseSetting<bool>::parse(const std::string & str, const ApplyConfigOptions & options) const
{
if (str == "true" || str == "yes" || str == "1")