Paramiko sending text to psuedo shell - python

Testing sending commands to IoT devices that drops the user into a python script instead of a bash shell.
i.e. in /etc/passwd - admin:x:1000:0:admin:/home/admin:/home/admin/IoT.py
So cant use the normal bash exec_commands to talk to the python script. Basically once it connects it needs the send a command string and receive the output. Below the script reads IP addresses from a text file, whats the correct way to send and receive commands
#!/usr/bin/env python
import paramiko
import sys
def LogonSSH(ipAddress) :
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
for line in open(ipAddress, "r").readlines() :
ipAddr = line.strip()
username='admin'
password='admin'
try :
print "[+] Trying %s with username: %s password: %s " % (ipAddr, username, password)
ssh.connect(ipAddr, username=username, password=password)
except paramiko.AuthenticationException:
print "[x] %s - could not log in" % (ipAddr)
continue
stdin, stdout, stderr = ssh.exec_command('help')
stdin.flush()
stdin.write('help\n')
out = stdout.read()
stdin.flush()
print out
ssh.close()
break
if __name__ == "__main__" :
LogonSSH(sys.argv[1])

Below changes might help you. Just make send and recv, recv only takes the last commands output, but you need to consider:
Put some sleep after sending command (just trial and error)
Some tricks on output data, because it has invisible string parts such as (<pre>..</pre>)
Some output data might be too big to handle > 65100 bytes of recv, just check Paramiko Docs for recv method.
#!/usr/bin/env python
import paramiko
import sys
def LogonSSH(ipAddress) :
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
for line in open(ipAddress, "r").readlines() :
ipAddr = line.strip()
username='admin'
password='admin'
try:
print "[+] Trying %s with username: %s password: %s " % (ipAddr, username, password)
ssh.connect(ipAddr, port=22, username=uname, password=pin, timeout=240,banner_timeout=250, auth_timeout=500)
my_ssh_shell = ssh.invoke_shell()
except Exception as ex:
print("Some exception occured:" + str(ex))
continue
my_ssh_shell.send(bytes("help" + "\x0D", 'utf-8'))
result_screen = my_ssh_shell.recv(65100).decode('ascii')
print("Your SSH -help- command returns:" + str(result_screen))
my_ssh_shell.close()
break
if __name__ == "__main__" :
LogonSSH(sys.argv[1])
Edit: Just change it to any exception for connection, and also some additional paramters on connection string.

The following code will get the MOTD, but send still not working
my_ssh_shell = ssh.invoke_shell()
my_ssh_shell.settimeout(2)
while True :
data = my_ssh_shell.recv(1024).decode('ascii')
if len(data) < 1 :break
print(data)
return
#### DOESNT SEND HERE OR GET RECEIVE RESULTS#########
# my_ssh_shell.send('help\n')
# while True :
# data = my_ssh_shell.recv(1024).decode('ascii')
# if len(data) < 1 :break
# print(data)
# return
my_ssh_shell.close()

Related

How to skip lines when printing output from Paramiko SSH

So I built a program that prints out the login logs of my ubuntu server using tail -f.
The program uses Paramiko to connect via ssh and runs the command to tail the logs.
The program works but it prints out the motd from the server which is unnecessary.
I've tried splicing using itertools.
Tried using next().
Still doesn't work.
Here's my code:
import yaml, paramiko, getpass, traceback, time, itertools
from paramiko_expect import SSHClientInteraction
with open("config.yaml", "r") as yamlfile:
cfg = yaml.load(yamlfile, Loader=yaml.FullLoader)
def main():
command = "sudo tail -f /var/log/auth.log"
try:
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
server_pw = getpass.getpass("Enter the password for your account %s on %s:" % (cfg['ssh_config']['username'], cfg['ssh_config']['host']))
sudo_pw = getpass.getpass("Enter the sudo password for %s on %s: " % (cfg['ssh_config']['username'], cfg['ssh_config']['host']))
ssh.connect(hostname = cfg['ssh_config']['host'], username = cfg['ssh_config']['username'], port = cfg['ssh_config']['port'], password = server_pw)
interact = SSHClientInteraction(ssh, timeout=10, display=False)
interact.send(command)
interact.send(sudo_pw + "\n")
with open(interact.tail(line_prefix=cfg['ssh_config']['servername']+': ')) as tail:
for line in itertools.islice(tail, 17, None):
print(line)
except KeyboardInterrupt:
print('Ctrl+C interruption detected, stopping tail')
except Exception:
traceback.print_exc()
finally:
try:
ssh.close()
except:
pass
if __name__ == '__main__':
main()
You get MOTD because you are opening an interactive shell session. I do not think you need that, quite on the contrary.
Use SSHClient.exec_command instead:
stdin, stdout, stderr = ssh.exec_command(command, get_pty=True)
stdin.write(sudo_pw + "\n")
stdin.flush()
for line in iter(stdout.readline, ""):
print(line, end="")
Related questions:
Get output from a Paramiko SSH exec_command continuously
Pass input/variables to command/script over SSH using Python Paramiko
What is the difference between exec_command and send with invoke_shell() on Paramiko?
Obligatory warning: Do not use AutoAddPolicy – You are losing a protection against MITM attacks by doing so. For a correct solution, see Paramiko "Unknown Server".

