Open graywolf opened 7 months ago
Hello!
Thank you for the bug report.
I investigated the problem I came to conclusion that it's because currently the config file is being read after the options are set: https://github.com/artyom-poptsov/guile-ssh/blob/7b2759753e875012aa18ece8aed39d9c89a44515/modules/ssh/session.scm#L111
So even though you provide #:port
option to make-session
and it set port it will be overwritten by the port number from the configuration file.
I think that we must read the config first and then set the provided options.
I'll see what I can do.
-avp
As a workaround you can try to set the #:config
option to #f
so the configuration file will not be read:
https://github.com/artyom-poptsov/guile-ssh/blob/7b2759753e875012aa18ece8aed39d9c89a44515/modules/ssh/session.scm#L105
The issue is fixed in the latest release.
Thanks!
Hello, thanks for the fix, however I am not sure it works.
I have ran it with guix shell --with-branch=guile-ssh=master guile guile-ssh strace
to make sure I have the latest version, and I am still getting the port 2222. I did put together complete reproducer (using guix):
$ printf 'host example.org\nport 2222\n' >/tmp/test-ssh-config
$ guix shell -CN --with-branch=guile-ssh=master --expose=/tmp/test-ssh-config=/home/$USER/.ssh/config guile guile-ssh strace -- strace guile -c '(use-modules (ssh session)) (define session (make-session #:host "example.org" #:user "root" #:log-verbosity '\''functions #:port 22)) (connect! session)' 2>&1 | grep htons
connect(5, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("192.168.0.1")}, 16) = 0
recvfrom(5, "\257\316\201\200\0\1\0\1\0\0\0\0\7example\3org\0\0\1\0\1\300\f\0"..., 2048, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("192.168.0.1")}, [28 => 16]) = 45
recvfrom(5, "\3717\201\200\0\1\0\1\0\0\0\0\7example\3org\0\0\34\0\1\300\f\0"..., 65536, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("192.168.0.1")}, [28 => 16]) = 57
connect(5, {sa_family=AF_INET, sin_port=htons(2222), sin_addr=inet_addr("93.184.215.14")}, 16) = 0
getsockname(5, {sa_family=AF_INET, sin_port=htons(45431), sin_addr=inet_addr("10.11.0.28")}, [28 => 16]) = 0
connect(5, {sa_family=AF_INET6, sin6_port=htons(2222), sin6_flowinfo=htonl(0), inet_pton(AF_INET6, "2606:2800:21f:cb07:6820:80da:af6b:8b2c", &sin6_addr), sin6_scope_id=0}, 28) = 0
getsockname(5, {sa_family=AF_INET6, sin6_port=htons(34785), sin6_flowinfo=htonl(0), inet_pton(AF_INET6, "fdda:d0d0:cafe:1197::101a", &sin6_addr), sin6_scope_id=0}, [28]) = 0
connect(5, {sa_family=AF_INET, sin_port=htons(2222), sin_addr=inet_addr("93.184.215.14")}, 16) = -1 EINPROGRESS (Operation now in progress)
As you can see, it still tries to connect to 2222.
Also, looking closer at the code, the `config' should be #f anyway, so the whole moved block (when config ...) should not even trigger. And when I check the strace output, I can see the ~/.ssh/config file being opened for reading. So it is probably coming from somewhere else?
The commit is (in my opinion) still desired for situations with config being non-#f, but it does not seem to address this particular issue.
Or I am doing something wrong, also possible.
EDIT: When I pass #:config "/dev/null"
into make-session, it starts to use the 22 port.
I think https://github.com/libssh/libssh-mirror/blob/ac6d2fad4a8bf07277127736367e90387646363f/src/options.c#L1472 is the cause why #:config "/dev/null"
causes it to work. Looking at https://api.libssh.org/master/group__libssh__session.html#ga7a801b85800baa3f4e16f5b47db0a73d I think #:config #f
should imply SSH_OPTIONS_PROCESS_CONFIG
being set to 0
. Which is currently not possible as far as I can tell.
diff --git a/libguile-ssh/session-func.c b/libguile-ssh/session-func.c
index 7006b62..81fa227 100644
--- a/libguile-ssh/session-func.c
+++ b/libguile-ssh/session-func.c
@@ -55,6 +55,7 @@ static gssh_symbol_t session_options[] = {
{ "knownhosts", SSH_OPTIONS_KNOWNHOSTS },
{ "timeout", SSH_OPTIONS_TIMEOUT },
{ "timeout-usec", SSH_OPTIONS_TIMEOUT_USEC },
+ { "process-config", SSH_OPTIONS_PROCESS_CONFIG },
{ "ssh1", SSH_OPTIONS_SSH1 },
{ "ssh2", SSH_OPTIONS_SSH2 },
{ "log-verbosity", SSH_OPTIONS_LOG_VERBOSITY },
@@ -380,6 +381,7 @@ set_option (SCM scm_session, gssh_session_t* sd, int type, SCM value)
case SSH_OPTIONS_TIMEOUT_USEC:
return set_uint64_opt (session, type, value);
+ case SSH_OPTIONS_PROCESS_CONFIG:
case SSH_OPTIONS_SSH1:
case SSH_OPTIONS_SSH2:
case SSH_OPTIONS_STRICTHOSTKEYCHECK:
diff --git a/modules/ssh/session.scm b/modules/ssh/session.scm
index 4aaf5d3..f80925b 100644
--- a/modules/ssh/session.scm
+++ b/modules/ssh/session.scm
@@ -82,7 +82,8 @@ Return a new SSH session."
(session-set-if-specified! host)
- (when config
+ (if config
+(begin
(or host
(throw 'guile-ssh-error
"'config' is specified, but 'host' option is missed."))
@@ -93,6 +94,8 @@ Return a new SSH session."
(%gssh-session-parse-config! session #f))
(else
(throw 'guile-ssh-error "Wrong 'config' value" config))))
+(session-set! session 'process-config #f)
+)
(session-set-if-specified! port)
(session-set-if-specified! user)
This seems to work? Sorry for the formatting, mg
does not have the best scheme support.
As far as I can tell process-config
and #f
passed as the value for config
option must have different meaning. When config
is set to #f
it means that the default user configuration file (~/.ssh/config
) must be read. On the other hand, setting process-config
(SSH_OPTIONS_PROCESS_CONFIG
) to #f
means that no configuration files should be read at all (including the system config):
SSH_OPTIONS_PROCESS_CONFIG Set it to false to disable automatic processing of per-user and system-wide OpenSSH configuration files. LibSSH automatically uses these configuration files unless you provide it with this option or with different file (bool).
As such, having process-config
option in Guile-SSH is desirable indeed, but I think that it does not solve the problem described in this issue.
But your attempt to solve the problem helped me to find another bug: currently there's no way to set config to #f
in libssh as when config
is false in make-session
all the config handling code is skipped. I'll fix it in the next release.
What is interesting with current situation is that when config
option is set for make-session
the configuration file should be read before setting other options thus options set after config is read should overwrite the options from the config. But according to your observations we don't see this behavior.
As far as I can tell
process-config
and#f
passed as the value forconfig
option must have different meaning. Whenconfig
is set to#f
it means that the default user configuration file (~/.ssh/config
) must be read. On the other hand, settingprocess-config
(SSH_OPTIONS_PROCESS_CONFIG
) to#f
means that no configuration files should be read at all (including the system config):
Albeit that might be the intention, it is not obvious from the documentation:
‘config’
The option specifies whether an SSH config should be parsed or
not, and optionally the path to a config file.
Setting the VALUE to ‘#t’ means that the default
‘~/.ssh/config’ should be parsed; in turn, setting the option
to ‘#f’ (the default value) means that the config should not
be parsed at all. If the value is a string, then the string
is expected to be a path to config file.
The procedure reads the config file after all other specified
options are set. When the config file is read, the options
for SESSION are set, overwriting those that were passed to the
procedure.
You _must_ specify at least a host name when using this
option, otherwise the procedure will fail.
Optionally you could use ‘session-parse-config!’ procedure
explicitly to read the config (see below.)
Expected types of VALUE: Either a string or a boolean value.
My understanding of in turn, setting the option to ‘#f’ (the default value) means that the config should not be parsed at all.
is that by default (== #f
), no configuration file (not even the default one) will be parsed.
One option I guess would be to change the default value to #t
and apply some variation of the patch above.
PS: The documentation snippet above still says
The procedure reads the config file after all other specified
options are set. When the config file is read, the options
for SESSION are set, overwriting those that were passed to the
procedure.
should I open another bug? I feel like I am spamming you enough already. :)
What is interesting with current situation is that when
config
option is set formake-session
the configuration file should be read before setting other options thus options set after config is read should overwrite the options from the config. But according to your observations we don't see this behavior.
This seems to be a misunderstanding, as I said above: When I pass #:config "/dev/null" into make-session, it starts to use the 22 port.
So passing in any config, even /dev/null
is enough to suppress the reading of the default one.
Albeit that might be the intention, it is not obvious from the documentation:
Okay, that's a good point. The documentation needs to be fixed.
But maybe we can improve the API as follows:
#:process-config #f
disables reading of any configuration files altogether as it works in libssh.#:config "/dev/null"
or #:config #f
means the same thing -- no user configuration file should be read.#:config 'default
reads the default user configuration file (~/.ssh/config
.)#:config "/path/to/config"
specifies a config file to read.What do you think?
PS: The documentation snippet above still says
Yeah, it seems that was my intention back then when I implemented make-session
. But you're right, the options passed to make-session
should overwrite the options from the config, it is more logical.
should I open another bug? I feel like I am spamming you enough already. :)
I think it's fine to use this bug to discuss the documentation changes related to make-session
as well. Thanks for helping!
BTW, I just fixed (d56fb10) a bug in a test that checks if options passed to make-session
overwrite options from the user configuration. The test looks now as follows:
(test-equal "make-session: keywords must overwrite config options"
22
(let ((s (make-session #:host "example"
#:port 22
;; Configuration sets port to 2222
#:config %config)))
(session-get s 'port)))
The configuration file contains port number 2222 but #:port
overwrites this value with 22 as it should so the test passes fine.
On 2024-05-05 09:15:12 -0700, Artyom V. Poptsov wrote:
Albeit that might be the intention, it is not obvious from the documentation:
Okay, that's a good point. The documentation needs to be fixed.
But maybe we can improve the API as follows:
#:process-config #f
disables reading of any configuration files altogether as it works in libssh.
What will be the behavior when called with #:process-config #f #:config 'default
?
I wonder whether we actually need separate keyword (#:process-config) or the
Naming nitpick: I am pretty new to Guile, but should the name not be
#:process-config?
?
#:config "/dev/null"
or#:config #f
means the same thing -- no user configuration file should be read.
It is not obvious from this proposal whether that is the case, so I will just note that I do not think string "/dev/null" should have a special meaning. It will work to suppress the configuration reading, same like it does now, but should not get special handling.
#:config 'default
reads the default user configuration file (~/.ssh/config
.)
I personally would prefer #t to 'default, it is shorter. But if you prefer the symbol, maybe at least both ('default and #t) could be allowed?
I guess for backward compatibility this needs to be default (no pun intended) value of #:config. Do you think someone passes in #f explicitly in their code?
#:config "/path/to/config"
specifies a config file to read.What do you think?
I like it ^_^ Just the comments above.
PS: The documentation snippet above still says
Yeah, it seems that was my intention back then when I implemented
make-session
. But you're right, the options passed tomake-session
should overwrite the options from the config, it is more logical.should I open another bug? I feel like I am spamming you enough already. :)
I think it's fine to use this bug to discuss the documentation changes related to
make-session
as well. Thanks for helping!
You are welcome. More like, thank you for writing this library. I was putting together a Guile-based server installer script and guile-ssh was of immense help.
Cheers, Tomas
-- There are only two hard things in Computer Science: cache invalidation, naming things and off-by-one errors.
Hello! Sorry for the late answer.
I guess for backward compatibility this needs to be default (no pun intended) value of #:config. Do you think someone passes in #f explicitly in their code?
Agreed, we should keep backward compatibility where it's possible.
But currently there's a problem in config-handling code in Guile-SSH:
%gssh-session-parse-config!
with #f
in the place of the config tells libssh to read default configuration files.#f
in make-session
then %gssh-session-parse-config!
is not called at all, leading to some default internal libssh behavior. So there's no way to call %gssh-session-parse-config!
with #f
.So maybe we should go with the following:
#:config #f
means that no user configuration file should be read (as per Guile-SSH documentation.) Internally we'll set process-config?
option in libssh to false
. In the same time #:config "/dev/null"
will be passed to libssh without any special handling as it works now. Thus it would be a bugfix in the code so it will match the description in the documentation.#:config #t
reads the default user configuration file (~/.ssh/config
.) We should use it by default for the backward compatibility.#:config "/path/to/config"
specifies a config file to read (no changes in the API.)That means that instead of
(when config
(or host
(throw 'guile-ssh-error
"'config' is specified, but 'host' option is missed."))
(cond
((string? config)
(%gssh-session-parse-config! session config))
((boolean? config)
(%gssh-session-parse-config! session #f))
(else
(throw 'guile-ssh-error "Wrong 'config' value" config))))
we will do something like this (as you proposed):
(if config
(begin
(or host
(throw 'guile-ssh-error
"'config' is specified, but 'host' option is missed."))
(cond
((string? config)
(%gssh-session-parse-config! session config))
((boolean? config) ; set to #t
(%gssh-session-parse-config! session #f))
(else
(throw 'guile-ssh-error "Wrong 'config' value" config))))
(session-set! session 'process-config? #f))
Does this sound right?
By the way, I see that you've been quite active in Guile-SSH recently with several useful bug reports. Do you want to participate in Guile-SSH development more directly? You could start with a pull request to one of your issues, and then we'll see -- maybe I could give you the commit access.
Thanks! -avp
I pushed the proposed fix to the branch 38-the-port-seems-to-be-ignored.
-avp
Hello,
I am trying to deploy using guix deploy (which under the hood uses guile-ssh), and it seems the #:port argument to (make-session) is ignored. I managed to reproduce it even without guix, the steps follow.
I have this in my ~/.ssh/config:
However, on specific host I am deploying to the sshd is still running on 22. I can connect fine using the ssh command (ssh -4 -p22 ...). Using strace I see this line:
However when I try to do the same using guile-ssh:
I see this instead:
Notice that sin_port is set to the value from ~/.ssh/config instead of what I passed in #:port.