12

I am trying to execute a sudo command on a remote machine using python-paramiko, when I execute the command, I bind it with 3 streams, and I use the input stream to pass the password, but it doesn't work, this is the traceback result:

Traceback (most recent call last):
File "<input>", line 1, in <module>
File "/usr/local/lib/python2.7/dist-packages/paramiko/file.py", line 314, in write
self._write_all(data)
File "/usr/local/lib/python2.7/dist-packages/paramiko/file.py", line 439, in _write_all
count = self._write(data)
File "/usr/local/lib/python2.7/dist-packages/paramiko/channel.py", line 1263,in _write
self.channel.sendall(data)
File "/usr/local/lib/python2.7/dist-packages/paramiko/channel.py", line 796, in sendall
raise socket.error('Socket is closed')
error: Socket is closed

and this is my python code:

import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('192.168.0.104', username='cdc',password='123456')
stdin, stdout, stderr = ssh.exec_command("sudo dmesg")
stdin.write("123456\n")
stdin.flush()
print stdout.readlines()
ssh.close()

Any help? Thanks in advance

user3176971
  • 599
  • 2
  • 6
  • 16
  • see my answer http://stackoverflow.com/questions/6270677/how-to-run-sudo-with-paramiko-python/28011904#28011904 – AlexS Jan 18 '15 at 16:17

4 Answers4

17

First of all, have you tried in console with ssh cdc@192.168.0.104 "sudo -S -p '' dmesg". If it also fails, then you might check the sshd settings and the sudoer settings.

If it works well, please add some echo between lines, so that we can know exactly when the exception was thrown. I highly doubt that you should change sudo dmesg to sudo -S -p '' dmesg.

You might also try my wrapper of paramiko. I can use it smoothly to access any CentOS/SuSE node and perform any commands (w/wo sudo privilege):

#!/usr/bin/python

from StringIO import StringIO
import paramiko 

class SshClient:
    "A wrapper of paramiko.SSHClient"
    TIMEOUT = 4

    def __init__(self, host, port, username, password, key=None, passphrase=None):
        self.username = username
        self.password = password
        self.client = paramiko.SSHClient()
        self.client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        if key is not None:
            key = paramiko.RSAKey.from_private_key(StringIO(key), password=passphrase)
        self.client.connect(host, port, username=username, password=password, pkey=key, timeout=self.TIMEOUT)

    def close(self):
        if self.client is not None:
            self.client.close()
            self.client = None

    def execute(self, command, sudo=False):
        feed_password = False
        if sudo and self.username != "root":
            command = "sudo -S -p '' %s" % command
            feed_password = self.password is not None and len(self.password) > 0
        stdin, stdout, stderr = self.client.exec_command(command)
        if feed_password:
            stdin.write(self.password + "\n")
            stdin.flush()
        return {'out': stdout.readlines(), 
                'err': stderr.readlines(),
                'retval': stdout.channel.recv_exit_status()}

if __name__ == "__main__":
    client = SshClient(host='host', port=22, username='username', password='password') 
    try:
       ret = client.execute('dmesg', sudo=True)
       print "  ".join(ret["out"]), "  E ".join(ret["err"]), ret["retval"]
    finally:
      client.close() 
stanleyxu2005
  • 8,081
  • 14
  • 59
  • 94
  • I would like to see an answer to sudo requirement for a shell beyond simply changing your sshd settings. In other words, assuming I don't have access to the servers sshd settings, how do I run sudo commands? – rfportilla Aug 31 '15 at 15:49
  • @rfportilla My solution does not require changing sshd settings at all. Using my code, you can run any shell command as you do in local. – stanleyxu2005 Aug 31 '15 at 15:54
  • "...then you might check the sshd settings and the sudoer settings." I think the common error here is that sudo can't be run remotely through ssh without a tty: sudo: "sorry, you must have a tty to run sudo." You can get around this by modifying sudoers file, but this is not always an option. Your solution does not help with this from a client perspective. I may have down-voted prematurely, but it won't let me take it back now without a change (even a minor change) to the answer. Sorry. – rfportilla Aug 31 '15 at 17:09
  • @rfportilla Never mind for a down-voting. You have a good point that from a client perspective my solution still requires a tty. In other words, it does not elevate permissions with magic, but fixed a wrong command usage appears in the original question. – stanleyxu2005 Sep 01 '15 at 12:50