paramiko exec_command hangs after a few commands

im connecting a linux server with sshclient .And then, im connecting cisco routers via telnet on this server. I'm connecting server and execute telnet command perfectly but in second or third telnet command code get stucked and doesnt throw error.Here is part of my code:
def __init__(self):
self.pre_client=paramiko.SSHClient()
self.pre_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
sellf.pre_client.connect("server",username="user",password="password")
self.client=self.pre_client.invoke_shell()
def connect(self,ip):
o=self.client.recv(1024)
print o
self.client.exec_command("telnet %s\n"%(ip))
while True:
o=self.client.recv(1024)
print o
#EXECUTE COMMAND ON ROUTER
self.client.exec_command("exit\n")
if 'exit' in o:
break
Why it get stuck on this command? How can i handle it?
I think I'd need to see more code to find what's wrong. If you don't have a specific reason for needing to use a Paramiko Channel, life will probably be a lot easier for you if you just use a Paramiko Client. This is a rough snippet from an old script of mine:
def ssh_connect(usr, pswds, host):
# Open Connection - auto add policy
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# Retry with multiple passwords
for pswd in pswds:
try:
ssh.connect(host, username=usr, password=pswd)
return ssh
except:
pass
else:
print("Could not login to: " + host)
return None
def send_command(conn, command):
try:
stdin, stdout, stderr = conn.exec_command(command)
if stdout:
for str in stdout:
sys.stdout.write("\t%s" % str)
return True
if stderr:
for str in stderr:
sys.stderr.write("\t%s" % str)
return False
else:
print("\n")
return True
except paramiko.ssh_exception.SSHException as e:
print(e.message)
return False
And of course, call them with:
conn = ssh_connect(login, passwords, host)
send_command(conn, command)

SSH programming with Python 3 paramiko, EOFError when trying to execute commands

