Paramiko and Pseudo-tty Allocation - python

I'm trying to use Paramiko to connect to a remote host and execute a number of text file substitutions.
i, o, e = client.exec_command("perl -p -i -e 's/" + initial + "/"
+ replaced + "/g'" + conf);
Some of these commands need to be run as sudo, which results in:
sudo: sorry, you must have a tty to
run sudo
I can force pseudo-tty allocation with the -t switch and ssh.
Is it possible to do the same thing using paramiko?

Actually it's quite simple. Just:
stdin, stdout, stderr = client.exec_command(command, get_pty=True)

The following code works for me:
#!/usr/bin/env python
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('localhost',username='root',password='secret')
chan = ssh.get_transport().open_session()
chan.get_pty()
chan.exec_command('tty')
print(chan.recv(1024))
This was just assembled from looking at a few examples online... not sure if its the "right" way.

I think you want the invoke_shell method of the SSHClient object (I'd love to give a URL but the paramiko docs at lag.net are frame-heavy and just won't show me a specific URL for a given spot in the docs) -- it gives you a Channel, on which you can do exec_command and the like, but does that through a pseudo-terminal (complete with terminal type and numbers of rows and columns!-) which seems to be what you're asking for.

According to the sudo manpage:
The -S (stdin) option causes sudo to read the password from the standard input instead of the terminal device. The
password must be followed by a newline character.
You can write to the stdin because it is a file object with write():
import paramiko
client = paramiko.client.SSHClient()
client.set_missing_host_key_policy(paramiko.client.AutoAddPolicy())
client.connect(hostname='localhost', port=22, username='user', password='password')
stdin, stdout, stderr = client.exec_command('sudo -S aptitude update')
stdin.write('password\n')
stdin.flush()
# print the results
print stdout.read()
client.close()

Related

paramiko. answering questions asked by equipment [duplicate]

