# 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 PyserSSH Before creating PyserSSH, we evaluated several existing libraries: ### SSHim SSShim 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:** ```python 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.*)')).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. ### Twisted 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 ([Thank For this gist](https://gist.github.com/michaellihs/d2070d7a6d3bb65be18c)):** ```python 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. ### Paramiko Paramiko is a Python library for SSH protocol implementation. It supports both SSH client and server functionalities but is primarily used for SSH client tasks. It is well-documented and widely used, providing a more straightforward approach for SSH operations compared to more complex frameworks. **Example Code:** ```python import paramiko class SimpleSSHServer(paramiko.ServerInterface): def __init__(self): self.event = paramiko.Event() self.event.set() def check_channel_request(self, kind, channel): if kind == 'session': return paramiko.OPEN_SUCCEEDED return paramiko.FAILURE def check_auth_password(self, username, password): if username == 'test' and password == 'password': return paramiko.AUTH_SUCCESSFUL return paramiko.AUTH_FAILED def handle_client(client_socket): transport = paramiko.Transport(client_socket) transport.add_server_key(paramiko.RSAKey.generate(2048)) server = SimpleSSHServer() transport.start_server(server=server) channel = transport.accept(20) if channel is None: print("No channel.") return channel.send("Hello from Paramiko SSH server!") channel.recv(1024) transport.close() if __name__ == "__main__": import socket server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket.bind(('0.0.0.0', 2200)) server_socket.listen(100) while True: client_socket, addr = server_socket.accept() handle_client(client_socket) ``` **Observation:** Paramiko is straightforward and well-suited for implementing SSH clients and servers. However, it requires additional code for handling specific server features and authentication mechanisms. ## Reconversion to PyserSSH In contrast to SSHim, Twisted, and Paramiko, PyserSSH aims to simplify SSH server implementation. The following example illustrates how PyserSSH makes it easier to handle user input and interactions: ```python 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.