Wizards of stackoverflow. I'm trying to read in commands from a .txt file to SSH using paramiko. Currently it will read in the first line from the file and execute it. When I get to the second line though, it will throw an EOFError and quit. I put a check in to see if the second command makes it to print and it does, however, it does not execute. I was hoping someone would be able to help and solve this issue with me. Here is my code:
from paramiko import client
import logging
import os
#Clear the screen before starting the script
os.system('cls')
# Prompting for the user input config file
filename = input('Input configuration filename, the file extension must be attached: ')
# Creating the LOG file for the execution of the config file
logFileName = "LOG" + filename[0:]
try:
logging.basicConfig(filename= logFileName ,format='%(asctime)s %(message)s', level= logging.DEBUG)
print ("The file was created!")
except IOError:
print ("File failed to create")
logging.info("---------------------------------------------------------------------------")
logging.info("NEW CONFIGURATION LOG ENTRY")
logging.info("---------------------------------------------------------------------------")
# Class for creating an SSH client, logging in, letting us write out commands, and close the client.
class ssh:
client = None
def __init__(self, address, username, password):
print ("Connecting to server...")
logging.info('Connecting to server...')
self.client = client.SSHClient()
self.client.set_missing_host_key_policy(client.AutoAddPolicy())
self.client.connect(address, username= username, password= password, look_for_keys= False)
logging.info("Address: " + address)
logging.info("Username: " + username)
print ("Connection successful!")
logging.info('Connection successful!')
def sendCommand(self, command):
if (self.client):
stdin, stdout, stderr = self.client.exec_command(command)
receiveData = b""
while not stdout.channel.exit_status_ready():
receiveData += stdout.channel.recv(1024)
if stdout.channel.recv_ready():
received = stdout.channel.recv(1024)
while received:
receiveData += received
received = stdout.channel.recv(1024)
if receiveData:
print (str(receiveData, "utf8"))
else:
print ("stdout is empty")
else:
print ("Connection failed, check credentials and try again..")
logging.warning('Connection failed, check credentials and try again..')
connection = ssh('0.0.0.0', 'test', 'test')
with open(filename) as f:
for line in f:
print(line)
connection.sendCommand(line)
The .txt file would read something like this:
configure terminal
Interface Gi0/9
description Test_Interface
Any help is much appreciated, thank you.
Possible bug. Current implementation of sendCommand may not receive output (or full output).
Reason
exit_status_ready is a non blocking way to find that exit status is received. It is possible that lastpart of output still not readed by script. You need to call recv after while if recv_ready is True.
Also, I don't think that checking recv_ready in while loop is good idea. It's non-blocking method. Because of it while loop will uselessly run multiple times, just wasting your CPU power.
This version work for me:
receiveData = b""
while not stdout.channel.exit_status_ready():
receiveData += stdout.channel.recv( 2048 )
if stdout.channel.recv_ready():
received = stdout.channel.recv( 2048 )
while received: #received will be empty only when all data received
receiveData += received
received = stdout.channel.recv( 2048 )
if receiveData:
print( str( receiveData, "utf8" ) )
else:
print( "stdout is empty" )
Also I should mention that there is easer way to build string from output. You can use the fact that stdin, stdout, and stderr are file like objects.
Here simpler example for stderr (it may be good idea to read it too):
data = ""
for line in stderr:
#line in string already (probably will work only if locale is utf8)
data += line
if data:
print( data )
else:
print( "stderr is empty" )
Update:
if you don't have multiple commands on one line then
filename = input('Input configuration filename, the file extension must be attached: ')
# define ssh class here (i'll skip it to make it shorter)
connection = ssh('0.0.0.0', 'test', 'test')
with open(filename) as f:
for line in f:
connection.sendCommand( line )
If you have several commands per line just split them to array of different commands.

Paramiko - python SSH - multiple command under a single channel

