Daniel
Daniel

Reputation: 21

Python SSH server (twisted.conch) command filtering and port forwarding

I need to create an SSH server (twisted.conch has been chosen for the job) which would do the following:

  1. Perform port forwarding (the attached code does not do that and I do not know what to modify)
  2. Filter commands BEFORE they are executed (or at least log them before or after).

The code attached below creates a perfect SSH and SFTP server, BUT it is missing one main component - port forwarding (and command filtering, but that is not as important as port forwarding)

I searched where I possibly could, but could not find these two.. Please help me out, - it is the last peace of the puzzle.

#!/usr/bin/env python
from twisted.conch.unix import UnixSSHRealm
from twisted.cred.portal import Portal
from twisted.cred.credentials import IUsernamePassword
from twisted.cred.checkers import ICredentialsChecker
from twisted.cred.error import UnauthorizedLogin
from twisted.conch.ssh.factory import SSHFactory
from twisted.internet import reactor, defer
from twisted.conch.ssh.transport import SSHServerTransport
from twisted.conch.ssh.userauth import SSHUserAuthServer
from twisted.conch.ssh.connection import SSHConnection
from twisted.conch.ssh.keys import Key
from zope.interface import implements
from subprocess import Popen,PIPE
from crypt import crypt

publicKey = 'ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAGEArzJx8OYOnJmzf4tfBEvLi8DVPrJ3/c9k2I/Az64fxjHf9imyRJbixtQhlH9lfNjUIx+4LmrJH5QNRsFporcHDKOTwTTYLh5KmRpslkYHRivcJSkbh/C+BR3utDS555mV'
privateKey = """-----BEGIN RSA PRIVATE KEY-----
MIIByAIBAAJhAK8ycfDmDpyZs3+LXwRLy4vA1T6yd/3PZNiPwM+uH8Yx3/YpskSW
4sbUIZR/ZXzY1CMfuC5qyR+UDUbBaaK3Bwyjk8E02C4eSpkabJZGB0Yr3CUpG4fw
vgUd7rQ0ueeZlQIBIwJgbh+1VZfr7WftK5lu7MHtqE1S1vPWZQYE3+VUn8yJADyb
Z4fsZaCrzW9lkIqXkE3GIY+ojdhZhkO1gbG0118sIgphwSWKRxK0mvh6ERxKqIt1
xJEJO74EykXZV4oNJ8sjAjEA3J9r2ZghVhGN6V8DnQrTk24Td0E8hU8AcP0FVP+8
PQm/g/aXf2QQkQT+omdHVEJrAjEAy0pL0EBH6EVS98evDCBtQw22OZT52qXlAwZ2
gyTriKFVoqjeEjt3SZKKqXHSApP/AjBLpF99zcJJZRq2abgYlf9lv1chkrWqDHUu
DZttmYJeEfiFBBavVYIF1dOlZT0G8jMCMBc7sOSZodFnAiryP+Qg9otSBjJ3bQML
pSTqy7c3a2AScC/YyOwkDaICHnnD3XyjMwIxALRzl0tQEKMXs6hH8ToUdlLROCrP
EhQ0wahUTCk1gKA4uPD6TMTChavbh4K63OvbKg==
-----END RSA PRIVATE KEY-----"""

# check if username/password is valid
def checkPassword(username,password):
    try:
        ret=False
        if username and password:
            output=Popen(["grep",username,"/etc/shadow"],stdout=PIPE,stderr=PIPE).communicate()[0]
            hash=""
            if output:
                tmp=output.split(":")
                if tmp>=2:
                    hash=tmp[1]
                del tmp
            ret=crypt(password,hash)==hash
            del output,hash
    except Exception,e:
        ret=False
    return ret

# authorization methods
class XSSHAuth(object):
    credentialInterfaces=IUsernamePassword,implements(ICredentialsChecker)
    def requestAvatarId(self, credentials):
        #print "Credentials:",credentials.username,credentials.password
        if credentials.username=="root" and credentials.password and checkPassword(credentials.username,credentials.password):
            # successful authorization
            return defer.succeed(credentials.username)
        # failed authorization
        return defer.fail(UnauthorizedLogin("invalid password"))
class XSSHUserAuthServer(SSHUserAuthServer):
    def _ebPassword(self, reason):
        addr = self.transport.getPeer().address
        if addr.host!="3.22.116.85" and addr.host!="127.0.0.1":
            p1 = Popen(["iptables","-I","INPUT","-s",addr.host,"-j","DROP"], stdout=PIPE, stderr=PIPE)
            p1.communicate()
        print(addr.host, addr.port, self.user, self.method)
        self.transport.loseConnection()
        return defer.fail(UnauthorizedLogin("invalid password"))

# the transport class - we use it to log MOST OF THE ACTIONS executed thru the server
class XSSHTransport(SSHServerTransport):
    ourVersionString="SSH-2.0-X"
    logCommand=""
    def connectionMade(self):
        print "Connection made",self.getPeer()
        SSHServerTransport.connectionMade(self)
        #self.transport.loseConnection()
    def connectionLost(self,reason):
        print "Connection closed",self.getPeer()
        SSHServerTransport.connectionLost(self,reason)
    def dataReceived(self, data):
        SSHServerTransport.dataReceived(self,data)
    def dispatchMessage(self, messageNum, payload):
        SSHServerTransport.dispatchMessage(self,messageNum,payload)

# start the server
class XSSHFactory(SSHFactory):
    protocol=XSSHTransport
factory = XSSHFactory()
factory.publicKeys = {'ssh-rsa': Key.fromString(data=publicKey)}
factory.privateKeys = {'ssh-rsa': Key.fromString(data=privateKey)}
factory.services = {
    'ssh-userauth': XSSHUserAuthServer,
    'ssh-connection': SSHConnection
}
portal=Portal(UnixSSHRealm())
portal.registerChecker(XSSHAuth())
factory.portal=portal
reactor.listenTCP(22, factory)
reactor.run()

Upvotes: 2

Views: 686

Answers (2)

chzijian
chzijian

Reputation: 21

commands log may be can done in dataReceived(self, data):

def dataReceived(self, data):  
        SSHServerTransport.dataReceived(self,data)   
        self.buf += data  
        if data == '\r':  
           cmd = self.buf  
           self.buf = ''

But it can't handle the delete key, tab, arrow-up, arrow-down, and other special characters well. I want to know how you get the command last.

Upvotes: 0

Glyph
Glyph

Reputation: 31860

Since you are using UnixConchUser which implements global_tcpip_forward, it does in fact work. When I run your example and connect to it with ssh -L4321:remote.host:1234 root@localhost -p 2222 and then telnet localhost 4321, I get tunneled through to remote.host 1234. You will have to state your problem in more detail.

Upvotes: 0

Related Questions