diff --git a/doc/manual/change-authors.yml b/doc/manual/change-authors.yml index 51e2a11be..016b0cc3b 100644 --- a/doc/manual/change-authors.yml +++ b/doc/manual/change-authors.yml @@ -153,6 +153,9 @@ roberth: display_name: Robert Hensing github: roberth +seppel3210: + github: Seppel3210 + teofilc: forgejo: teofilc github: TeofilC diff --git a/doc/manual/rl-next/ssh-store-port.md b/doc/manual/rl-next/ssh-store-port.md new file mode 100644 index 000000000..26eb9a3a3 --- /dev/null +++ b/doc/manual/rl-next/ssh-store-port.md @@ -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`. diff --git a/doc/manual/src/advanced-topics/distributed-builds.md b/doc/manual/src/advanced-topics/distributed-builds.md index 80443b53e..e88c0490d 100644 --- a/doc/manual/src/advanced-topics/distributed-builds.md +++ b/doc/manual/src/advanced-topics/distributed-builds.md @@ -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=]`, 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`. diff --git a/lix/libstore/legacy-ssh-store.cc b/lix/libstore/legacy-ssh-store.cc index 028db6d46..04354dd97 100644 --- a/lix/libstore/legacy-ssh-store.cc +++ b/lix/libstore/legacy-ssh-store.cc @@ -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. diff --git a/lix/libstore/legacy-ssh-store.md b/lix/libstore/legacy-ssh-store.md index 043acebd6..eb57b03ed 100644 --- a/lix/libstore/legacy-ssh-store.md +++ b/lix/libstore/legacy-ssh-store.md @@ -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. diff --git a/lix/libstore/ssh-store.cc b/lix/libstore/ssh-store.cc index 2c76cb707..8e2224d9a 100644 --- a/lix/libstore/ssh-store.cc +++ b/lix/libstore/ssh-store.cc @@ -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. diff --git a/lix/libstore/ssh-store.hh b/lix/libstore/ssh-store.hh index 725f0429a..7377cd835 100644 --- a/lix/libstore/ssh-store.hh +++ b/lix/libstore/ssh-store.hh @@ -2,6 +2,7 @@ ///@file #include "lix/libstore/store-api.hh" +#include namespace nix { @@ -9,6 +10,9 @@ struct CommonSSHStoreConfig : virtual StoreConfig { using StoreConfig::StoreConfig; + const Setting> port{this, std::nullopt, "port", + "Port that should be used instead of the default on the remote machine."}; + const Setting sshKey{this, "", "ssh-key", "Path to the SSH private key used to authenticate to the remote machine."}; diff --git a/lix/libstore/ssh-store.md b/lix/libstore/ssh-store.md index 881537e71..fd72042c2 100644 --- a/lix/libstore/ssh-store.md +++ b/lix/libstore/ssh-store.md @@ -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. diff --git a/lix/libstore/ssh.cc b/lix/libstore/ssh.cc index cbb7aa32a..8ad7f7e53 100644 --- a/lix/libstore/ssh.cc +++ b/lix/libstore/ssh.cc @@ -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 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(getEnv("NIX_SSHOPTS").value_or(""))) args.push_back(i); if (!keyFile.empty()) diff --git a/lix/libstore/ssh.hh b/lix/libstore/ssh.hh index 321655804..94609e74e 100644 --- a/lix/libstore/ssh.hh +++ b/lix/libstore/ssh.hh @@ -4,6 +4,7 @@ #include "lix/libutil/file-system.hh" #include "lix/libutil/processes.hh" #include "lix/libutil/sync.hh" +#include namespace nix { @@ -12,6 +13,7 @@ class SSHMaster private: const std::string host; + const std::optional 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 port, const std::string & keyFile, const std::string & sshPublicHostKey, bool useMaster, bool compress, int logFD = -1); struct Connection { diff --git a/lix/libutil/config-impl.hh b/lix/libutil/config-impl.hh index 5993693d4..449050bd2 100644 --- a/lix/libutil/config-impl.hh +++ b/lix/libutil/config-impl.hh @@ -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::convertToArg(Args & args, const std::string & category) DECLARE_CONFIG_SERIALISER(std::string) DECLARE_CONFIG_SERIALISER(std::optional) +DECLARE_CONFIG_SERIALISER(std::optional) DECLARE_CONFIG_SERIALISER(bool) DECLARE_CONFIG_SERIALISER(Strings) DECLARE_CONFIG_SERIALISER(StringSet) diff --git a/lix/libutil/config.cc b/lix/libutil/config.cc index ebc45ff13..5cbd81541 100644 --- a/lix/libutil/config.cc +++ b/lix/libutil/config.cc @@ -270,6 +270,21 @@ template<> std::string BaseSetting>::to_string() cons return value ? *value : ""; } +template<> std::optional BaseSetting>::parse(const std::string & str, const ApplyConfigOptions & options) const +{ + if (str == "") + return std::nullopt; + else if (auto n = string2Int(str)) + return n; + else + throw UsageError("setting '%s' has invalid value '%s'", name, str); +} + +template<> std::string BaseSetting>::to_string() const +{ + return value ? std::to_string(*value) : ""; +} + template<> bool BaseSetting::parse(const std::string & str, const ApplyConfigOptions & options) const { if (str == "true" || str == "yes" || str == "1")