4

I'm trying to establish an interactive SSH connection to a remote server using PHP via the command line on Mac OS X 10.6. I'm currently using PHP's proc_open function to execute the following command:

ssh -t -t -p 22 user@server.com

This almost works. The -t -t options are supposed to force a pseudo terminal which they almost do. I am able to enter the SSH password and press enter. However, after pressing enter the terminal appears to simply hang. No output, no nothing - it's as if the SSH session has failed. I can't run commands or anything and have to kill the whole thing using Ctrl+C. I know the login is successful because I can execute a command like ssh -t -t -p 22 user@server.com "ls -la" and get the correct output.

I thought the problem must be related to the fact that I was using standard pipes in my proc_open call, so I replaced them with pty. I get the following error: "pty pseudo terminal not supported on this system..."

Does Mac OS X simply not support pty or pseudo terminals? (I'm pretty new at using all this shell terminology).

Here's the PHP code:

$descriptorspec = array(0 => array("pty"), 1 => array("pty"), 2 => array("pty"));  
$cwd = getcwd();  
$process = proc_open('ssh -t -t -p 22 user@server.com', $descriptorspec, $pipes, $cwd);  
if (is_resource($process))  
{  
    while (true)  
    {  
        echo(stream_get_contents($pipes[1]));  
        $status = proc_get_status($process);  
        if (! $status["running"])  
            break;  
    }  
} 

(Sorry - cannot for the life of me figure out SO's formatting instructions...)

What am I doing wrong? Why can't I use pty? Is this just impossible on Mac OS X? Thanks for your help!

BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
Cameron
  • 641
  • 1
  • 7
  • 18
  • I've fixed the formatting for you. To format code blocks, simply add four spaces to the beginning of each line of code. To format inline code samples use backticks `\``. – BoltClock Nov 05 '10 at 08:00
  • If you specify your reason for opening SSH from PHP, maybe we can find other ways to accomplish what you are trying to do. – Halil Özgür Nov 05 '10 at 09:20
  • I'm trying to open an SSH connection in PHP because I'm working on a deployment framework that will automate deploying code to a remote server, kind of like Capistrano does. It's overarching goal is to be able to check code out of a Subversion/Git/Bazaar repository and automate other tasks. – Cameron Nov 05 '10 at 16:02
  • 3
    @battal - He doesn't need a reason, he just wants to do it. Stop with this mentality of having to know each little darn unrelated detail to answer a question. – Christian Jan 23 '11 at 22:45

6 Answers6

5

You should use public key authentication rather than trying to programmatically bypass interactive password authentication.

The password prompt is supposed to be used from a tty and I believe it was made intentionally difficult to use otherwise. Also the -t -t argument only takes effect once you are connected to the remote host. And I don't believe the PHP function proc_open() can run a command inside a virtual terminal.

To setup public key authentication:

# Generate keypair
ssh-keygen -t rsa

# Copy public key to server
scp ~/.ssh/id_rsa.pub example.com:.ssh/authorized_keys

# Now you shouldn't be prompted for a password when connecting to example.com
# from this host and user account.
ssh example.com

# Since the web server (and thus PHP) probably has its own user account...
# Copy the ~/.ssh/id_rsa file somewhere else
cp ~/.ssh/id_rsa /some_path/id_rsa

# Change ownership of the file to the web server account
chown www-data:www-data /some_path/id_rsa

# Fix the file permissions (ssh ignore the keyfile if it is world readable)
chown 600 /some_path/id_rsa

# Try connecting to the server through the web server account
su -c "ssh -i /some_path/id_rsa -o UserKnownHostsFile=/some_path/known_hosts example.com" www-data

# Add the host to the known hosts file when prompted


Alternately, you could use plink (part of PuTTY for Linux) instead of OpenSSH as it can take the password on the command line plink -pw password example.com. But doing so presents a security risk as anyone who runs ps aux on the server can see the password in the process list.

There is also a program called sshpass that takes the password from an environment variable or command argument and passes it to ssh.

Stacey Richards
  • 6,536
  • 7
  • 38
  • 40
Alex Jasmin
  • 39,094
  • 7
  • 77
  • 67
  • I wouldn't recommend the last option, having the password visible in the command. Anyone who runs `ps aux` on the server can see your password. – Lekensteyn Nov 05 '10 at 09:39
  • Thanks Alexandre, but this goes beyond just needing a password to login. I would also like to be able to offer an interactive session to the users of my framework so they can respond to whatever prompts they might receive from the server. Public key logins are great, just not for this purpose. I'm just using the SSH login as an example. – Cameron Nov 05 '10 at 16:06
  • @Cameron The password prompt is *special* it is not displayed by the remote computer. Once you are connected you should be able to interact with the shell using `$descriptorspec = array(0 => array("pipe", "r"), 1 => array("pipe", "w"), 2 => => array("pipe", "w"));` in PHP and non-blocking IO. The big limitation though is that tha PHP resources such as pipes can't be serialized in the session (i.e. kept between http requests) so a web based terminal emulator for instance is not doable. – Alex Jasmin Nov 05 '10 at 18:01
  • That's fine Alexandre - I'm not trying to serialize them in a session, in fact the whole project is command-line based. The problem is that my terminal on Mac OS X refuses to create a pseudo terminal even if I use "pty" in place of "pipe". – Cameron Nov 05 '10 at 21:25
  • @Cameron You can't use `'pty'` with `proc_open()` there's some code for this in the PHP source but if you take a look at [proc_open.c line 65](http://lxr.php.net/opengrok/xref/PHP_5_3/ext/standard/proc_open.c#65) you can see that code is disable by an `#if 0` so it's never built. – Alex Jasmin Nov 05 '10 at 21:49
  • .... but if you pass `-t -t` to `ssh` I don't think you need ptys on the PHP side at all. Your only obstacle should be the initial password prompt. I edited my post to add `sshpass` as another alternative. – Alex Jasmin Nov 05 '10 at 22:21
  • Ok, thanks Alexandre. I had forgotten the third element of the descriptorspec array (the one for the stderr stream) and it works more effectively now. I can enter the SSH password interactively and it appears to log in. However, I can't write new commands to stdin for some reason. I've noticed that PHP's system() function does everything I need except providing access to stdin/out. Is there a way to use system() or should I stick with proc_open()? – Cameron Nov 06 '10 at 06:34
  • If your script has to manipulate stdin/out/err you will have to use `proc_open()`. Ideally you would just execute remote commands like `ssh host command` with `system()` but it seems your requirements are more complex... You could also use `popen()` if you only need the input or output. – Alex Jasmin Nov 06 '10 at 06:46
4

It looks like the problem is best solved using PHP's passthru() function. After alot more (rather painful) research I was able to issue a command through this function and could interact with the remote server through the terminal as if I had run ssh and svn export by hand (they both require passwords, therefore were good tests). What I'm going to have to do is construct a (potentially very long) string of commands separated by && and attach them to the end of the ssh command: ssh -t -t -p 22 hostname command1 && command2 ... The output will be sent to my terminal in Mac OS X even though the commands are being executed on the remote server. Looks like this is the solution I was looking for the whole time - pretty simple really! Thanks to everyone who helped me with this. I gave Alexandre the "green checkmark" because he was the only one who kept responding and was quite helpful in deducing the final answer to the problem. Thanks Alexandre!

Cameron
  • 641
  • 1
  • 7
  • 18
3

This is old, but for any googlers out there, here is an actual solution using proc_open:

Pty descriptors are available in PHP, but have to be configured during compilation (see this 10yr old bug report https://bugs.php.net/bug.php?id=33147)

But in python however, we don't have that problem. So instead of running the ssh command directly, run this python script:

import sys
import pty

args = " ".join(sys.argv[1:])
pty.spawn(['/usr/bin/ssh', args])

About pty.spawn from python docs:

Spawn a process, and connect its controlling terminal with the current process’s standard io. This is often used to baffle programs which insist on reading from the controlling terminal.

  • 1
    This works beautifully, thanks. I used it by saving your snippet as `ssh.py` then in my php `proc_open('python ssh.py username@host', $descriptorSpec, $pipes)` – Daniel Howard Sep 17 '18 at 22:34
1

Have you tried phpseclib, a pure PHP SSH implementation?:

<?php
include('Net/SSH2.php');

$ssh = new Net_SSH2('www.domain.tld');
if (!$ssh->login('username', 'password')) {
    exit('Login Failed');
}

echo $ssh->read('username@username:~$');
$ssh->write("ls -la\n");
echo $ssh->read('username@username:~$');
?>
neubert
  • 15,947
  • 24
  • 120
  • 212
1

Have you tried the PHP SSH2 extension?

Anti Veeranna
  • 11,485
  • 4
  • 42
  • 63
  • I tried the SSH2 extension to no avail. It won't provide me with an interactive session via pipes or pty. I can execute commands and get the output, but anything with input won't work (like anything requiring a password). – Cameron Nov 05 '10 at 16:04
0

I wrote a ssh client on php with ssh2 extension, you can take a look to the source code on the github page https://github.com/roke22/PHP-SSH2-Web-Client

Please send some feedback.

Roke
  • 329
  • 1
  • 3
  • 16