Hello,

I have a python script that executes a python code on a remote host using
SSH. It was working perfectly with Twisted 2.5. The method of returning
private and public has changed in userauth.SSHUserAuthClient (Twisted 8.1).

I was using (Twisted 2.5)
------------------------------------------------
def getPublicKey(self):
  return keys.getPublicKeyString(self.keyfile+'.pub')

def getPrivateKey(self):
  return defer.succeed(keys.getPrivateKeyObject(self.keyfile))


Now I am using (Twisted 8.1)
----------------------------------------------------
def getPublicKey(self):
  return keys.Key.fromFile(self.keyfile+'.pub')

def getPrivateKey(self):
  return defer.succeed(keys.Key.fromFile(self.keyfile))

and it doesn't work any more. I have attached the simplified script. Here is
the scenario I am using and the exception I got. Any help is greatly
appreciated.

Scenario
----------------------
I am creating a DSA key using ssh-keygen (in Debian and Ubuntu) with an
empty password

$ ssh-keygen -t dsa

and copy it to my localhost

$ ssh-copy-id -i ~/.ssh/id_dsa localhost

then run the attached script, which tries to execute a python script
remotely using SSH and get the following error

Traceback (most recent call last):
  File "/usr/lib/python2.5/site-packages/twisted/internet/selectreactor.py",
line 146, in _doReadOrWrite
    why = getattr(selectable, method)()
  File "/usr/lib/python2.5/site-packages/twisted/internet/tcp.py", line 362,
in doRead
    return self.protocol.dataReceived(data)
  File "/usr/lib/python2.5/site-packages/twisted/conch/ssh/transport.py",
line 314, in dataReceived
    self.dispatchMessage(messageNum, packet[1:])
  File "/usr/lib/python2.5/site-packages/twisted/conch/ssh/transport.py",
line 336, in dispatchMessage
    messageNum, payload)
--- <exception caught here> ---
  File "/usr/lib/python2.5/site-packages/twisted/python/log.py", line 51, in
callWithLogger
    return callWithContext({"system": lp}, func, *args, **kw)
  File "/usr/lib/python2.5/site-packages/twisted/python/log.py", line 36, in
callWithContext
    return context.call({ILogContext: newCtx}, func, *args, **kw)
  File "/usr/lib/python2.5/site-packages/twisted/python/context.py", line
59, in callWithContext
    return self.currentContext().callWithContext(ctx, func, *args, **kw)
  File "/usr/lib/python2.5/site-packages/twisted/python/context.py", line
37, in callWithContext
    return func(*args,**kw)
  File "/usr/lib/python2.5/site-packages/twisted/conch/ssh/service.py", line
44, in packetReceived
    return f(packet)
  File "/usr/lib/python2.5/site-packages/twisted/conch/ssh/userauth.py",
line 262, in ssh_USERAUTH_FAILURE
    if method not in self.authenticatedWith and self.tryAuth(method):
  File "/usr/lib/python2.5/site-packages/twisted/conch/ssh/userauth.py",
line 234, in tryAuth
    return f()
  File "/usr/lib/python2.5/site-packages/twisted/conch/ssh/userauth.py",
line 338, in auth_publickey
    keyType = getNS(publicKey)[0]
  File "/usr/lib/python2.5/site-packages/twisted/conch/ssh/common.py", line
51, in getNS
    l, = struct.unpack('!L',s[c:c+4])
exceptions.TypeError: 'Key' object is unsubscriptable

Sincerely

Saki
# -*- coding: utf-8 -*-

#############################################################
# Executes a python code on the remote host using SSH
#############################################################

from twisted.conch.ssh import transport, userauth, connection, common, keys, channel
from twisted.internet import defer, protocol, reactor
from twisted.python import log
import sys, os, re

class lqSSHException(Exception):
    def __init__(self, errorcode, message):
        self.errorcode = errorcode
        self.message = message
    
    def __str__(self):
        return '%s %s' % (self.errorcode, self.message)

class lqSSHClient(protocol.ClientFactory):
    '''
    Executes a remote python code
    '''
    def __init__(self, username, server, keyfile, command):
        '''
        Necessary information to connect
        '''
        self.username = username
        self.server = server
        self.keyfile = keyfile
        self.command = command
        self._deferer_ = defer.Deferred()
        self._transport = None
    
    def buildProtocol(self, addr):
        '''
        Create the protocol after connection
        '''
        self._transport = lqSSHTransport(self.username, self.keyfile, self.command, self._deferer_)
        return self._transport
    
    def connect(self):
        reactor.connectTCP(self.server, 22, self)
        self._deferer_.addCallback(self.disconnectOnSuccess)
        self._deferer_.addErrback(self.disconnectOnError)
        return self._deferer_
    
    def disconnect(self):
        if self._transport is not None:
            self._transport.loseConnection()
            
    def disconnectOnError(self, failure):
        self.disconnect()
        raise failure
    
    def disconnectOnSuccess(self, result):
        self.disconnect()
        return result
    
    def clientConnectionFailed(self, connector, reason):
        '''
        When connection failed
        '''
        protocol.ClientFactory.clientConnectionFailed(self, connector, reason)
        log.err(reason)
        self._deferer_.errback(reason)
    