i have read other Stackoverflow threads on this. Those are older posts, i would like to get the latest update.
Is it possible to send multiple commands over single channel in Paramiko ? or is it still not possible ?
If so, is there any other library which can do the same.
Example scenario, automating the Cisco router confi. : User need to first enter "Config t" before entering the other other commands. Its currently not possible in paramiko.
THanks.
if you are planning to use the exec_command() method provided within the paramiko API , you would be limited to send only a single command at a time , as soon as the command has been executed the channel is closed.
The below excerpt from Paramiko API docs .
exec_command(self, command) source code Execute a command on the
server. If the server allows it, the channel will then be directly
connected to the stdin, stdout, and stderr of the command being
executed.
When the command finishes executing, the channel will be closed and
can't be reused. You must open a new channel if you wish to execute
another command.
but since transport is also a form of socket , you can send commands without using the exec_command() method, using barebone socket programming.
Incase you have a defined set of commands then both pexpect and exscript can be used , where you read a set of commands form a file and send them across the channel.
See my answer here or this page
import threading, paramiko
strdata=''
fulldata=''
class ssh:
shell = None
client = None
transport = None
def __init__(self, address, username, password):
print("Connecting to server on ip", str(address) + ".")
self.client = paramiko.client.SSHClient()
self.client.set_missing_host_key_policy(paramiko.client.AutoAddPolicy())
self.client.connect(address, username=username, password=password, look_for_keys=False)
self.transport = paramiko.Transport((address, 22))
self.transport.connect(username=username, password=password)
thread = threading.Thread(target=self.process)
thread.daemon = True
thread.start()
def closeConnection(self):
if(self.client != None):
self.client.close()
self.transport.close()
def openShell(self):
self.shell = self.client.invoke_shell()
def sendShell(self, command):
if(self.shell):
self.shell.send(command + "\n")
else:
print("Shell not opened.")
def process(self):
global strdata, fulldata
while True:
# Print data when available
if self.shell is not None and self.shell.recv_ready():
alldata = self.shell.recv(1024)
while self.shell.recv_ready():
alldata += self.shell.recv(1024)
strdata = strdata + str(alldata)
fulldata = fulldata + str(alldata)
strdata = self.print_lines(strdata) # print all received data except last line
def print_lines(self, data):
last_line = data
if '\n' in data:
lines = data.splitlines()
for i in range(0, len(lines)-1):
print(lines[i])
last_line = lines[len(lines) - 1]
if data.endswith('\n'):
print(last_line)
last_line = ''
return last_line
sshUsername = "SSH USERNAME"
sshPassword = "SSH PASSWORD"
sshServer = "SSH SERVER ADDRESS"
connection = ssh(sshServer, sshUsername, sshPassword)
connection.openShell()
connection.send_shell('cmd1')
connection.send_shell('cmd2')
connection.send_shell('cmd3')
time.sleep(10)
print(strdata) # print the last line of received data
print('==========================')
print(fulldata) # This contains the complete data received.
print('==========================')
connection.close_connection()
Have a look at parallel-ssh:
from pssh.pssh2_client import ParallelSSHClient
cmds = ['my cmd1', 'my cmd2']
hosts = ['myhost']
client = ParallelSSHClient(hosts)
for cmd in cmds:
output = client.run_command(cmd)
# Wait for completion
client.join(output)
Single client, multiple commands over same SSH session and optionally multiple hosts in parallel - also non-blocking.
I find this simple to understand and use. Code provides 2 examples with singlehost and multihost. Also added example where you can login to a second user and continue your commands with that user.
More info can be found in here: https://parallel-ssh.readthedocs.io/en/latest/advanced.html?highlight=channel#interactive-shells
from pssh.clients import SSHClient
from pssh.exceptions import Timeout
from pssh.clients import ParallelSSHClient
from pssh.config import HostConfig
def singleHost():
host_ = "10.3.0.10"
pwd_ = "<pwd>"
pwd_root = "<root pwd>"
user_ = "<user>"
client = SSHClient(host_, user=user_, password=pwd_, timeout=4, num_retries=1)
#####
shell = client.open_shell(read_timeout=2)
shell.run("whoami")
# login as new user example
shell.run("su - root")
shell.stdin.write(pwd_root + "\n")
shell.stdin.flush()
shell.run("pwd")
try:
# Reading Partial Shell Output, with 'timeout' > client.open_shell(read_timeout=2)
for line in shell.stdout:
print(line)
except Timeout:
pass
shell.run("whoami")
shell.run("cd ..")
print(".......")
try:
# Reading Partial Shell Output, with 'timeout' > client.open_shell(read_timeout=2)
for line in shell.stdout:
print(line)
except Timeout:
pass
shell.close()
def multiHost():
pwd_ = "<pwd>"
user_ = "<user>"
workingIP_list = ["10.3.0.10", "10.3.0.10"]
host_config_ = []
# HostConfig is needed one per each 'workingIP_list'
host_config_.append(HostConfig(user=user_, password=pwd_))
host_config_.append(HostConfig(user=user_, password=pwd_))
client_ = ParallelSSHClient(workingIP_list, host_config=host_config_, num_retries=1, timeout=3)
# now you have an open shell
shells = client_.open_shell(read_timeout=2)
command = "pwd"
client_.run_shell_commands(shells, command)
try:
# Reading Partial Shell Output, with 'timeout' > client_.open_shell(read_timeout=2)
for line in shells[0].stdout:
print(line)
except Timeout:
pass
print(".......")
command = "cd repo/"
client_.run_shell_commands(shells, command)
command = "pwd"
client_.run_shell_commands(shells, command)
#Joined on shells are closed and may not run any further commands.
client_.join_shells(shells)
for shell in shells:
for line in shell.stdout:
print(line)
print(shell.exit_code)
if __name__ == '__main__':
print("singleHost example:")
singleHost()
print("multiHost example:")
multiHost()

Python: Execute a command in Cisco Router on successful ping else print error