I'm trying to run an interactive command through paramiko. The cmd execution tries to prompt for a password but I do not know how to supply the password through paramiko's exec_command and the execution hangs. Is there a way to send values to the terminal if a cmd execution expects input interactively?
ssh = paramiko.SSHClient()
ssh.connect(server, username=username, password=password)
ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command("psql -U factory -d factory -f /tmp/data.sql")
Does anyone know how this can addressed? Thank you.
The full paramiko distribution ships with a lot of good demos.
In the demos subdirectory, demo.py and interactive.py have full interactive TTY examples which would probably be overkill for your situation.
In your example above ssh_stdin acts like a standard Python file object, so ssh_stdin.write should work so long as the channel is still open.
I've never needed to write to stdin, but the docs suggest that a channel is closed as soon as a command exits, so using the standard stdin.write method to send a password up probably won't work. There are lower level paramiko commands on the channel itself that give you more control - see how the SSHClient.exec_command method is implemented for all the gory details.
I had the same problem trying to make an interactive ssh session using ssh, a fork of Paramiko.
I dug around and found this article:
Updated link (last version before the link generated a 404): http://web.archive.org/web/20170912043432/http://jessenoller.com/2009/02/05/ssh-programming-with-paramiko-completely-different/
To continue your example you could do
ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command("psql -U factory -d factory -f /tmp/data.sql")
ssh_stdin.write('password\n')
ssh_stdin.flush()
output = ssh_stdout.read()
The article goes more in depth, describing a fully interactive shell around exec_command. I found this a lot easier to use than the examples in the source.
Original link: http://jessenoller.com/2009/02/05/ssh-programming-with-paramiko-completely-different/
You need Pexpect to get the best of both worlds (expect and ssh wrappers).
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(server_IP,22,username, password)
stdin, stdout, stderr = ssh.exec_command('/Users/lteue/Downloads/uecontrol-CXC_173_6456-R32A01/uecontrol.sh -host localhost ')
alldata = ""
while not stdout.channel.exit_status_ready():
solo_line = ""
# Print stdout data when available
if stdout.channel.recv_ready():
# Retrieve the first 1024 bytes
solo_line = stdout.channel.recv(1024)
alldata += solo_line
if(cmp(solo_line,'uec> ') ==0 ): #Change Conditionals to your code here
if num_of_input == 0 :
data_buffer = ""
for cmd in commandList :
#print cmd
stdin.channel.send(cmd) # send input commmand 1
num_of_input += 1
if num_of_input == 1 :
stdin.channel.send('q \n') # send input commmand 2 , in my code is exit the interactive session, the connect will close.
num_of_input += 1
print alldata
ssh.close()
Why the stdout.read() will hang if use dierectly without checking stdout.channel.recv_ready(): in while stdout.channel.exit_status_ready():
For my case ,after run command on remote server , the session is waiting for user input , after input 'q' ,it will close the connection .
But before inputting 'q' , the stdout.read() will waiting for EOF,seems this methord does not works if buffer is larger .
I tried stdout.read(1) in while , it works
I tried stdout.readline() in while , it works also.
stdin, stdout, stderr = ssh.exec_command('/Users/lteue/Downloads/uecontrol')
stdout.read() will hang
I'm not familiar with paramiko, but this may work:
ssh_stdin.write('input value')
ssh_stdin.flush()
For information on stdin:
http://docs.python.org/library/sys.html?highlight=stdin#sys.stdin
Take a look at example and do in similar way
(sorce from http://jessenoller.com/2009/02/05/ssh-programming-with-paramiko-completely-different/):
ssh.connect('127.0.0.1', username='jesse',
password='lol')
stdin, stdout, stderr = ssh.exec_command(
"sudo dmesg")
stdin.write('lol\n')
stdin.flush()
data = stdout.read.splitlines()
for line in data:
if line.split(':')[0] == 'AirPort':
print line
You can use this method to send whatever confirmation message you want like "OK" or the password. This is my solution with an example:
def SpecialConfirmation(command, message, reply):
net_connect.config_mode() # To enter config mode
net_connect.remote_conn.sendall(str(command)+'\n' )
time.sleep(3)
output = net_connect.remote_conn.recv(65535).decode('utf-8')
ReplyAppend=''
if str(message) in output:
for i in range(0,(len(reply))):
ReplyAppend+=str(reply[i])+'\n'
net_connect.remote_conn.sendall(ReplyAppend)
output = net_connect.remote_conn.recv(65535).decode('utf-8')
print (output)
return output
CryptoPkiEnroll=['','','no','no','yes']
output=SpecialConfirmation ('crypto pki enroll TCA','Password' , CryptoPkiEnroll )
print (output)

Connect to gce instance and run command

I have a simple request. I want to connect to an already existing google compute engine instance, run a command, and close the connection.
I have used the great sample code here for instance creation and deletion.
Additionally, I have a startup script running which works perfectly.
Now I am reading this article to use paramiko to connect to my instance. This may or may not be the best thing to do, so please correct me if I am going down the wrong path.
I have the following sample code:
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(
paramiko.AutoAddPolicy())
ssh.connect('35.***.***.**',username='user',password='pass')
stdin, stdout, stderr = ssh.exec_command("sudo su -")
stdin, stdout, stderr = ssh.exec_command("ls -l")
stdout.readlines()
Now - I am not sure which username or password I am supposed to use.
When I run this code, I do not get the list of files and directories in my root as I want, but I do get a list of files and directories in the default user account's home - so it is connecting.
My goal is to connect to a gce instance, run a command, and that is it! For some reason it is trickier than I anticipated. Am I doing something wrong here?
If you are facing a similar use case you can explore gcloud ssh. It worked for me, but I cannot comment if this is best practice or not.
My solution here was something like the following:
import subprocess
def check_for_completion(instance_name = ""):
cmd = "gcloud compute ssh %s --zone=us-east1-b --command=\"sudo -S -i -u root -p '' ls /root/temp/ \""%(instance_name)
try:
res = subprocess.check_output(cmd, shell=True)
items = str(res).split('\n')
return {'response':items,'complete':False}
except:
return {'response':None,'complete':True}

Subprocess on remote server

I am using this code for executing command on remote server.
import subprocess
import sys
COMMAND="ls"
ssh = subprocess.Popen(["ssh", "%s" % HOST, COMMAND],
shell=False,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
result = ssh.stdout.readlines()
if result == []:
error = ssh.stderr.readlines()
print >>sys.stderr, "ERROR: %s" % error
else:
print result
When I try to execute this script, I get prompt for password. Is there any way I could avoid it, for example, can I enter password in script somehow? Also, password should be encrypted somehow so that people who have access to the script cannot see it.
Why make it so complicated? Here's what I suggest:
1) Create a ssh config section in your ~/.ssh/config file:
Host myserver
HostName 50.50.50.12 (fill in with your server's ip)
Port xxxx (optional)
User me (your username for server)
2) If you have generated your ssh keypair do it now (with ssh-keygen). Then upload with:
$ ssh-copy-id myserver
3) Now you can use subprocess with ssh. For example, to capture output, I call:
result = subprocess.check_output(['ssh', 'myserver', 'cat', 'somefile'])
Simple, robust, and the only time a password is needed is when you copy the public key to the server.
BTW, you code will probably work just fine as well using these steps.
One way is to create a public key, put it on the server, and do ssh -i /path/to/pub/key user#host or use paramiko like this:
import paramiko
import getpass
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
p = getpass.getpass()
ssh.connect('hostname', username='user', password=p)
stdin, stdout, stderr = ssh.exec_command('ls')
print stdout.readlines()
ssh.close()
You should use pexpect or paramiko to connect to remote machine,then spawn a child ,and then run subprocess to achieve what you want.
Here's what I did when encountering this issue before:
Set up your ssh keys for access to the server.
Set up an alias for the server you're accessing. Below I'll call it remote_server.
Put the following two lines at the end of ~/.bash_profile.
eval $(ssh-agent -s)
ssh-add
Now every time you start your shell, you will be prompted for a passphrase. By entering it, you will authenticate your ssh keys and put them 'in hand' at the start of your bash session. For the remainder of your session you will be able to run commands like
ssh remote_server ls
without being prompted for a passphrase. Here ls will run on the remote server and return the results to you. Likewise your python script should run without password prompt interruption if you execute it from the shell.
You'll also be able to ssh to the server just by typing ssh remote_server without having to enter your username or password every time.
The upside to doing it this way is that you should be doing this anyway to avoid password annoyances and remembering funky server names :) Also you don't have to worry about having passwords saved anywhere in your script. The only potential downside is that if you want to share the python script with others, they'll have to do this configuring as well (which they should anyway).
You don't really need something like pexpect to handle this. SSH keys already provide a very good and secure solution to this sort of issue.
The simplest way to get the results you want would probably be to generate an ssh key and place it in the .ssh folder of your device. I believe github has a pretty good guide to doing that, if you look into it. Once you set up the keys correctly on both systems, you won't actually have to add a single line to your code. When you don't specify a password it will automatically use the key to authenticate you.
While subprocess.Popen might work for wrapping ssh access, this is not the preferred way to do so.
I recommend using paramiko.
import paramiko
ssh_client = paramiko.SSHClient()
ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh_client.connect(server, username=user,password=password)
...
ssh_client.close()
And If you want to simulate a terminal, as if a user was typing:
chan=ssh_client.invoke_shell()
def exec_cmd(cmd):
"""Gets ssh command(s), execute them, and returns the output"""
prompt='bash $' # the command line prompt in the ssh terminal
buff=''
chan.send(str(cmd)+'\n')
while not chan.recv_ready():
time.sleep(1)
while not buff.endswith(prompt):
buff+=ssh_client.chan.recv(1024)
return buff[:len(prompt)]
Example usage: exec_cmd('pwd')
If you don't know the prompt in advance, you can set it with:
chan.send('PS1="python-ssh:"\n')
You could use following.
import subprocess
import sys
COMMAND="ls"
ssh = subprocess.Popen("powershell putty.exe user#HOST -pw "password", stdout=PIPE, stdin=PIPE, stderr=STDOUT)
result = ssh.stdout.readlines()
if result == []:
error = ssh.stderr.readlines()
print >>sys.stderr, "ERROR: %s" % error
else:
print result

How do I start an SSH session locally using Python?

What I mean to ask is, if I am on System "A" (Linux) and I want to ssh into System "B" (Windows): On System "A", I can do ssh admin#xx.xx.xx.xx which will prompt me to a password and when that gets authenticated, I will get to the "$" of System "B" (on System "A").
how do I send username and password together as a single line (since I want to use a script)
How to achieve the scenario that I have above.
I generally do it with Paramiko, its easier
import paramiko
# ssh
print 'enter ssh'
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # this will automatically add the keys
ssh.connect(machineHostName, username=user, password=password)
# Run your commands
# example 1 : ls command
print 'do a ls command'
stdin, stdout, stderr = ssh.exec_command('ls')
print stdout.readlines()
time.sleep(2)
# example 2 : change ip address
print 'changing ip address'
stdin, stdout, stderr = ssh.exec_command('sed -i -- s/'+oldIp+'/'+newIp+'/g /etc/sysconfig/network-scripts/ifcfg-eth0')
print stdout.readlines()
time.sleep(2)
To install Paramiko, you can download the tar.gz file from here.
Assuming you are really new to python, how to install this :
Download the tar.gz file
Extract the contents to a folder
cd into that extracted folder, from your terminal
execute this python setup.py install
then you can try something like the above example
NOTE : if you get stuck with installation comment here, and I can help you.
Instead of using a passphrase for authentication, you could use ssh keys as described here.
Start your ssh client on System "A" using subprocess.call(['/path/to/ssh', 'admin#xx.xx.xx.xx', 'remote_script.sh'])

Running interactive commands in Paramiko

I'm trying to run an interactive command through paramiko. The cmd execution tries to prompt for a password but I do not know how to supply the password through paramiko's exec_command and the execution hangs. Is there a way to send values to the terminal if a cmd execution expects input interactively?
ssh = paramiko.SSHClient()
ssh.connect(server, username=username, password=password)
ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command("psql -U factory -d factory -f /tmp/data.sql")
Does anyone know how this can addressed? Thank you.
The full paramiko distribution ships with a lot of good demos.
In the demos subdirectory, demo.py and interactive.py have full interactive TTY examples which would probably be overkill for your situation.
In your example above ssh_stdin acts like a standard Python file object, so ssh_stdin.write should work so long as the channel is still open.
I've never needed to write to stdin, but the docs suggest that a channel is closed as soon as a command exits, so using the standard stdin.write method to send a password up probably won't work. There are lower level paramiko commands on the channel itself that give you more control - see how the SSHClient.exec_command method is implemented for all the gory details.
I had the same problem trying to make an interactive ssh session using ssh, a fork of Paramiko.
I dug around and found this article:
Updated link (last version before the link generated a 404): http://web.archive.org/web/20170912043432/http://jessenoller.com/2009/02/05/ssh-programming-with-paramiko-completely-different/
To continue your example you could do
ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command("psql -U factory -d factory -f /tmp/data.sql")
ssh_stdin.write('password\n')
ssh_stdin.flush()
output = ssh_stdout.read()
The article goes more in depth, describing a fully interactive shell around exec_command. I found this a lot easier to use than the examples in the source.
Original link: http://jessenoller.com/2009/02/05/ssh-programming-with-paramiko-completely-different/
You need Pexpect to get the best of both worlds (expect and ssh wrappers).
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(server_IP,22,username, password)
stdin, stdout, stderr = ssh.exec_command('/Users/lteue/Downloads/uecontrol-CXC_173_6456-R32A01/uecontrol.sh -host localhost ')
alldata = ""
while not stdout.channel.exit_status_ready():
solo_line = ""
# Print stdout data when available
if stdout.channel.recv_ready():
# Retrieve the first 1024 bytes
solo_line = stdout.channel.recv(1024)
alldata += solo_line
if(cmp(solo_line,'uec> ') ==0 ): #Change Conditionals to your code here
if num_of_input == 0 :
data_buffer = ""
for cmd in commandList :
#print cmd
stdin.channel.send(cmd) # send input commmand 1
num_of_input += 1
if num_of_input == 1 :
stdin.channel.send('q \n') # send input commmand 2 , in my code is exit the interactive session, the connect will close.
num_of_input += 1
print alldata
ssh.close()
Why the stdout.read() will hang if use dierectly without checking stdout.channel.recv_ready(): in while stdout.channel.exit_status_ready():
For my case ,after run command on remote server , the session is waiting for user input , after input 'q' ,it will close the connection .
But before inputting 'q' , the stdout.read() will waiting for EOF,seems this methord does not works if buffer is larger .
I tried stdout.read(1) in while , it works
I tried stdout.readline() in while , it works also.
stdin, stdout, stderr = ssh.exec_command('/Users/lteue/Downloads/uecontrol')
stdout.read() will hang
I'm not familiar with paramiko, but this may work:
ssh_stdin.write('input value')
ssh_stdin.flush()
For information on stdin:
http://docs.python.org/library/sys.html?highlight=stdin#sys.stdin
Take a look at example and do in similar way
(sorce from http://jessenoller.com/2009/02/05/ssh-programming-with-paramiko-completely-different/):
ssh.connect('127.0.0.1', username='jesse',
password='lol')
stdin, stdout, stderr = ssh.exec_command(
"sudo dmesg")
stdin.write('lol\n')
stdin.flush()
data = stdout.read.splitlines()
for line in data:
if line.split(':')[0] == 'AirPort':
print line
You can use this method to send whatever confirmation message you want like "OK" or the password. This is my solution with an example:
def SpecialConfirmation(command, message, reply):
net_connect.config_mode() # To enter config mode
net_connect.remote_conn.sendall(str(command)+'\n' )
time.sleep(3)
output = net_connect.remote_conn.recv(65535).decode('utf-8')
ReplyAppend=''
if str(message) in output:
for i in range(0,(len(reply))):
ReplyAppend+=str(reply[i])+'\n'
net_connect.remote_conn.sendall(ReplyAppend)
output = net_connect.remote_conn.recv(65535).decode('utf-8')
print (output)
return output
CryptoPkiEnroll=['','','no','no','yes']
output=SpecialConfirmation ('crypto pki enroll TCA','Password' , CryptoPkiEnroll )
print (output)

Categories

Resources