user3176971
user3176971

Reputation: 619

Running Sudo Command with paramiko

I am trying to execute a sudo command on a remote machine using python-paramiko.

I tried this 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()

I expected this to bind all three streams and pass the password to sudo using the input stream. But it doesn't work and I get this error instead:

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

What is wrong, and how do I fix it?

Upvotes: 12

Views: 40981

Answers (5)

OutputLogic
OutputLogic

Reputation: 409

It worked for me when I combined both suggestions mentioned above:

  1. putting "-S -p ''" into sudo command: "sudo -S -p '' dmesg"
  2. setting password on stdin: stdin.write(password+'\n') stdin.flush()

Upvotes: -1

gloomy.penguin
gloomy.penguin

Reputation: 5911

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)

Upvotes: 4

Michael K
Michael K

Reputation: 13

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.

Upvotes: 0

AlexS
AlexS

Reputation: 917

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)

Upvotes: 11

stanleyxu2005
stanleyxu2005

Reputation: 8241

First of all, have you tried in console with ssh [email protected] "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() 

Upvotes: 18

Related Questions