19

What I've tried:

  1. invoke_shell() then channel.send su and then sending the password resulted in not being root
  2. invoke_shell() and then channel.exec_command resulted in a "Channel Closed" error
  3. _transport.open_session() then channel.exec_command resulted in not being root
  4. invoke_shell() then writing to stdin and flushing it resulted in not being root
Martin Prikryl
  • 167,268
  • 50
  • 405
  • 846
Takkun
  • 6,869
  • 13
  • 36
  • 41

7 Answers7

27

check this example out:

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

Example found here with more explanations: http://jessenoller.com/2009/02/05/ssh-programming-with-paramiko-completely-different/

Hope it helps!

W0bble
  • 747
  • 8
  • 24
  • 9
    This will not work if your sudoer requires a password, though: "sudo: no tty present and no askpass program specified" – Haroldo_OK May 28 '14 at 12:08
  • 2
    Okay, riskable's comment at the link you provided solves the problem above: stdin, stdout, stderr = ssh.exec_command("sudo -S %s" % command) # If stdout is still open then sudo is asking us for a password if stdout.channel.closed is False: stdin.write('%s\n' % password) stdin.flush() – Haroldo_OK May 28 '14 at 12:21
  • 9
    my bad, I didn't check stderr, it should be said that one must use `get_pty=True`, so you need to use : `ssh.exec_command('your command', get_pty=True)` – sliders_alpha Jul 08 '16 at 08:48
  • 3
    get_pty=True is helpful. without this password prompt is not coming. – VISHAL VIRADIA Sep 23 '16 at 07:22
  • Given link is withdrawn by the author. – Adrian W Aug 16 '19 at 13:58
  • 1
    https://web.archive.org/web/20091220003139/http://jessenoller.com/2009/02/05/ssh-programming-with-paramiko-completely-different/ – NomNomNom Jan 03 '20 at 20:19
  • 1
    @NomNomNom I was about to post the same thing! Nicely done! – David Golembiowski Jun 05 '21 at 16:35
8

invoke_shell worked for me like this:

import paramiko, getpass, re, time

ssh_client = paramiko.SSHClient()   
ssh_client.connect( host )
sudo_pw = getpass.getpass("sudo pw for %s: " % host)
command = "sudo magicwand"

channel = ssh_client.invoke_shell() 
channel.send( command )       
# wait for prompt             
while not re.search(".*\[sudo\].*",channel.recv(1024)): time.sleep(1)
channel.send( "%s\n" % sudo_pw )
Celeo
  • 5,345
  • 8
  • 38
  • 40
seelaman
  • 81
  • 1
  • 3
6

AlexS Fine tuned answer (which I am now using it in production) would be:

def sudo_run_commands_remote(command, server_address, server_username, server_pass, server_key_file):
    ssh = paramiko.SSHClient()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    ssh.connect(hostname=server_address,
                username=server_username,
                password=server_pass,
                key_filename=server_key_file)
    session = ssh.get_transport().open_session()
    session.set_combine_stderr(True)
    session.get_pty()
    session.exec_command("sudo bash -c \"" + command + "\"")
    stdin = session.makefile('wb', -1)
    stdout = session.makefile('rb', -1)
    stdin.write(server_pass + '\n')
    stdin.flush()
    print(stdout.read().decode("utf-8"))

Remove the key_filename part of connect method if you dont use a key file and in contrast if you only use a key without password, remove the password part.

Some notes about this is that, it is multi command capable. Meaning that is is running a bash as root so you can as much commands as you can in a single run with just separating them with ;.

AmirHossein
  • 3,281
  • 23
  • 25
  • I'm using your approach with salt and SSH tunnel and I'm finding in the returned values the password. Can you please doublecheck and share your experiences? Thanks – Javier Dec 16 '21 at 13:54
3

You Can use channel to send sudo password:

  passwd = getpass.getpass()
  ssh = paramiko.client.SSHClient()
  ssh.set_missing_host_key_policy(paramiko.client.AutoAddPolicy())
  ssh.load_system_host_keys()
  ssh.connect(host, allow_agent=True)
  chan = ssh.get_transport().open_session()
  chan.get_pty()
  chan.setblocking(1)

  chan.exec_command("sudo -k dmesg")

  while chan.recv_ready()==False:
      stdout=chan.recv(4096)
      if re.search('[Pp]assword', stdout):
          chan.send(passwd+'\n')
      time.sleep(1)
      
  while chan.recv_ready():
      stdout += chan.recv(20000)
  chan.close()
  ssh.close()
Martlark
  • 13,157
  • 12
  • 78
  • 96
Aashutosh jha
  • 504
  • 6
  • 8
2

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

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)
AlexS
  • 887
  • 4
  • 15
  • 27
0

To my mind it would be much easier and secure to create a script which has sudoer rights.

For example, add this to sudoers:

myuser  ALL=NOPASSWD:/home/myuser/somescript.sh

Now you can just invoke the script via paramiko on the host machine and be done with it.

Uku Loskit
  • 39,250
  • 9
  • 87
  • 91
  • To edit sudoers, we have to log in as root (or use `su`|`sudo`) and edit sudoers or run script to do that. I may be difficult or impossible if you have access to remote system only thru `paramiko`. For example, you are automating something, you don't want to prepare manually each host for your scripts. – Jury Dec 24 '15 at 09:13
  • This is not an option in most cases since this is high security risk. – Appu Sidhardh May 12 '16 at 09:52
-1

I was able to run sudo cupsdisable command on the remote server manually without having to enter the password when I login to that server as one of the admin user(not root) but when I execute the same using stdin, stdout, stderr = client.exec_command("sudo cupsdisable <Printqueuename>") it does nothing.

The command that worked for me was:

stdin, stdout, stderr = client.exec_command("sudo -u root /usr/sbin/cupsdisable <printQueuename>")

This is specific to the above mentioned scenario only. Hope this helps someone

Pandemonium
  • 6,565
  • 2
  • 29
  • 49
  • 1
    This answer is specific to your case, not a general answer. It does not answer the question. – Azsgy Apr 05 '18 at 17:10