SSH2 in PHP to connect to remote server - php

I'm using the following code to connect locally using SSH2, but I'm trying to work out how public/private keys are handled when connecting to a remote server.
$SSH_CONNECTION = ssh2_connect('localhost', 22, array('hostkey'=>'ssh-rsa'));
ssh2_auth_pubkey_file($SSH_CONNECTION, 'username','/path/to/id_rsa.pub','/path/to/id_rsa')
If I'm connecting to a remote server, does there need to be a key file (public or private?) on the remote server, and how do I reference it? I'm not sure if the code is the same or exactly how it works.
Appreciate any explanation.

In order for the remote server to accept the keys, you need to put a copy of the public key in a file called authorized_keys in the .ssh directory in the home folder of the user you are logging in as.
So to authenticate as bob on a remote server, you would have a file called authorized_keys in /home/bob/.ssh on the remote with the public key in it (e.g. ssh-rsa AAAA..<long-string-of-text>..cXrTp bob#host) (you can have more than one authorized keys, each one goes on it's own line in the file).
The id_rsa and id_rsa.pub files need to be on the client system where you call ssh2_auth_pubkey_file and readable by your PHP script.
This article about SSH Keys gives a good explanation about how to generate a keypair for key based authentication and how you can transfer the key to the host as well.
Without the public key in the authorized_keys file for the user you are trying to authenticate as, the authentication will not work.
Also, be sure to take the necessary security precautions by protecting the private key with a passphrase, making the private key unreadble by other users on the system, and protecting the passphrase in your script or controlling access to the file your script will read it from if it will be stored.

Related

Why does PHP's ssh2_auth_pubkey_file require a public key?

I can log into a server using only a private key via command line. Why does this PHP function require a public key also?
$connection = ssh2_connect($server_address, $port, array('hostkey'=>'ssh-rsa'));
if (!#ssh2_auth_pubkey_file($connection, $username, $public_key_path, $private_key_path, $password))
{
echo '<h3 class="error">Unable to authenticate. Check ssh key pair.</h3>';
break;
}
echo '<h3 class="success">Authenticated.</h3>';
I am working on a personal use test script to check firewall settings and access permissions as I adjust and deploy new servers. I'm mostly just curious as this seems to indicate I am missing some information about how ssh works. But I'm also annoyed that I have to give two paths when it seems I should only need one.
I do not have a direct experience with PHP SSH2 functions. But PHP ssh2_auth_pubkey_file internally calls libssh2_userauth_publickey_fromfile_ex from libssh2, whose documentation says about the publickey parameter:
Path name of the public key file.
(e.g. /etc/ssh/hostkey.pub). If libssh2 is built against OpenSSL, this option
can be set to NULL.
So maybe you can pass null in PHP (as PHP builds against OpenSSL). If not, it's only a limitation of PHP SSH2 API. Not a something that comes intrinsically from SSH as such.
For the reason why SSH APIes usually allow specifying a separate public key file, when key-pair file (usually not-really-correctly called private key file) is enough, see my answer to:
Purpose of pubkey parameter of JSch.addIdentity
I believe that given the PHP SSH API, the argument is rather useless, as the API does not even allow you to have multiple keys loaded and you have to specify the passphrase upfront anyway.
When you "log on to a web site," you always present the public key.
The corresponding private key should be very-securely kept on the server so that it can use it to validate the public keys that are presented. Private keys should never be "out in the wild."
Alternatives exist – often, the server only contains a "signing key" (for self-issued certificates), or it simply relies on the fact that the presented key has been signed by a recognized authority.

How to tell if phpseclib sftp response is a challenge with a password request when key doesn't match

Recently someone inadvertently changed the keyfile used for my ssh/sftp to a remote server. I deduced this when I tried to ssh to the server from the command line and I got challenged with a password request, which indicated that the key was no longer recognised.
How would I make my php program detect an unexpected password challenge? Currently I have this:
$sftp = new SFTP(self::DOMAIN_NAME);
$Key = new RSA();
$private_rsa_key = file_get_contents('/home/ddfs/.ssh/' . self::KEY_FILE);
$Key->loadKey($private_rsa_key);
$rc = $sftp->login(self::USER, $Key);
$errors = $sftp->getSFTPErrors();
At the moment I see $rc is set to FALSE and $errors is an empty array.
SSH initiated password change requests
SSH has a mechanism built into it for password resets. My reading of RFC4252 § 8 implies that SSH_MSG_USERAUTH_PASSWD_CHANGEREQ packets should only be sent in response to a "password" SSH_MSG_USERAUTH_REQUEST but who knows how the OpenSSH devs interpreted that section of the RFC.
Since you're doing public key authentication phpseclib would be sending a "publickey" SSH_MSG_USERAUTH_REQUEST so it seems like SSH_MSG_USERAUTH_PASSWD_CHANGEREQ wouldn't be a valid response, but again, who knows.
If the server did respond with a SSH_MSG_USERAUTH_PASSWD_CHANGEREQ packet than you could do $sftp->getErrors() (instead of getSFTPErrors) and look for one that starts with SSH_MSG_USERAUTH_PASSWD_CHANGEREQ:. Maybe even do $sftp->getLastError().
getSFTPErrors returns errors with the SFTP layer - not the SSH2 layer. SFTP as a protocol doesn't know about authentication - that's handled entirely by the SSH layer. ie. it's not SFTP errors you'd want to look at but SSH errors.
Reference code: https://github.com/phpseclib/phpseclib/blob/1.0.7/phpseclib/Net/SSH2.php#L2219
Other possible password request mechanisms
It's possible that password request isn't coming from SSH's built-in authentication mechanism. It's possible you're getting a SSH_MSG_USERAUTH_SUCCESS response from the "publickey" SSH_MSG_USERAUTH_REQUEST.
At this point I can see two possibilities:
It could be a banner message that you're seeing. You can get those by doing $sftp->getBannerMessage().
It's possible you're only seeing this error when you SSH into the server as opposed to SFTP'ing into it. ie. it's possible you wouldn't see the error unless you did $ssh->exec() or $ssh->write(). At this point the "error" could be communicated to you via stderr or stdout.
To know for sure I'd have to see the SSH logs. The phpseclib logs may or may not be sufficient. I mean you could do $sftp->exec('pwd'); or $sftp->read('[prompt]'); but my guess is that you're not already doing that. If you wanted to go that route you could do define('NET_SSH2_LOGGING', 2); and then echo $sftp->getLog() after you do either $sftp->exec() or $sftp->read().
The PuTTY logs might be more useful. To get them you can go to PuTTY->Session->Logging, check the "SSH packets" radio button and then connect as usual.
Unfortunately, OpenSSH does not, to the best of my knowledge, log the raw / decrypted SSH2 packets so OpenSSH isn't going to be too useful here.

Deployment using ssh with key without providing passphrase for private key (ssh-agent)

Wherein lies the difference between Capistrano and Rocketeer when it comes to the passphrase for a private key?
I already have both Capistrano and Rocketeer deployment strategies set up properly and working. Capistrano lets ssh-agent provide the passphrase - Rocketeer, as it seems, does not. The question is not about how but why the passphrase is needed.
Background:
I want to use Rocketeer for deployment of a Laravel application instead of Capistrano. It seems as if it delegates the SSH connection to Laravel.
After setting only the remote server's name in the configuration and running a check, after some prompts for credentials Rocketeer stores the needed passphrase and the path to my desired private key in a non-version-controlled file.
I do not want to have credentials for establishing a SSH connection stored on my disk - especially not the passphrase to any of my private keys.
So, why is anything more than the server's name required?
I see that Laravel has those fields prepared in its remotes config - I just could not find out which component is responsible eventually and why it does not leave the SSH connection completely to the system itself.
Is it Rocketeer, Laravel, Symfony, phpseclib or even php itself underneath that needs that many information for establishing a SSH connection?
It's Laravel's missing implementation of phpseclib's ssh-agent that requires that many information for establishing a SSH connection.
That's why Rocketeer does not allow to rely on the ssh-agent next to username/password and privatekey/passphrase authentication as does Capistrano.
A proposal was stated and merged to include phpseclib's undocumented implementation for using the ssh-agent instead of an explicit key.
Rocketeer would profit from this as it relies on said implementation of phpseclib in Laravel.
(Thanks to #hannesvdvreken, #ThomasPayer and #passioncoder for pointing in the right directions)
There are some thing you might want to know.
You can use the default app/config/remote.php or you can use the Rocketeer config.php that gets published under app/packages/anahkiasen/rocketeer.
I tend to use the Laravel file. I made a copy of that file into the app/config/development folder which is ignored by git with .gitignore. I only write down the passkey of my private key down in that file. It will get merged with the array in app/config/remote.php.
Here's my app/config/development/remote.php file:
return array(
'connections' => array(
'staging' => array(
'keyphrase' => 'your-secret-here',
),
'production' => array(
'keyphrase' => 'your-secret-here',
),
),
);
Hope this helps.

how to open and write texfile to remote server's specific location in PHP

I am currently opening and writing a text file into my local server with the following:
$mypath="sms_file\\cbsms_";
$fp = fopen($file_name.'.txt', "w");
fwrite($fp, $value. "\r\n");
fclose($fp);
I want to now copy that file to a remote server like /home/project on 10.10.18.23 (home network)
Assuming that I have R/W access in that directory, what would be the best way of achieving this?
The remote server needs to know that there is a request coming in to store a file on it. There are several possibilities here, the easiest would be to run a FTP server.
Another option would be to use the exec() function call scp on the command line (provided you have exchanged ssh keys with the remote server).
Another option would be to create a PHP page on the remote server that accepts POST requests with files and stores them. You must provide your own security measures in this case.
If you can mount the remote host as a permanent volume (via NFS or CIFS), you can use the regular PHP copy() function.
You can try using exec() to run an SCP command:
exec('scp /path/to/file.txt user#homenetworkhost:/home/project/file.txt');
// Obviously, you'll have to set up your SSH permissions and for 'user#homenetworkhost' you'll want to change it to your home network's user and host names.
By the looks of your exmample, I would say your PHP server is on Windows (looking at the backslash in $mypath="sms_file\\cbsms_";), and your remote host is UNIX/LINUX (looking at forward slashes and location /home/project). I would suggest setting up SSH or FTP on the remote host and rather use those protocols than copying it to a network location. Your PHP server (Windows box) will then have to communicate via SSH/FTP and copy the file.
References:
http://php.net/manual/en/book.ftp.php
http://php.net/manual/en/function.ssh2-scp-send.php

Does a capistrano deploy.rb file contain server credentials?

This is my first time using Capistrano and I am getting server authentication errors right at the start of my deploy:setup stage. I am a PHP user using rvm on a mac.
I noticed my deploy.rb file does not contain the password to my server. It only contains the password to my private git repo. Is there an attribute available for setting the server password so my connection could authenticate?
Do deploy.rb files list server credentials?
I'd like to refer you to a related discussion. Point in case: It's better to setup publickey authentication for your servers, it saves you from having your credentials stored in plain text and it is safer to begin with.
If you use github for your git hosting, you can use your publickey there as well. Be sure to use ssh_options[:forward_agent] = true to forward your publickey to the server when deploying.
If you really want to set your user and password, I believe you can do it as follows:
set :user, "sshuser"
set :password, "sshpassword"
set :scm_passphrase, "gitpassword"
More info can be found at github help/capistrano
The previous answer covers good info about deploy and i agree it is better to setup public keys.
But if you have password issues, try to add this line:
default_run_options[:pty] = true
to your deploy.rb file, so you allow Capistrano to prompt for passwords.
#Amit Erandole (in reply to [ip_address_omitted] (Net::SSH::AuthenticationFailed: root), app_name_ommitted (Errno::ETIMEDOUT: Operation timed out - connect(2)):
Looks like root access over ssh is not allowed on the server (and generally not recommended). Try it again with a valid user or turn root access on in sshd_config (PermitRootLogin yes).
But as was already mentioned by HectorMalot, create an ssh-key and forget about the passwords. ;)

Categories