11

Im sorry i dont have time for details answer but i was able to implement sudo commands on paramiko using this advise

#!/usr/bin/env python
import paramiko
l_password = "yourpassword"
l_host = "yourhost"
l_user = "yourusername"
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(l_host, username=l_user, password=l_password)    
transport = ssh.get_transport()
session = transport.open_session()
session.set_combine_stderr(True)
session.get_pty()
#for testing purposes we want to force sudo to always to ask for password. because of that we use "-k" key
session.exec_command("sudo -k dmesg")
stdin = session.makefile('wb', -1)
stdout = session.makefile('rb', -1)
#you have to check if you really need to send password here 
stdin.write(l_password +'\n')
stdin.flush()
for line in stdout.read().splitlines():        
    print 'host: %s: %s' % (l_host, line)
IsaacS
  • 3,551
  • 6
  • 39
  • 61
AlexS
  • 927
  • 4
  • 16
  • 29
  • using this method, is there any way to get the return value of the executed command without parsing the output? – Jesus Gomez Feb 05 '16 at 22:22
4

I know this question is kind of old but I was wanting to use sudo and paramiko together, too. It took me a while to figure out this solution. It may not work for everyone but I figured it was worth adding.

def ssh_handler(hostname, username=USER, password=PASS, command=CMD): 
    ssh = paramiko.SSHClient()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    ssh.connect(hostname,
                username=username,
                password=password) 
    # set get_pty to True to get a pseudo terminal
    stdin, stdout, stderr = ssh.exec_command(prepare_command(command), get_pty=True)
    # write and flush the password to the remote terminal
    # note that the password will also be written to the terminal from where the script is executed.
    stdin.write(password+'\n')
    stdin.flush()

    response = stdout.read()   
    ssh.close()
    print(response)
             
     
def prepare_command(command):  
    if (not isinstance(command, basestring)): 
        command = ' ; '.join(command)  
    command = command.replace('"','\"') 
    command = 'sudo -s -- " '+command+' " \n'
    return command


# kind of a dumb example but you get the point 
mycmd = []; 
mycmd.append('cd /dir/this/user/doesnt/have/access/to')
mycmd.append('ls -las')
mycmd.append('cat file_in_dir.txt')

ssh_handler(server, command=mycmd)
Debargha Roy
  • 2,320
  • 1
  • 15
  • 34
gloomy.penguin
  • 5,833
  • 6
  • 33
  • 59
  • What's the point of this line? `command = command.replace('"','\"')` Why do you need a backslash in front of double quotes? – jxmorris12 Mar 27 '19 at 04:42
  • @jxmorris12 - it is escaping the double quote in the string. I wrote this ~5 years ago so I'm not totally sure but if you look at the following line, `'sudo -s -- " '+command+' " \n'`, you see that the whole command is being passed to sudo between double quotes. Escaping them will allow that to process... straight in the cli, an example would be `sudo -s -- " randomCommand --arg=\"pablo\" "` (probably wouldn't use those padding spaces, but it shows the quotes better here) – gloomy.penguin Apr 30 '19 at 05:48
  • How this solve the problem of providing sudo password? – Murphy Meng May 13 '20 at 09:39
  • How can this work without providing password? ```# stdin.write(password+'\n') ``` I am confused due to this line. – Rahul Shaw Jun 29 '20 at 06:57
  • Updated the answer to supply the sudo password when running the command. – Debargha Roy Dec 19 '22 at 12:30
0

I was able to implement this solution which was found in other Stackoverflow threads.

https://www.reddit.com/r/learnpython/comments/60taz7/execute_sudo_su_and_input_password_with_paramiko/df94q7s/

The solution for me was to use invoke_shell() when requiring the use of commands with sudo.

Michael K
  • 17
  • 1
  • 8