The following code fetches IP address (Cisco Router) from a text file and executes the mentioned command and prints the resultant output on to a file. Here am trying to first test the reach-ability of the device by using PING, on successful ping response commands should be executed else should print an error and move to the next host. Please help me on how to achieve this. I am a newbie.
Here is my code,
import paramiko
import sys
import os
import subprocess
with open('C:\Python27\Testing\Fetch.txt') as f:
for line in f:
line = line.strip()
dssh = paramiko.SSHClient()
dssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
dssh.connect(line, username='cisco', password='cisco')
stdin, stdout, stderr = dssh.exec_command('sh ip ssh')
mystring = stdout.read()
print mystring
f = open('C:\Python27\Testing\output.txt', 'a+')
f.write(mystring)
f.close()
dssh.close()
Input file Fetch.txt looks like this,
10.0.0.1
10.0.0.2
10.0.0.3
10.0.0.4
10.0.0.5
I scoured through the forum and achieved just about what I am looking for.. If all the IP addresses are reachable in that list, the script works just fine. But if any one of the IP address is unreachable then the script ends abruptly without proceeding to the next IP address. I realize that am doing something wrong here, I just need that little bit of help to get this working..... Please help out.
import paramiko
import sys
import os
import subprocess
dssh = paramiko.SSHClient()
dssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
with open('C:\Python27\Testing\Fetch.txt') as f:
for line in f:
line = line.strip()
with open(os.devnull, "wb") as limbo:
ip = line
result = subprocess.Popen(["ping", "-n", "1", "-w", "200", ip],
stdout=limbo, stderr=limbo).wait()
if result:
print ip, "Down"
else:
print ip, "Reachable"
dssh.connect(line, username='cisco', password='cisco')
stdin, stdout, stderr = dssh.exec_command('sh ip ssh')
mystring = stdout.read()
print mystring
f = open('C:\Python27\Testing\output.txt', 'a+')
f.write('\n' + ip + '\n' + mystring)
f.close()
dssh.close()
You ideally don't have to test if a host is pingable first using a separate if statement..paramiko comes inbuilt with a lot of exception checking ..using this along the socket module..your program can be written in a cleaner fashion without having to use subprocesses..
import paramiko
import socket
dssh = paramiko.SSHClient()
dssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ips = [i.strip() for i in open("C:\Python27\Testing\Fetch.txt")] # creates a list from input file
for ip in ips:
try:
dssh.connect(ip, username='cisco', password='cisco', timeout=4)
stdin, stdout, stderr = ssh.exec_command('sh ip ssh')
print ip + '===' + stdout.read()
ssh.close()
except paramiko.AuthenticationException:
print ip + '=== Bad credentials'
except paramiko.SSHException:
print ip + '=== Issues with ssh service'
except socket.error:
print ip + '=== Device unreachable'
this will catch other exceptions like bad credentials and other issues with ssh service
You could try using fabric which is made for executing SSH commands on multiple machines.
This is just a snippet i threw together but it should show you the way to go.
from fabric.api import run, execute ,env
class Fetcher:
def __init__(self,hosts=[]):
env.hosts= hosts
env.warn_only = True # needed to not abort on pingtimout or other errs
def getclock(self)
run('sh clock')
def fetch(self):
results = execute(self.getclock,hosts=env.hosts)
if __name__ == '__main__':
hosts = loadfromtxt(hosts.txt)
f = Fetcher(hosts=hosts)
f.fetch()
I recall an example of python threading, either in the docs or in a book I read (don't remember the source) that does something like what you're trying to do. Something like this should work:
import sys
import os
import subprocess
from threading import Thread
class Pinger(Thread):
def __init__ (self, ip):
Thread.__init__(self)
self.ip = ip
self.status = False
def __repr__(self):
return "Pinger for '%s' status '%s'" % (self.ip, self.status)
def run(self):
with open(os.devnull, "wb") as limbo:
# Changed the arguments because I don't have a windows ping.exe to test it on
result = subprocess.Popen(["ping", "-c", "2", "-q", self.ip],
stdout=limbo, stderr=limbo).wait()
if result:
# print self.ip, "Down"
self.status = False
else:
# print self.ip, "Reachable"
self.status = True
hosts = []
with open('Fetch.txt') as f:
for line in f:
host = Pinger(line.rstrip())
# print host
hosts.append(host)
host.start()
for host in hosts:
host.join()
if host.status:
print "Host '%s' is up" % host.ip
#### Insert your ssh exec code here ####
# dssh.connect(host.ip, username='cisco', password='cisco')
# etc.
else:
print "Host '%s' is down" % host.ip
Why do you need Paramiko module or to create an input if python can do itself ?
#!/usr/bin/python
import os
hostname = raw_input('Enter the Router Name: ')
routers = hostname.split(',')
print routers
for hostname in routers:
response = os.system("ping -c5 " + hostname)
if response == 0:
print(hostname, 'is up!')
else:
print(hostname, 'is down!')

Categories