class lqSSHTransport(transport.SSHClientTransport):
    
    def __init__(self, username, keyfile, command, _deferer_):
        '''
        Necessary info to start SSH connection
        '''
        self.username = username
        self.keyfile = keyfile
        self.command = command
        self._deferer_ = _deferer_
        self.timeout = reactor.callLater(30, self.sshTransportTimeout)
    
    def verifyHostKey(self, hostKey, fingerprint):
        '''
        Check fingerprint
        '''
        # print 'host key fingerprint: %s' % fingerprint
        self.timeout.cancel()
        return defer.succeed(1)
    
    def connectionSecure(self):
        '''
        User authentication 
        '''
        self.requestService(
            lqSSHUserAuth(self.username, self.keyfile, self._deferer_,
                lqSSHConnection(self.command, self._deferer_)))

    def sshTransportTimeout(self):
        '''
        If the connection times out
        '''
        self.loseConnection()
        self._deferer_.errback(lqSSHException('408', 'Bağlantı zaman aşımına uğradı'))
    
class lqSSHUserAuth(userauth.SSHUserAuthClient):
    
    def __init__(self, username, keyfile, _deferer_, connection):
        '''
        User authentication
        '''
        userauth.SSHUserAuthClient.__init__(self, username, connection)
        self.keyfile = keyfile
        self._deferer_ = _deferer_
    
    def getPassword(self):
        '''
        Password auth is not provided, give error
        '''
        self.transport.sendDisconnect(transport.DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE, 'no more auths')
        self._deferer_.errback(lqSSHException('401', 'Wrong Key / Key authentication failed'))
        return
    
    def getGenericAnswers(self, name, instruction, questions):
        '''
        Keyboard interactive password is not provided, give erro
        '''
        self.transport.sendDisconnect(transport.DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE, 'no more auths')
        self._deferer_.errback(lqSSHException('401', 'User authentication failed'))
        return defer.fail(None)
    
    def getPublicKey(self):
        '''
        Create the public key
        '''
        if not os.path.exists(self.keyfile) or self.lastPublicKey:
            return
        
        return keys.Key.fromFile(self.keyfile+'.pub')
    
    def getPrivateKey(self):
        '''
        Create the private key
        '''
        return defer.succeed(keys.Key.fromFile(self.keyfile))

class lqSSHConnection(connection.SSHConnection):
    
    def __init__(self, command, _deferer_, *args, **kwargs):
        '''
        Tracks the SSH connection
        '''
        connection.SSHConnection.__init__(self)
        self.command = command
        self._deferer_ = _deferer_
    
    def serviceStarted(self):
        '''
        Runs just after the authentication finished
        '''
        self.openChannel(lqSSHChannel(self.command, self._deferer_, \
                                      2**16, 2**15, conn=self))
    
    def serviceStopped(self):
        '''
        Runs if the service stopped
        '''
    
class lqSSHChannel(channel.SSHChannel):
    
    name = 'session'
    
    _buffer = ''
    delimiter = '\n'
    MAX_LENGTH = 16384

    def __init__(self, command, _deferer_, *args, **kwargs):
        '''
        SSH channel parameters
        '''
        channel.SSHChannel.__init__(self, *args, **kwargs)
        self.command, self.input = command
        self._deferer_ = _deferer_
        self.success = True
        self.output = ''
    
    def openFailed(self, reason):
        self._deferer_.errback(reason)
    
    def channelOpen(self, ignoredData):
        d = self.conn.sendRequest(self, 'exec', common.NS(self.command), wantReply = True)
        d.addCallback(self._cbRequest)
    
    def _cbRequest(self, ignored):
        if self.input:
            self.write(self.input)
        self.conn.sendEOF(self)
    
    def dataReceived(self, data):
        """
        Splits the data to lines
        """
        lines  = (self._buffer+data).split(self.delimiter)
        self._buffer = lines.pop(-1)
        for line in lines:
            if len(line) > self.MAX_LENGTH:
                return self.lineLengthExceeded(line)
            else:
                self.lineReceived(line)
        if len(self._buffer) > self.MAX_LENGTH:
            return self.lineLengthExceeded(self._buffer)
    
    def lineReceived(self, line):
        code, message = line.strip().split(' ', 1)
        self.output += line + self.delimiter
        if code != '200':
            self.success = False
            self._deferer_.errback(lqSSHException(code, message))
    
    def lineLengthExceeded(line):
        self.success = False
        self._deferer_.errback(lqSSHException('500', 'Very long line'))
    
    def extReceived(self, dataType, data):
        print "500 Extended data:", dataType, data
    
    def closed(self):
        if self.success:
            self._deferer_.callback(self.output)
    
if __name__ == "__main__":
    
    USER = os.getlogin()
    HOST = 'localhost'
    KEYFILE = os.path.expanduser('~/.ssh/id_dsa')
    
    def done(result):
        print result
        reactor.stop()
    
    def failed(failure):
        print >> sys.stderr, failure.getErrorMessage()
        reactor.stop()
    
    remoteCode = "print 'Hello World'"
    
    client = lqSSHClient(USER, HOST, KEYFILE, ('python', remoteCode))
    d = client.connect()
    d.addCallback(done)
    d.addErrback(failed)
    
    reactor.run()
_______________________________________________
Twisted-Python mailing list
Twisted-Python@twistedmatrix.com
http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-python

Reply via email to