PyserSSH-Docs/docs/history.md
2024-11-10 16:35:43 +07:00

8.5 KiB

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:

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.

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):

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:

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:

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.