-2

I've created a script which connects to a server using the phpseclib extension, using public key autentication (as you see here), by generating a key pair using ssh-keygen.

What I'm facing now is that the connection works very well when I run the php script from my localhost, but when I run the script from the server, I always get a failed login attempt, with the following error reported:

SSH_MSG_USERAUTH_FAILURE

(which I get when using $sftp->getLastError() after trying $sftp->login()).

Any idea why this happens?????

UPDATE

Thought it may be due to some error related to file_get_contents(), so I've checked on it, it's the value of the key indeed. I've also directly pasted the private key into the PublicKeyLoader::load() function, same error happened, so that's not the reason.

The main log events where I think the issue happened are, when running the exact same script trying to establish a connection to the target server:

from the localhost (working):

NET_SSH2_MSG_SERVICE_REQUEST;
NET_SSH2_MSG_SERVICE_ACCEPT;
NET_SSH2_MSG_USERAUTH_REQUEST (ssh-connection none); 
NET_SSH2_MSG_USERAUTH_FAILURE; 
NET_SSH2_MSG_USERAUTH_REQUEST(ssh-connection publickey); 
NET_SSH2_MSG_USERAUTH_PK_OK;

from the server (not working):

NET_SSH2_MSG_SERVICE_REQUEST;
NET_SSH2_MSG_SERVICE_ACCEPT;
NET_SSH2_MSG_USERAUTH_REQUEST (ssh-connection none); 
NET_SSH2_MSG_USERAUTH_FAILURE;
NET_SSH2_MSG_USERAUTH_REQUEST (ssh-connection publickey); 
NET_SSH2_MSG_USERAUTH_FAILURE;

