PHP CLI recording output from interactive SSH session - ssh

I've been trying to sort out recording an interactive SSH session via a PHP CLI script. All I really want is sort of like a tee command, where the entire output is recorded into a string.
I've tried most of the commands to execute external processes to get it to work. They either work ok such as passthru and pcntl_exec but don't let me record the output into a string or array, or they don't display any output to my terminal but still accept stuff from STDIN.
I'm wondering if there is a way of having an interactive ssh session i.e, works normally, but actually records the output for use in PHP.
Thanks

<?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:~$');
?>
You could do something like that.

They either work ok such as passthru
and pcntl_exec but don't let me record
the output into a string or array
Well, you can use a hack to get that string:
ob_start();
passthru('Your command here');
$result=ob_get_clean();
It is bad, but if your server is not high-load - it should work.

Try using ObserveIT, it includes a great SSH and Telnet Recorder that cannot be stopped by powerusers

Related

Automatic answer (input) in PuTTY

Is there any way to provide answers (input) to commands executed in PuTTY?
Lets say I have a PuTTY session named TEST and I want to start the session with some commands stored in putty.cmd file. After triggering some functions it expects an option to select, so how can I automate the response
Ex: When I trigger phone command it gives option like
Initiate a call
Send SMS
Quit
I have to select an option.
putty.cmd has just one line
./phone
echo 2 (this didn't enable option 2)
Is there any way to select that option?
For automation, use Plink (from PuTTY package). It's a console equivalent of PuTTY. So it supports input redirection:
(
echo ./phone
echo 2
) | plink username#example.com
Though the correct option would be to find out, if the command (phone) accepts some arguments/switches to select the options you want to use.

Not able to establish Oracle SQL session from within a BASH script

#!/bin/bash
#Oracle DB Info for NEXT
HOST="1.2.3.4"
PORT="5678"
SERVICE="MYDB"
DB_USER=$(whoami)
DB_PASS=$(base64 -d ~/.passwd)
DB_SCHEMA="my_db"
#Section for all of our functions.
function SQLConnection(){
sqlplus "$DB_USER"/"$DB_PASS"#"$HOST":"$PORT"/"$SERVICE"
}
function Connected(){
SQLConnection <<EOF
select sys_context('USERENV','SERVER_HOST') from dual;
EOF
}
function GetJMS(){
SQLConnection <<EOF
set echo on timing on lines 200 pages 100
select pd.destination from ${DB_SCHEMA}.pd_notification pd where pd.org_id = '$ORGID';
EOF
}
TODAY=$(date +"%A %B %d, %Y")
read -r -p $'\n\nWhat is the ORG ID? ' ORGID
read -r -p $'\n\nWhat is the REMOTE QUEUE MANAGER NAME? ' RQM
read -r -p $'\n\nWhat is the IP address of the REMOTE QUEUE MANAGER? ' CONN
read -r -p $'\n\nWhat is the PORT of the REMOTE QUEUE MANAGER? ' PORT
echo -en "* $(whoami)\n* $TODAY\n* MQ Setup $ORGID\n\nDEFINE +\n\tCHANNEL('$RQM.LQML') +\n\tCHLTYPE(SDR) +\n\tCONNAME('$CONN($PORT)') +\n\tXMITQ('BUF.2.$ORGID.XMQ')\n\tCHAUTH(TLS_RSA_WITH_AES_256_CBC_SHA256)\n\nDEFINE +\n\tCHANNEL('LQML.$RQM') +\n\tCHLTYPE(RCVR) +\n\tTRPTYPE(TCP)\n\nDEFINE +\n\tQLOCAL('$RQM') +\n\tTRIGDATA('LQML.$RQM') +\n\tINITQ('SYSTEM.CHANNEL.INITQ') +\n\tTRIGGER USAGE(XMITQ)\n\n" > ~/mqsetup.mqsc
CONNECTED=$(Connected | awk 'NR==16')
echo -en "\n\nHello From: $CONNECTED\n\n"
for JMSDESTINATION in $(GetJMS | awk 'NR>=16&&NR<=24{print $1}')
do
read -r -p $'\n\nWhich REMOTE QUEUE NAME matches with this ${JMSDESTINATION}?' RNAME
QDESC=$(echo "$JMSDESTINATION" | tr '.' ' ' | tr '[[:upper:]]' '[[:lower:]]')
echo -en "\n\nDEFINE +\n\tQR($JMSDESTINATION) +\n\t\tREPLACE DESCR('$ORGID $QDESC Queue') +\n\t\tREPLACE MAXDEPTH(5000) +\n\t\tXMITQ('BUF.2.$ORGID.XMQ') +\n\t\tRNAME('$RNAME') +\n\t\tRQMNAME('$RQM')" >> ~/mqsetup.mqsc
done
Here is the script I've built, hoping to automate the setup of IBM MQ Queues and Channels. My problem is that outside this script, I can establish an SQL Session without an issue, directly from the shell, provided I input the variables seen in the script. I can call the functions and everything returns just as I'd hope it would. When I run the exact same things from within the script, I get timeout errors ... the "Hello From" is blank, which tells me there is no DB connection.
I'm totally stumped as to why it all works great from outside the script, but inside it times out.
I appreciate the eyes and the help!
You're overwritng a variable value. You have this at the top of the script:
PORT="5678"
but then later on you do:
read -r -p $'\n\nWhat is the PORT of the REMOTE QUEUE MANAGER? ' PORT
which overwrites your 5678 value with whatever is entered there. That port may not be listening on the DB server at all, or may be doing something else, or if you don't enter a value it'll default to port 1521 when you connect. But either way the connection is going to fail, either quickly or slowly depending on the port state (e.g. slower maybe if a firewall blocks it).
If you test the connection by adding a Connected call before the read calls (as I initially did) then it seems to be working fine; but the connections after the reads don't work because port value it tries to connect to is now wrong.
Use a different name for the two variables, e.g. RQ_PORT for the second one - both in its read command and the subsequent creation of the ~/mqsetup.mqsc file.
You may also find it useful to add the -l flag to your SQL*Plus call so that if the connection fails for some reason it won't re-prompt for credentials, which in some circumstances can make the script appear to hang until you hit enter a few times.
Not directly relevant to the problem, but when automating anything like this I usually also use the -s flag to suppress the banners (which can vary between environments); and if you're only interested in capturing query output I'd usually set headings and/or pagination off, and feedback off, and generally set SQL*Plus up to generate as little noise as possible - it makes parsing out the interesting bits easier.

proc_open interaction

Here's what I'm trying to achieve: open a shell (korn or bash, doesn't matter), from that shell, I want to open a ssh connection (ssh user#host). At some point it is likely to happen I will be prompted for either a password or I might be asked whether or not I'm sure I want to connect (offending keys).
Before anyone asks: yes, I am aware there is a plugin for ssh2 exec calls, but the servers I'm working on don't support it, and are unlikely to do so.
Here's what I've tried so far:
$desc = array(array('pipe','r'),array('pipe','w'));//used in all example code
$p = proc_open('ssh user#host',$desc,$pipes);
if(!is_resource($p)){ die('#!#$%');}//will omit this line from now on
sleep(1);//omitting this,too but it's there every time I need it
Then I tried to read console output (stream_get_contents($pipes[1])) to see what I have to pass next (either password, yes or return 'connection failed: '.stream_get_contents($pipes[1]) and proc_close $p.
This gave me the following error:
Pseudo-terminal will not be allocated because stdin is not a terminal.
So, I though ssh was called in the php:// io-stream context, seems a plausible explanation of the above error.
Next: I though about my first SO question and decided it might be a good idea to open a bash/ksh shell first:
$p = proc_open('bash',$desc,$pipes);
And take it from there, but I got the exact same error message, only this time, the script stopped running but ssh did run. So I got hopeful, then felt stupid and, eventually, desperate:
$p=proc_open('bash && ssh user#host',$desc,$pipes);
After a few seconds wait, I got the following error:
PHP Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 133693440 bytes)
The Call Stack keeps bringing up the stream_get_contents line, even in my last desperate attempt:
#!/path/to/bin/php -n
<?php
$p = proc_open('bash && ssh user#host',array(array('pipe','r'),array('pipe','w')),$ps);
if (!is_resource($p))
{
die('FFS');
}
usleep(10);
fwrite($ps[0],'yes'."\n");
fflush($ps[0]);
usleep(20);
fwrite($ps[0],'password'."\n");
fflush($ps[0]);
usleep(20);
fwrite($ps[0],'whoami'."\n");
fflush($ps[0]);
usleep(2);
$msg = stream_get_contents($ps[1]);
fwrite($ps[0],'exit'."\n");
fclose($ps[0]);
fclose($ps[1]);
proc_close($p);
?>
I know, its a mess, a lot of fflush and redundancy, but the point is: I know this connection will first prompt me for offending keys, and then ask a password. My guess is the stream in $pipes[1] holds the ssh connection, hence it's content is huge. what I need then, is a pipe inside a pipe... is this even possible? I must be missing something, what good is a pipe if this isn't possible...
My guess is the proc_open command is wrong to begin with, (error: Broken pipe). But I really can't see any other way around the first error... any thoughts? Or follow up questions if the above rant isn't at all clear (which it probably isn't).
Before anyone asks: yes, I am aware there is a plugin for ssh2 exec
calls, but the servers I'm working on don't support it, and are
unlikely to do so.
There are actually two. The PECL module, which is a PITA that most servers don't have installed anyway and phpseclib, a pure PHP SSH2 implementation. An example of its use:
<?php
include('Net/SSH2.php');
$ssh = new Net_SSH2('www.domain.tld');
if (!$ssh->login('username', 'password')) {
exit('Login Failed');
}
echo $ssh->exec('pwd');
echo $ssh->exec('ls -la');
?>

More efficient way of looping over SSH in KSH?

I currently have the following lines of code in a script:
set -A ARRAY OPTION1 OPTION2 OPTION3 OPTION4
set -A matches
for OPTION in ${ARRAY[#]}; do
DIFF=$(ssh $USER#$host " diff $PERSONALCONF $PRESETS$OPTION" )
if [[ $DIFF == "" ]]; then
set -A matches"${matches[#]}" $OPTION
fi
done
Basically, I have a loop that goes through each element in a pre-defined array, connects to a remote server (same server each time), and then compares a file with a file as defined by the loop using the diff command. Basically, it compares a personal.conf file with personal.conf.option1, personal.conf.option2, etc. If there is no difference, it adds it to the array. If there is a difference, nothing happens.
I was wondering if its possible to execute this or get the same result (storing the matching files in an array ON THE HOST MACHINE, not the server that's being connected to) by way of only connecting once via SSH. I cannot store anything on the remote server, nor can I execute a remote script on that server. I can only issue commands via ssh (kind of a goofy setup). Currently, it connects as many times as there are options. This seems inefficient. If anyone has a better solution I'd love to hear it.
Several options:
You can use OpenSSH multiplexing feature (see ssh(1)).
Also, most shells will gladly accept a script to run over stdin, so you could just run something like
cat script.sh | ssh $HOST /bin/sh
Most scripting languages (Perl, Python, Ruby, etc.) have some SSH module that allows connection reuse:
#!/usr/bin/perl
use Net::OpenSSH;
my ($user, $host) = (...);
my #options = (...);
my #matches;
my $ssh = Net::OpenSSH->new("$user\#$host");
for my $option (#options) {
my $diff = $ssh->capture("diff $personal_conf $presets$option");
if ($ssh->error) {
warn "command failed: " . $ssh->error;
}
else {
push #matches, $option if $diff eq '';
}
}
print "#matches\n";

Creating a script for a Telnet session?

Does anyone know of an easy way to create a script that can connect to a telnet server, do some usual telnet stuff, and then log off? I am dealing with users who are not familiar with telnet and the commands they will need to run. All I want is for them to double-click on a script, and have that script automatically execute the commands for them.
You're probably wondering, "What platform are the users on?" They will be on both Windows and Linux. Implementations in languages like Perl, Java, or Python are acceptable. I see that Perl has a Net:: Telnet module. Has anyone used that?
My ideal solution would be to create two script files. a BAT file for windows, and a shell script for Linux. While this would make dual maintenance an issue, it would mean I wouldn't have to install Perl/Java/Python/etc... on every machine. Unfortunately, I have not seen any way to automate a telnet session with batch files or shell scripts.
Thanks.
I've used various methods for scripting telnet sessions under Unix, but the simplest one is probably a sequence of echo and sleep commands, with their output piped into telnet. Piping the output into another command is also a possibility.
Silly example
(echo password; echo "show ip route"; sleep 1; echo "quit" ) | telnet myrouter
This (basically) retrieves the routing table of a Cisco router.
Expect is built for this and can handle the input/output plus timeouts etc. Note that if you're not a TCL fan, there are Expect modules for Perl/Python/Java.
EDIT: The above page suggests that the Wikipedia Expect entry is a useful resource :-)
Another method is to use netcat (or nc, dependent upon which posix) in the same format as vatine shows or you can create a text file that contains each command on it's own line.
I have found that some posix' telnets do not handle redirect correctly (which is why I suggest netcat)
This vbs script reloads a cisco switch, make sure telnet is installed on windows.
Option explicit
Dim oShell
set oShell= Wscript.CreateObject("WScript.Shell")
oShell.Run "telnet"
WScript.Sleep 1000
oShell.Sendkeys "open 172.25.15.9~"
WScript.Sleep 1000
oShell.Sendkeys "password~"
WScript.Sleep 1000
oShell.Sendkeys "en~"
WScript.Sleep 1000
oShell.Sendkeys "password~"
WScript.Sleep 1000
oShell.Sendkeys "reload~"
WScript.Sleep 1000
oShell.Sendkeys "~"
Wscript.Quit
It may not sound a good idea but i used java and used simple TCP/IP socket programming to connect to a telnet server and exchange communication. ANd it works perfectly if you know the protocol implemented. For SSH etc, it might be tough unless you know how to do the handshake etc, but simple telnet works like a treat.
Another way i tried, was using external process in java System.exec() etc, and then let the windows built in telnet do the job for you and you just send and receive data to the local system process.
Check for the SendCommand tool.
You can use it as follows:
perl sendcommand.pl -i login.txt -t cisco -c "show ip route"
import telnetlib
user = "admin"
password = "\r"
def connect(A):
tnA = telnetlib.Telnet(A)
tnA.read_until('username: ', 3)
tnA.write(user + '\n')
tnA.read_until('password: ', 3)
tnA.write(password + '\n')
return tnA
def quit_telnet(tn)
tn.write("bye\n")
tn.write("quit\n")
Couple of questions:
Can you put stuff on the device that you're telnetting into?
Are the commands executed by the script the same or do they vary by machine/user?
Do you want the person clicking the icon to have to provide a userid and/or password?
That said, I wrote some Java a while ago to talk to a couple of IP-enabled power strips (BayTech RPC3s) which might be of use to you. If you're interested I'll see if I can dig it up and post it someplace.
I like the example given by Active State using python. Here is the full link. I added the simple log in part from the link but you can get the gist of what you could do.
import telnetlib
prdLogBox='142.178.1.3'
uid = 'uid'
pwd = 'yourpassword'
tn = telnetlib.Telnet(prdLogBox)
tn.read_until("login: ")
tn.write(uid + "\n")
tn.read_until("Password:")
tn.write(pwd + "\n")
tn.write("exit\n")
tn.close()
Bash shell supports this out-of-box, e.g.
exec {stream}<>/dev/tcp/example.com/80
printf "GET / HTTP/1.1\nHost: example.com\nConnection: close\n\n" >&${stream}
cat <&${stream}
To filter and only show some lines, run: grep Example <&${stream}.
Write the telnet session inside a BAT Dos file and execute.