Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SSH2 exec crashes while returning data & "Please close the channel (1) before trying to open it again" #1985

Closed
procodix opened this issue Feb 27, 2024 · 9 comments

Comments

@procodix
Copy link

I am trying to trigger a docker download over a SSH connections on a remote server.

image

When calling the docker command locally, the images are downloaded. Doing that over SSH2, the output seems to confuse phpseclib and forces it to abort the connection without any returned result.

Moreover, the next exec statements raises the "Please close the channel (1) before trying to open it again" exception, showing, that the former exec() didn't finish correctly.

Base on an earlier bug report I included a

$this->Connection->enablePTY();

call before the exec command. This doesn't help.

@procodix
Copy link
Author

reopened from #1936.

@terrafrost
Copy link
Member

If you could post code that reproduces the problem and the logs (which you can get by doing define('NET_SSH2_LOGGING', 3); that'd be helpful.

Moreover, the next exec statements raises the "Please close the channel (1) before trying to open it again" exception, showing, that the former exec() didn't finish correctly.

If the server hasn't ended the last exec() then phpseclib can't start a new one. You'd have to close the channel and re-open it.

If you opened a PTY before running the command then you can close the channel by doing disablePTY(). Most likely the command isn't going to stop running on it's own anyway. I mean, maybe ls -latr would but if you do something like top with a PTY it won't ever end until you tell it to.

This wouldn't be as big of an issue if phpseclib supported multiple channels better but that's years away. It'd require a fairly comprehensive rewrite of SSH2.php and right now, for phpseclib 4.0, I'm in the middle of a fairly comprehensive rewrite of ASN1.php / X509.php.

@procodix
Copy link
Author

procodix commented Mar 6, 2024

What I do is, execute docker commands on a remote machine over a single connection. for example to restart a docker container:

  • instantiate SSH2 connection
  • directly after this command, enablePTY()
  • execute first command "docker stop & rm"
  • execute next command "docker run ..." <- this downloads the docker images an shows CLI progress bars.

Without enablePTY() this crashes the connection, but silently without any error. With enablePTY() the this or the next command complains with this issue's title.

@procodix
Copy link
Author

procodix commented Mar 6, 2024

last debug entries before stop. This occurs during the first exec call "docker rm"

<- NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION (since last: 0.0003, network: 0.0001s)
00000000 00:00:00:01:00:00:00:00:00:00:00:00:00:00:80:00 ................

-> NET_SSH2_MSG_CHANNEL_REQUEST (since last: 0.0003, network: 0s)
00000000 00:00:00:00:00:00:00:07:70:74:79:2d:72:65:71:01 ........pty-req.
00000010 00:00:00:05:76:74:31:30:30:00:00:00:50:00:00:00 ....vt100...P...
00000020 18:00:00:00:00:00:00:00:00:00:00:00:01:00 ..............

<- NET_SSH2_MSG_CHANNEL_SUCCESS (since last: 0.0056, network: 0.0001s)
00000000 00:00:00:01 ....

-> NET_SSH2_MSG_CHANNEL_REQUEST (since last: 0.0002, network: 0s)
00000000 00:00:00:00:00:00:00:04:65:78:65:63:01:00:00:00 ........exec....
00000010 13:64:6f:63:6b:65:72:20:73:74:6f:70:20:6e:65:6f .docker stop neo
00000020 6c:69:6e:6b link

<- NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST (since last: 0.0069, network: 0.0002s)
00000000 00:00:00:01:00:20:00:00 ..... ..

<- NET_SSH2_MSG_CHANNEL_SUCCESS (since last: 0.0002, network: 0.0001s)
00000000 00:00:00:01

throws exception: "Please close the channel (1) before trying to open it again"

@terrafrost
Copy link
Member

  • instantiate SSH2 connection
  • directly after this command, enablePTY()
  • execute first command "docker stop & rm"
  • execute next command "docker run ..." <- this downloads the docker images an shows CLI progress bars.

I assume this means that you're doing this?:

$ssh->enablePTY();
$ssh->exec('docker stop & rm');
$ssh->exec('docker run ...');

Anyway, as I said, that's not going to work and there's nothing I can easily do about that. I have designs to make that work better in phpseclib 5 but given that phpseclib 4 is prob years away from being released, clearly, phpseclib 5 is even farther away.

You can do $ssh->disablePTY() between the two $ssh->exec()'s but even then I'm not sure that that'll work as each new $ssh->exec() instance is like running commands in a whole new terminal. Like if you do $ssh->exec('cd /path/to/whatever') and then do echo $ssh->exec('pwd') it'll be like the cd never happened.

If you're going to do that you might as well use an interactive shell. Like read the prompt, send the command, read the prompt, and then send the next command. If you're not sure how to use that then https://phpseclib.com/docs/commands#read-with-regular-expressions-sudo should hopefully provide a good guide, however, you will need to figure out what your prompt is.

Anyway, the SSH logs that'd be useful to see would be the one where it "crashes the connection, but silently without any error". The SSH logs and, if you can get them, the server logs.

@procodix
Copy link
Author

procodix commented Mar 6, 2024

The crash is not logged. I can only tell it crashes, because pulling the docker image locally takes about a minute. SSH2:exec returns after 10 seconds without any output.

@procodix
Copy link
Author

procodix commented Mar 6, 2024

I would say there is some overflow, maybe of a network buffer capturing the output. Since the docker progress bars send lots of text, could this be crashing exec()?

@terrafrost
Copy link
Member

SSH2:exec returns after 10 seconds without any output.

Sounds like you're hitting the timeout. By default it's 10 seconds. You can make it so that it doesn't time out all together by doing $ssh->setTimeout(0).

That said, even if nothing is returned, you can still do $ssh->getLog() provided you did define('NET_SSH2_LOGGING', 2) before. Like the following will return logged data:

define('NET_SSH2_LOGGING', 2);
$ssh = new SSH2('whatever.com');
$ssh->login('user', 'pass');
echo $ssh->getLog();

$ssh->login() isn't outputting anything but $ssh->getLog() still outputs stuff.

@procodix
Copy link
Author

procodix commented Mar 8, 2024

Thank you, The timeout() was the right solution to prevent the aborts.
Setting it to 0 however brings me a "No data received from server" exception. So I set it to 100, which seems sufficient for even the largest-to-download docker images.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants