Pipe ssh session into and out of python - python

The company I work for uses an archaic information system (Copyright 1991-2001). The system is a Centos machine running an ssh server. There's no access to the back-end or it's data in any way. All data needs to be retrieved through text reports, or input with manual keystrokes. Here's an example of the view you get when you login.
I'm trying to write a python script that will simulate keystrokes to run reports and do trivial tasks. I've already successfully done this with a .cmd file on windows that connects and simulates keystrokes. The problem is that there are some processes that have unpredictable branches (A message sometimes pops up and asks for some information or a key press to verify that you've seen a message). I can predict where a branch might occur, but can't detect if it actually has because my .cmd file is blind to output from the ssh session. (I'm working in windows by the way)
What I'm trying to do is use a python script that uses stdin and makes decisions based on what it sees, but I'm new to how piping works. Piping into my script works, but I'm unsure how to send keystrokes back to the ssh session from the python script. Here's an example of my test script:
import sys
import time
buff=''
try:
while True:
buff += sys.stdin.read(1)
if buff[-5:] == 'press':
print('found word "press"!')
#Send a keystroke back to the ssh session here
buff = ''
except KeyboardInterrupt:
sys.stdout.flush
pass
And here's how I call it:
ssh MyUsername####.###.###.### | python -u pipe_test.py
While it's running, I can't see anything, but I've verified that I can send keystrokes through the terminal with my regular keyboard.
Any ideas on how to output keystrokes to the ssh session?
Should I be doing some completely different, much simpler thing?
FYI: The data sent by the server to the terminal has ASCII escape characters flying all over the place. It's not a nice bash interface or anything like that. Also, I've installed a bunch of Unix command line tools so that I can, for example, ssh from windows.
tl;dr How do I pipe from an ssh session into python, and send keystrokes back to the ssh session from that same python script?

You definitely don't do this with pipes. A Unix pipe is a unidirectional inter-process communications mechanism. You can send data to it, or you can read data from it. But not both (through the same pipe).
It is possible to use pairs of pipes to create co-processes. This is even supported directly in some Unix shells (such as Korn shell and Bash as of version 4 (https://www.gnu.org/software/bash/manual/html_node/Coprocesses.html). However this mechanism is somewhat fragile and prone to deadlock. It works so long as the processes on both sides of this pair of pipes are rigorous in their handling of the pipes and the associated buffering (Actually it's possible for just one end to be rigorous, but even that's tricky). This is not likely to be the case for the programs that you're trying to run remotely.
Someone suggested pexpect which is an excellent choice for controlling a locally spawned terminal or curses application. It's possible to manage a remote process with it, by spawning a local ssh client and controlling that.
However, a better choice for accessing ssh protocols and APIs from Python would be Paramiko. This implements the ssh protocol such that you access the remote sshd process as an API rather than through a client (command line utility).
An advantage of this approach is that you can programmatically manage port redirections, transfer and manage files (as you would with sftp) (including setting permissions and such), and you can execute programs and separately access their standard input, output, and error streams or their pseudo-terminals (pty) as well as fetch the exit codes of remote processes as distinct from the exit code of your local ssh client.
There's even a package paramiko-expect adds an extension to Paramiko to enable more straightforward using of these remote pty objects. (Pexpect provides similar features on the pty controlling local processes). [Caveat: I haven't used Fotis Gimian's package yet; but I've used Paramiko fairly extensively and sometimes wished I had something like it].
As you may have figured out from this answer, the complexity of programmatically dealing with an interactive terminal/text program under Unix (Linux or any of its variants) has to do with details about how that program is written.
Some programs, such as shells, can be completely driven by line oriented input and output to their standard file descriptors (stdin, stdout, and stderr). Others must be controlled through their terminal interfaces (which is accomplished by launching them using a pseudo-terminal environment ... such as the one provided by sshd when it starts a normal interactive process, and those supplied by expect (and pexect and various other modules and utilities inspired by the old TCL/expect) and by your xterm or other terminal windowing programs under any modern OS (even including Cygwin or the "Bash for Windows" or WSL (Windows Support for Linux) under the latest versions of Microsoft Windows).
In general your attempts will need to use one or another approach and pipes are only very crudely useful for the approach using the standard file descriptors. The decision of which approach to use will mostly be driven by the program you're trying to (remotely) control.

Related

Execute command on remote windows machine and get output in python

I want to write a script that executes command on remote windows machine, and get output from that command. The problem is, I want to use built-in software in windows, so I can't unfortunately use for example SSH for that. I found "wmi" library, and I can log in to the machine and execute command, but i don't know how to recieve output from that command.
import wmi
c = wmi.WMI("10.0.0.2", user="administrator", password="admin")
process_startup = c.Win32_ProcessStartup.new()
process_id, result = c.Win32_Process.Create (r'cmd /c ping 10.0.0.1 -n 1 > c:\temp\temp.txt')
if result == 0:
print("Process started successfully: %d" % process_id)
print(result)
I tried to redirect output to file, but I can't find any way to get text file content either.
Is there any possible way to get output or text file content using wmi, or other python libraries?
For the application you're describing you may find RPyC is a good fit. It's a python remoting server you can connect to and issue commands; it will do anything that Python can do on the target machine. So you could, for example, use the subprocess module to run a windows command and capture the output then return the result.
The safe thing to do is to expose your remote functionality as a service -- basically, it's just a python script that RPyC can run for you. However you can also use RPyC classic mode, which is pretty much like running a python session directly on the remote machine. You should use it only when you are not worried about security, since classic mode can do anything on the remote machine -- but it's useful for prototyping.

Python to perform SSH logins and do something

I am trying to automate few routine tasks in Netapp storage using Python.
Since I am using Windows machine and Python I need to perform few tasks to connect storage using ssh session and perfom few things and I need view how it is acting . For example if we take we are connecting Linux box over ssh if I execute ls command I need to see the output of the ls.
To achieve this which tools I need to use ..I searched in web some are suggesting to use paramiko and pexpect but these are the modules to use connect ssh and perform tasks but we cannot view the output on terminal.
With the parallel-ssh library:
from pssh import ParallelSSHClient
# See also private key examples in documentation
client = ParallelSSHClient(['netapp.host'], user=<user>,
password=<password>)
output = client.run_command(<cmd>)
for line in output['netapp.host'].stdout:
print line
See parallel-ssh documentation. It uses paramiko under the hood so should work just as well on Windows.
stdin channel is also available in output for providing input like with pexpect, see run_command documentation for examples.
As a bonus, the client is parallel and can be used to run commands on any number of hosts.
Using paramiko directly is of course also possible, though a lot more code would be required which parallel-ssh does for you.

how to check if python script is being called remotely via ssh

So I'm writing a command line utility and I've run into a problem I'm curious about.
The utility can be called with file arguments or can read from sys.stdin. Originally I was using sys.stdin.isatty() to figure out if data is being piped in, however, I found that if I call the utility remotely via ssh server utility, sys.stdin.isatty() will return false, despite the fact that there is no actual data being piped in.
As a workaround I'm using - as the file argument to force reading from stdin (eg: echo "data_here" | utility -f -), but I'm curious to know if there's a reliable way to tell the difference between a pipe getting data from a process and a pipe that's only open because the call is via ssh.
Systems programming is not my forte, so I'm grateful for any help I can get from you guys.
You can tell if you're being invoked via SSH by checking your environment. If you're being invoked via an SSH connection, the environment variables SSH_CONNECTION and SSH_CLIENT will be set. You can test if they are set with, say:
if "SSH_CONNECTION" in os.environ:
# do something
Another option, if you wanted to just stick with your original approach of sys.stdin.isatty(), would be to to allocate a pseudo-tty for the SSH connection. Normally SSH does this by default if you just SSH in for an interactive session, but not if you supply a command. However, you can force it to do so when supplying a command by passing the -t flag:
ssh -t server utility
However, I would caution you against doing either of these. As you can see, trying to detect whether you should accept input from stdin based on whether it's a TTY can cause some surprising behavior. It could also cause frustration from users if they wanted a way to interactively provide input to your program when debugging something.
The approach of adding an explicit - argument makes it a lot more explicit and less surprising which behavior you get. Some utilities also just use the lack of any file arguments to mean to read from stdin, so that would also be a less-surprising alternative.
According to this answer the SSH_CLIENT or SSH_TTY environment variable should be declared. From that, the following code should work:
import os
def running_ssh():
return 'SSH_CLIENT' in os.environ or 'SSH_TTY' in os.environ
A more complete example would examine the parent processes to check if any of them are sshd which would probably require the psutil module.

ssh session as python subprocess takes input but does not print it to stdout

I'm trying use python's cmd library to create a shell with limited commands. One requirement I have is to be able to run a command that executes an existing shell script which opens an ssh session on a remote machine and from there allows the user to interact with the remote shell as if it was a regular ssh session.
Simply using subprocess.Popen('[/path/to/connect.sh]') works well at least as a starting point except for one issue. You can interact with the remote shell but the input that you type is not shown on stdout...so for example you see the prompt on your stdout but when you type 'ls' you don't see it being typed but when you hit return it works as expected.
I'm trying to wrap my head around how to print the input to stdout and still send it along to the remote ssh session.
EDIT:
Actual code without using cmd was just the one line:
ssh_session = subprocess.Popen(['connect.sh'])
it was fired from a do_* method in a class which extended cmd.Cmd. I think I may end up using paramiko but would still be interested in anyone's input on this.
Assuming you are using a Unix like system, SSH detects if you are on a terminal or not. When it detects that you are not on a terminal, like when using subprocess, it will not echo the characters typed. Instead you might want to use a pseudo-terminal, see pexpect, or pty. This way you can get the output from SSH as if it was running on a true terminal.

RDP script in python?

I am writing a script in python, and part of it needs to connect to a remote computer using rdp. Is there a script or an api that I could use to create this function? Also, if there is not, is there a way to package a rdp application along side python and then use a python script to run it? Any help would be much appreciated. Thanks in advance, Nate
If you need an interactive window, use the subprocess module to start your rdesktop.exe (or whatever).
If you need to run some command automatically, you're probably better off forgetting about RDP and using ssh (with passwordless, passphraseless authentication via RSA or similar), psexec (note that some antivirus programs may dislike psexec, not because it's bad, but because it's infrequently been used by malware for bad purposes) or WinRM (this is what you use in PowerShell; it's like ssh or psexec, except it serializes objects on the sender, transmits, and deserializes back to an object on the recipient).
Given a choice among the 3, I'd choose ssh. Cygwin ssh works fine, but there are several other implementations available for Windows.
HTH
As per this GitHub comment, you can try to use libfreerdp via ctypes in Python.
See: FreeRDP library at GitHub which is a free remote desktop protocol library and clients.
Home page: www.freerdp.com
Related: Programmatically manipulating active RDP session.

Categories