Friday, April 12, 2013

SSH Into Your Python Server

Have you ever wanted to see what's going on inside your Python server? With Crochet and Twisted, you can add a Python prompt to you process that is accessible via SSH, allowing you to poke around in the internals of your running program. Here's an example session to a Flask server:
$ ssh admin@localhost -p 5022
admin@localhost's password: ******
>>> app
< object at 0x28a96d0>
>>> app.url_map
Map([<Rule '/' (HEAD, OPTIONS, GET) -> index>,
 <Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>])
>>> from twisted.internet import reactor
>>> reactor._selectables
{9: <SSHServerTransport #0 on 5022>, 3: <<class 'twisted.internet.tcp.Port'> of twisted.conch.manhole_ssh.ConchFactory on 5022>, 6: <twisted.internet.posixbase._UnixWaker object at 0x28a2510>}
The code to start the SSH server has quite a lot of boilerplate, so I filed a ticket to provide a utility function. If you're using the system Twisted, you may need to install Twisted's Conch package, e.g. apt-get install python-twisted-conch on Ubuntu.
import logging

from flask import Flask
from crochet import setup, in_reactor

# Web server:
app = Flask(__name__)

def index():
    return "Welcome to my boring web server!"

def start_ssh_server(reactor, port, username, password,
    Start an SSH server on the given port, exposing a Python
    prompt with the given namespace.
    from twisted.conch.insults import insults
    from twisted.conch import manhole, manhole_ssh
    from twisted.cred.checkers import (
        InMemoryUsernamePasswordDatabaseDontUse as MemoryDB)
    from twisted.cred.portal import Portal

    sshRealm = manhole_ssh.TerminalRealm()
    def chainedProtocolFactory():
        return insults.ServerProtocol(manhole.Manhole,
    sshRealm.chainedProtocolFactory = chainedProtocolFactory

    portal = Portal(
        sshRealm, [MemoryDB(**{username: password})])
    reactor.listenTCP(port, manhole_ssh.ConchFactory(portal),

if __name__ == '__main__':
    import sys
    logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)
        5022, "admin", "secret", {"app": app}).wait()

1 comment:

  1. Thanks a lot Itamar. This is a great way to illustrate students while training them in python.