PyserSSH-Docs/docs/history.md
2024-09-03 20:39:31 +07:00

7.3 KiB

Development History of PyserSSH

PyserSSH Version 1.0 (Filename: "test277.py") Date Created: September 3, 2023

PyserSSH began as an experimental project aimed at addressing the lack of suitable SSH server libraries available for Python. Initially, it was developed solely for research purposes. The project was driven by the need for a customizable SSH server solution that could be tailored to specific requirements not met by existing libraries. As the project progressed, its potential to evolve into a fully functional and practical library became evident, leading to the decision to develop PyserSSH into a more robust tool for use in both software and server applications.

Before creating PyserSSH, we evaluated two existing libraries:

  1. SSShim

    • Description: SSHim is an SSH server library designed primarily for testing SSH clients. It is discontinued and not suitable for real-world use. It allows simple interactions with the user, such as input text, but lacks ease of use for more complex scenarios.
    • Example Code:
      import logging
      logging.basicConfig(level='DEBUG')
      logger = logging.getLogger()
      
      import sshim, re
      
      def hello_world(script):
          script.write('Please enter your name: ')
          groups = script.expect(re.compile('(?P<name>.*)')).groupdict()
          logger.info('%(name)s just connected', **groups)
          script.writeline('Hello %(name)s!' % groups)
      
      server = sshim.Server(hello_world, port=3000)
      try:
          server.run()
      except KeyboardInterrupt:
          server.stop()
      
    • Observation: While SSHim successfully receives user input, it is not very user-friendly or flexible for real-world applications.
  2. Twisted

    • Description: Twisted is a comprehensive event-driven networking engine in Python. It provides an SSH server implementation but is known for its complexity, making it less accessible to new users.
    • Example Code:
      from twisted.conch import avatar, recvline
      from twisted.conch.interfaces import IConchUser, ISession
      from twisted.conch.ssh import factory, keys, session
      from twisted.conch.insults import insults
      from twisted.cred import portal, checkers
      from twisted.internet import reactor
      from zope.interface import implements
      
      class SSHDemoProtocol(recvline.HistoricRecvLine):
          def __init__(self, user):
             self.user = user
      
          def connectionMade(self):
              recvline.HistoricRecvLine.connectionMade(self)
              self.terminal.write("Welcome to my test SSH server.")
              self.terminal.nextLine()
              self.do_help()
              self.showPrompt()
      
          def showPrompt(self):
              self.terminal.write("$ ")
      
          def getCommandFunc(self, cmd):
              return getattr(self, 'do_' + cmd, None)
      
          def lineReceived(self, line):
              line = line.strip()
              if line:
                  print(line)
                  with open('logfile.log', 'w') as f:
                      f.write(line + '\n')
                  cmdAndArgs = line.split()
                  cmd = cmdAndArgs[0]
                  args = cmdAndArgs[1:]
                  func = self.getCommandFunc(cmd)
                  if func:
                      try:
                          func(*args)
                      except Exception as e:
                          self.terminal.write("Error: %s" % e)
                          self.terminal.nextLine()
                  else:
                      self.terminal.write("No such command.")
                      self.terminal.nextLine()
              self.showPrompt()
      
          def do_help(self):
              publicMethods = [funcname for funcname in dir(self) if funcname.startswith('do_')]
              commands = [cmd.replace('do_', '', 1) for cmd in publicMethods]
              self.terminal.write("Commands: " + " ".join(commands))
              self.terminal.nextLine()
      
          def do_echo(self, *args):
              self.terminal.write(" ".join(args))
              self.terminal.nextLine()
      
          def do_whoami(self):
              self.terminal.write(self.user.username)
              self.terminal.nextLine()
      
          def do_quit(self):
              self.terminal.write("Thanks for playing!")
              self.terminal.nextLine()
              self.terminal.loseConnection()
      
          def do_clear(self):
              self.terminal.reset()
      
      class SSHDemoAvatar(avatar.ConchUser):
          implements(ISession)
      
          def __init__(self, username):
              avatar.ConchUser.__init__(self)
              self.username = username
              self.channelLookup.update({'session': session.SSHSession})
      
          def openShell(self, protocol):
              serverProtocol = insults.ServerProtocol(SSHDemoProtocol, self)
              serverProtocol.makeConnection(protocol)
              protocol.makeConnection(session.wrapProtocol(serverProtocol))
      
          def getPty(self, terminal, windowSize, attrs):
              return None
      
          def execCommand(self, protocol, cmd):
              raise NotImplementedError()
      
          def closed(self):
              pass
      
      class SSHDemoRealm(object):
          implements(portal.IRealm)
      
          def requestAvatar(self, avatarId, mind, *interfaces):
              if IConchUser in interfaces:
                  return interfaces[0], SSHDemoAvatar(avatarId), lambda: None
              else:
                  raise NotImplementedError("No supported interfaces found.")
      
      def getRSAKeys():
          with open('id_rsa') as privateBlobFile:
              privateBlob = privateBlobFile.read()
              privateKey = keys.Key.fromString(data=privateBlob)
      
          with open('id_rsa.pub') as publicBlobFile:
              publicBlob = publicBlobFile.read()
              publicKey = keys.Key.fromString(data=publicBlob)
      
          return publicKey, privateKey
      
      if __name__ == "__main__":
          sshFactory = factory.SSHFactory()
          sshFactory.portal = portal.Portal(SSHDemoRealm())
      
          users = {'admin': 'aaa', 'guest': 'bbb'}
          sshFactory.portal.registerChecker(
              checkers.InMemoryUsernamePasswordDatabaseDontUse(**users))
          pubKey, privKey = getRSAKeys()
          sshFactory.publicKeys = {'ssh-rsa': pubKey}
          sshFactory.privateKeys = {'ssh-rsa': privKey}
          reactor.listenTCP(22222, sshFactory)
          reactor.run()
      
    • Observation: While Twisted provides a comprehensive SSH server implementation, it is quite complex for new users and requires significant setup and configuration.

Reconversion to PyserSSH:

In contrast to SSHim and Twisted, PyserSSH aims to simplify SSH server implementation. The following example illustrates how PyserSSH makes it easier to handle user input and interactions:

from PyserSSH import Server, Send, wait_input, AccountManager

account = AccountManager(anyuser=True)
server = Server(account, inputsystem=False)

@server.on_user("connect")
def hello_world(client):
    name = wait_input(client, "Please enter your name: ")
    Send(client, f"Hello {name}!")

server.run("your private key")

Advantages of PyserSSH:

  • Simplicity: PyserSSH provides an intuitive and user-friendly API, making it easier for developers to create and manage SSH servers.
  • Flexibility: PyserSSH offers the functionality needed for more complex interactions while maintaining a straightforward setup and usage.