3

Related questions that are essentially asking the same thing, but have answers that don't work for me:

Make python enter password when running a csh script

How to interact with ssh using subprocess module

How to execute a process remotely using python

I want to ssh into a remote machine and run one command. For example:

ssh <user>@<ipv6-link-local-addr>%eth0 sudo service fooService status

The problem is that I'm trying to do this through a python script with only the standard libraries (no pexpect). I've been trying to get this to work using the subprocess module, but calling communicate always blocks when requesting a password, even though I supplied the password as an argument to communicate. For example:

proc = subprocess.Popen(
        [
            "ssh",
            "{testUser1}@{testHost1}%eth0".format(**locals()),
            "sudo service cassandra status"],
        shell=False,
        stdin=subprocess.PIPE)
a, b = proc.communicate(input=testPasswd1)
print "a:", a, "b:", b
print "return code: ", proc.returncode

I've tried a number of variants of the above, as well (e.g., removing "input=", adding/removing subprocess.PIPE assignments to stdout and sterr). However, the result is always the same prompt:

ubuntu@<ipv6-link-local-addr>%eth0's password:

Am I missing something? Or is there another way to achieve this using the python standard libraries?

Community
  • 1
  • 1
jonderry
  • 23,013
  • 32
  • 104
  • 171
  • Did you try what [this answer](http://stackoverflow.com/a/21433723/2073595) suggests? That works fine for me in a Linux environment. If you're on Windows, I think you may be out of luck... – dano Jul 11 '14 at 01:14
  • You need to supply the password twice, once for ssh on the client side and once for sudo on the server side. _How to interact with ssh using subprocess module_ looked reasonable to me and could be modified for the sudo part... what about it didn't work for you? – tdelaney Jul 11 '14 at 01:39
  • @tdelaney, There's no password for sudo on the server. It seems that the "How to interact..." solution may work, but I need to get the output of one of the commands, and a simple read is returning nothing. – jonderry Jul 11 '14 at 02:04

1 Answers1

9

This answer is just an adaptation of this answer by Torxed, which I recommend you go upvote. It simply adds the ability to capture the output of the command you execute on the remote server.

import pty
from os import waitpid, execv, read, write

class ssh():
    def __init__(self, host, execute='echo "done" > /root/testing.txt', 
                 askpass=False, user='root', password=b'SuperSecurePassword'):
        self.exec_ = execute
        self.host = host
        self.user = user
        self.password = password
        self.askpass = askpass
        self.run()

    def run(self):
        command = [
                '/usr/bin/ssh',
                self.user+'@'+self.host,
                '-o', 'NumberOfPasswordPrompts=1',
                self.exec_,
        ]

        # PID = 0 for child, and the PID of the child for the parent    
        pid, child_fd = pty.fork()

        if not pid: # Child process
            # Replace child process with our SSH process
            execv(command[0], command)

        ## if we havn't setup pub-key authentication
        ## we can loop for a password promt and "insert" the password.
        while self.askpass:
            try:
                output = read(child_fd, 1024).strip()
            except:
                break
            lower = output.lower()
            # Write the password
            if b'password:' in lower:
                write(child_fd, self.password + b'\n')
                break
            elif b'are you sure you want to continue connecting' in lower:
                # Adding key to known_hosts
                write(child_fd, b'yes\n')
            else:
                print('Error:',output)

        # See if there's more output to read after the password has been sent,
        # And capture it in a list.
        output = []
        while True:
            try:
                output.append(read(child_fd, 1024).strip())
            except:
                break

        waitpid(pid, 0)
        return ''.join(output)

if __name__ == "__main__":
    s = ssh("some ip", execute="ls -R /etc", askpass=True)
    print s.run()

Output:

/etc:
adduser.conf
adjtime
aliases
alternatives
apm
apt
bash.bashrc
bash_completion.d
<and so on>
Community
  • 1
  • 1
dano
  • 91,354
  • 19
  • 222
  • 219