I generated three key pairs, ran the localhost and the server-client test with all of them, and I always get what's shown above. And when I echo it out, I definitely get the key, and don't worry it's not an ecrypted OpenSSH private key, that's not the reason (not supported in phpseclib), and that would then fail on the localhost too (which I've tested).

So looks like it's failing on the step of the public key authentication on the server, although I'm using the exact same script, and although I'm getting the exactly correct key when echoing the retrieved key out, also on the server-side.

I've then checked what happens if I change a single character of my key; if that reproduces the above-displayed SSH - log, but no. If I do so, the script interrupts before logging anything, throwing an phpseclib3\Exception\NoKeyLoadedException: Unable to read key Exception.

From this I concluded that the only problem could be that the key is encoded in a certain wrong way before being sent to the server, and that the server misunderstands the key due to that.

Considering this, I've found that phpseclib requires the paragonie package according to this, and that one of phpseclibs dependencies of paragonie do some base64 - encoding. I thus supposed that it may the case that the encoding used to send the key is not properly done for some reason, and went to check on how the phpseclib extension was installed on the server (done by server admins, not by me).

Turned out it is a plesk server, which requires you to upload new composer packages in a somewhat unusual way, namely via the composer.json file, according to this. I've only installed composer packages either via command prompt, via SSH directly on the server, or to a local environment + deployed on the server. Neither of this work, and the instruction above also did not work.

I've then checked the composer.json file, and found its content to be:

{ 
  "name": "packagex/packagex", 
  "type": "plugin", 
  "description": "secret_description", 
  "keywords": ["secret_keyword"], 
  "homepage": "secret_url", 
  "license": "secret_license", 
  "authors": [ { "name": "secret_name", "email": "secret_mail" } ],
  "require": 
    {
      "php": ">=5.3", 
      "phpseclib/phpseclib": "^3.0"
    }
}

The file contained an extension already, which we cannot touch, but we need to add phpseclib. To do so, the line "phpseclib/phpseclib": "^3.0" has simply been added into the require key of the JSON, but I feel that this is not the correct way. IMHO, this kind of interpretes the phpseclib as a dependency of the already used package, which are completely independent from each other.

I'm starting to think now that this may be the final reason why the script fails when run on the server-side: the package is not properly installed in terms of its dependencies, hence the dependencies phpseclib itself needs are not properly used, as the phpseclib package itself is listed as a dependency. Due to this, some missing base64-encoding or similar (paragonie dependency of phpseclib package, see above) may be causing the issue. Is this possible? To eliminate this reason; I'd need to know how to properly install an extension via composer via composer.json on a plesk server, without touching the previously in-this-way installed packages. Any idea on how to do that? I've also posted a separate question for this, for further details; you may check on it.

What's also maybe causing an issue is that the current composer.json file requires a php version below the one which is required atm by phpseclib (php: >=5.6.1). But again, to change this, I need to know how to modify the composer.json file in the proper way.

UPDATE

I've updated the composer.json file by copying its contents from the plesk server to a local environment where I downloaded the composer. There I then installed the phpseclib package via command prompt, and then copied the resulting composer.json back to the Plesk Server. And I get exactly the same issue again (and besides also the same composer.json content as before). Help..?

DevelJoe
  • 856
  • 1
  • 10
  • 24
  • 1
    Show us `$sftp->getLog()` from both machines + Can you login *from the server* using any commandline/GUI SFTP client? – Martin Prikryl Nov 08 '21 at 13:59
  • 1
    So please TRY!! – Martin Prikryl Nov 08 '21 at 14:07
  • For `$sftp->getLog()` to work you need to do `define('NET_SSH2_LOGGING', 2)` at the top of the file in question. That said, I suspect the answer is simpler than that. Is the key in the actual PHP code as a string or are you doing `file_get_contents()` to read the key from the local file system? If the latter then are you sure `file_get_contents()` is actually reading the key? I'd do `echo $key;` just to make sure that the key is being loaded correctly – neubert Nov 09 '21 at 00:23
  • Let's say you're doing `$key = PublicKeyLoader::load()`. Does `echo $key` output something that looks like a key? For that matter can you share your key? Or more specifically, can you create a new key that duplicates the problem? As is it's really hard to say anything definitively without that. Like one possibility I can think of off the top of my head: the key is an encrypted OpenSSH private key, which isn't supported by phpseclib as discussed at https://phpseclib.com/docs/publickeys#supported-key-formats – neubert Nov 09 '21 at 12:15
  • Inserted entire history in my question as brief as possible, still not working.. – DevelJoe Nov 10 '21 at 13:03
  • 1
    If you're using OpenSSH I'd try to restart the SSH server with debug mode and see what output that gets. eg. `sudo /usr/sbin/sshd -ddd -p2222`. Beyond that... in-so-far as anyone here knows the SSH server could be discarding the key because it's not from a whitelisted IP address. Normally you'd restrict IP addresses by using iptables or some such, which would prevent you from even connecting, but tbh idk if you're even using OpenSSH and who knows what the capabilities of other SSH servers are – neubert Nov 11 '21 at 11:24
  • You also didn't provide the _full_ logs. If you had posted the _full_ logs I'd be able to see what the SSH server was but you instead stripped them down to what _you_ thought were the most relevant. If you did so to make for a more concise post then I'd suggest copy / pasting the logs to pastebin.com. If you stripped the logs down to preserve privacy then (1) you're sending the public key - not the private key. It wouldn't be public key crypto if the private key was sent. – neubert Nov 11 '21 at 11:28
  • 3
    If you'd be more comfortable sharing data with the phpseclib author I'd suggest emailing him. terrafrost@php.net. That email is in the phpseclib source code: https://github.com/phpseclib/phpseclib/blob/3.0.11/phpseclib/Net/SSH2.php#L42 That said, my take on it is this: the issue is most likely not with phpseclib but rather with your server. Ultimately, it's your server that's refusing the login request and, as you noted, you're able to login, without issue, locally. We know what you're using for the client but precious little about the server. – neubert Nov 11 '21 at 11:30
  • @neubert thanks a lot for your inputs. Your approach about IP whitelisting on the target server appears to be particularly interesting to me, I'll check on that. And yeah well I'm not a domain expert so I was unaware about which information will be exposed if I share the complete SSH logs, like requestint client's IP addresses, etc. no clue. I'll see if I can share them here then, if the IP whitelisting won't be enough already. Would however astonish me, as the script worked from my localhost...? – DevelJoe Nov 11 '21 at 11:31
  • when you talk about "server" you refer to the client server, am I right? – DevelJoe Nov 11 '21 at 11:32
  • 2
    "_when you talk about "server" you refer to the client server, am I right?_" yah I guess. I guess we ultimatley have 3x computers. A is your local machine, B is your prod server and C is the server you're trying to SSH into (the "client server" I guess). you can connect to C from A but not from B. and if it's a "client server" i'm guessing you wouldn't be able to do `sudo /usr/sbin/sshd -ddd -p2222` to get the logs. in that scenario i'd followup with them. be like "_i can connect to your server from my localhost but not from prod; the IP addresses of each are ..._" – neubert Nov 11 '21 at 17:00
  • 1
    but as for the contents of the logs... no they're not gonna contain any sensitive info. i mean, i guess they'd contain your username. idk if that's sensitive or not. they'd contain your public key but public keys are, by definition, designed to be seen by the public. the whole point of public key crypto is that _anyone_ can see the public key and it's not a huge deal. the IP address wouldn't be included either. only possibly sensitive thing would be the username. if you don't want that exposed i could walk you through how to remove it prior to posting it – neubert Nov 11 '21 at 17:03

1 Answers1

2

So what happened is that I've checked (among blocked IPs etc) on the target server on which port it listens for SFTP connections. And I noticed that it A) was not listening at the standard port and B) blocked incoming connection requests on the standard port.

Weirdly, when run from the local machine, the SSH connection could be established without any problem at all, without specifying any port, hence using the standard port, all the time.

In the script deployed on the server, the SSH connection could exclusively be established if the connection request was made on the port the server was configured to listen for SFTP.

Even more interestingly, the script run on the localhost fails in the way the deployed script failed when I specify the non-standard port in it.

So in the end, no idea why this happens, but it's definitely all confirmed with tests.

Dharman
  • 30,962
  • 25
  • 85
  • 135
DevelJoe
  • 856
  • 1
  • 10
  • 24