#1
  1. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Feb 2013
    Posts
    11
    Rep Power
    0

    Ssh scripting w/paramiko


    Hi;

    I'm not really sure how to proceed from here. Short version of the long winded discussion which follows is: Is there a more involved howto for paramiko that's somewhere between the many simple tutorials and the paramiko docs at http://www.lag.net/paramiko/docs/ which, while excellent docs, don't present a clear idea on how to use it - at least not to me...

    The end goal is to have a script which I can execute in one of several ways:

    Code:
    # to run a command on remote host as standard user authenticated via keys
    go -u dkoleary ${host} ${cmd} $arg $arg
    
    # to run a command on remote system as root via sudo
    go -r -u dkoleary -p ${pwd} ${host} ${cmd} ${arg} ${arg}
    
    # to log into a remote system as me, authenticating via pwd:
    go -u dkoleary -p ${pwd} ${host}
    
    # go log into a remote system as root via sudo:
    go -r -u dkoleary -p ${pwd} ${host}
    The code works as long as I'm logging in as me:

    Code:
    $ ./tn.py -d mgmt hostname
    User: dkoleary
    Pwd:  None
    Root: False
    Host: mgmt
    Args:  ['hostname']
    Cmd:  hostname
    Host: mgmt
    IP:   192.168.12.7
    You made it!
    
    $ ./tn.py mgmt hostname
    mgmt
    
    $ ./tn.py mgmt         
    *** Here we Go!!! ***
    
    Last login: Wed Feb 27 08:58:02 2013 from fw.olearycomputers.com
    $ h
    mgmt
    $ ^D
    When I add a simple sudo command, things get ugly:

    Code:
    $ ./tn.py -r -p ${pwd} mgmt # simple sudo cmd currently hard coded.
    Out: 
    Err: sudo: sorry, you must have a tty to run sudo
    Obviously, I need to tell paramiko to simulate a tty somehow. Some google searches showed one or two ways but indicated that the exec_command function would somehow become inappropriate.

    Another reading indicated that channel gets destroyed after every command. Due to restrictions at the current client, I need to "sudo su -", then run the command as root which should require two channels... I think.

    So, long story short: I'm looking for a how-to that's a little more involved than the simple tutorials but isn't the 'drinking from the firehose' that's at the paramiko docs site.

    For what it's worth, the current code (along w/commented tests) is below. Any hints/tips/suggestions greatly appreciated.

    Doug O'Leary

    Code:
    #!/usr/bin/python
    
    ####################################################################
    # tn:      my combined version of go/gsh/and tsshbatch.  Written in
    #          python and, primarily, a learning tool for python.
    # Author:  Doug O'Leary
    # Created: 02/19/13
    ####################################################################
    # $Log$
    ####################################################################
    
    #### imports:
    import interactive
    import getopt
    import getpass
    import os
    import paramiko
    import socket
    import sys
    import traceback
    
    def usage(msg):
       if len(msg) > 0: print "\n" + msg
       Usage = \
    "Usage: " + Prog + " [-u ${user} ] [-p ${pwd} [ -r ] ] ${host} \n\
                   [ $cmd $arg1 $arg2 $arg3 ]\n"
       print Usage
       sys.exit(1)
    
    Prog = sys.argv[0]
    if len(sys.argv) <= 1: usage("Invalid number of arguments!")
    
    
    ### process cli
    root = debug = False
    pwd  = None
    user = os.environ['LOGNAME']
    
    try: opts, args = getopt.getopt(sys.argv[1:], "du:p:r")
    except getopt.GetoptError as err: usage(str(err))
    
    for opt, val in opts:
       if opt == "-u": user  = val
       if opt == "-p": pwd   = val
       if opt == "-r": root  = True
       if opt == "-d": debug = True
    
    Host = args[0]
    args = args[1:]
    Cmd  = ' '.join(args[:])
    # Sudo = "sudo -S -p READINGSUDOPW su -"
    
    try:
       ip = socket.gethostbyname(Host)
    except socket.gaierror:
       usage("Invalid hostname: "+Host)
    
    if debug:
       print "User: " + user
       print "Pwd:  " + str(pwd)
       print "Root: " + str(root)
       print "Host: " + Host
       print "Args: ", args
       print "Cmd:  " + Cmd
       print "Host: " + Host
       print "IP:   " + ip
       print "You made it!"
       sys.exit(0)
    
    client = paramiko.SSHClient()
    client.load_system_host_keys()
    client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    # print "*** Connecting ***"
    client.connect(Host, 22, user)
    
    # # stdin, stdout, stderr = client.exec_command("sudo head -15 /etc/services")
    # stdin, stdout, stderr = client.exec_command("ls -al /tmp")
    # # stdin.write(pwd)
    # # stdin.flush()
    # print ''.join(stdout.read())
    # print "Err: " + ''.join(stderr.read())
    # client.close()
    # sys.exit(0)
    
    
    if root:
       # print "Sudo: " + Sudo
       if pwd is None: pwd = getpass.getpass('Password: ')
       stdin, stdout, stderr = client.exec_command('sudo -S head -15 /etc/services')
       stdin.write("%s\n" % pwd)
       stdin.flush()
       print "Out: " + ''.join(stdout.read())
       print "Err: " + ''.join(stderr.read())
       client.close()
       sys.exit(1)
    
    if len(args) == 0:
       try:
          chan = client.invoke_shell()
          print "*** Here we Go!!! ***"
          print
          interactive.interactive_shell(chan)
          client.close()
       except Exception, e:
          print '*** Caught exception: %s: %s' % (e.__class__, e)
          traceback.print_exc()
          try: client.close()
          except: pass
          sys.exit(1)
    else:
       try:
          # print "Cmd:  " + Cmd
          stdin, stdout, stderr = client.exec_command(Cmd)
          print ''.join(stdout.read())
          client.close()
       except Exception, e:
          print '*** Caught exception: %s: %s' % (e.__class__, e)
          traceback.print_exc()
          try: client.close()
          except: pass
          sys.exit(1)
    
    sys.exit(0)
  2. #2
  3. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Feb 2013
    Posts
    138
    Rep Power
    2
    Originally Posted by dkoleary
    [...]
    When I add a simple sudo command, things get ugly:

    Code:
    $ ./tn.py -r -p ${pwd} mgmt # simple sudo cmd currently hard coded.
    Out: 
    Err: sudo: sorry, you must have a tty to run sudo
    [...]
    Try using get_pty() before exec_command()

    From http://stackoverflow.com/questions/2909481/paramiko-and-pseudo-tty-allocation :
    Code:
    #!/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))
  4. #3
  5. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Feb 2013
    Posts
    11
    Rep Power
    0
    Hey;

    Thanks for the response. I saw that post and was playing around with iterations of that yesterday and today.

    This works:

    Code:
    client = paramiko.SSHClient()
    client.load_system_host_keys()
    client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    # print "*** Connecting ***"
    client.connect(Host, 22, user)
    
    chan = client.get_transport().open_session()
    chan.get_pty()
    chan.exec_command('ls -al /tmp')
    print(chan.recv(4096))
    client.close()
    sys.exit(0)
    However, once again, whenever I put sudo in, I can't get it to work:
    Code:
    ...
    stdin, stdout, stderr = chan.exec_command("sudo -S head -15 /etc/services")
    results in an exception about that command not being iterable. Bummer, ok, moving on to send/recv:

    Following the example of another post, I came up with:

    Code:
    client = paramiko.SSHClient()
    client.load_system_host_keys()
    client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    # print "*** Connecting ***"
    client.connect(Host, 22, user)
    
    # chan = client.get_transport().open_session()
    # chan.get_pty()
    # chan.exec_command('ls -al /tmp')
    # print(chan.recv(4096))
    # client.close()
    # sys.exit(0)
    
    print 'getting channel'
    chan = client.get_transport().open_session()
    print 'getting pty'
    chan.get_pty()
    print 'sending sudo command'
    chan.send("sudo head -15 /etc/shadow\n")
    while not chan.recv_ready():
       print 'waiting for challenge'
       time.sleep(1)
    print 'receiving'
    print chan.recv(1024)
    print 'sending password'
    chan.send("%s\n" % pwd)
    while not chan.recv_ready():
       print 'waiting for reply'
       time.sleep(1)
    print chan.recv(4096)
    client.close()
    sys.exit(0)
    However, that's hanging:

    Code:
    $ ./tn.py -r -p ${pwd} mgmt
    getting channel
    getting pty
    sending sudo command
    // long hang followed by kill ${pid} in another window
    Terminated
    I'm beginning to suspect I'm going to have to go the expect route. I wrote a similar script in perl/expect a long time back; but, the paramiko approach seems like it should be much cleaner... except for that whole lack of ability to get it to work thing...

    Thanks again for the reply. I appreciate it.

    Doug O'Leary
  6. #4
  7. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Feb 2013
    Posts
    138
    Rep Power
    2
    Originally Posted by dkoleary
    [...]
    I'm beginning to suspect I'm going to have to go the expect route. I wrote a similar script in perl/expect a long time back; but, the paramiko approach seems like it should be much cleaner... except for that whole lack of ability to get it to work thing...
    Or maybe look into Fabric (http://docs.fabfile.org/en/1.6/)?

    Also, if security allows you could add password-less sudo permissions on the remote server for the commands you need.

IMN logo majestic logo threadwatch logo seochat tools logo