Question

Every time a client connects to the server I get a prompt to enter the PEM pass phrase. I want to only enter it on startup of the server and not have to enter it again. The Twisted Matrix framework only requires the pass phrase on startup, why not gevent? Or am I using gevent wrong? The code below works fine if I use a certificate that require no PEM pass phrase, but I want to use a certificate with a pass phrase.

from gevent.server import StreamServer
from gevent.pool import Pool
from gevent import monkey

class SocketPool(object):

    def __init__(self): self.pool = Pool(1000)

    def listen(self, socket):
        while True:
            line = socket.recv(1024)
            if not line: break 
            print line
            socket.close()
            break

    def add_handler(self, socket, address):
        print "connection made:", address
        if self.pool.full(): raise Exception("At maximum pool size")
        else: self.pool.spawn(self.listen, socket)

    def shutdown(self): self.pool.kill()

monkey.patch_all()
sockPool = SocketPool() 
server = StreamServer(('', 5000), sockPool.add_handler, keyfile='key.pem', certfile='cert.pem')
server.serve_forever()
Was it helpful?

Solution

There doesn't appear to be an easy way to fix this on python 2.7. Internally, gevent.server uses the python ssl module which calls the OpenSSL library from C. The _ssl.sslwrap() function creates a new SSLContext for each call and this is why you are prompted for the key passphrase each time a connection is made.

Twisted uses pyOpenSSL instead of the ssl module. In pyOpenSSL you create your SSLContext first and that single context is used each time a connection is made. Thus you are only prompted for the passphrase once when using Twisted.

Python 3.2 adds an ssl.SSLContext class with a wrap_socket() method. If you were on this version or later then you could patch the gevent code to work more like Twisted, i.e. use a single SSLContext and replace the call to _ssl.sslwrap() with a call to the wrap_socket() method. It doesn't appear that gevent is ported to python3, though.

You could offload SSL handling by gevent by placing an SSL proxy in front so incoming SSL connections to a public port are proxied to your gevent server on an unencrypted internal port. You could write your own proxy with Twisted or use a dedicated proxy like stunnel. However, this might negate some or all of the reasons you wanted to use gevent in the first place.

You could patch your _ssl module either to cache SSLContexts or to use another way of accepting the passphrase. _ssl in python 2.7 uses the default passphrase callback, which interactively prompts to enter the passphrase. This wouldn't be that difficult, but it requires that you know at least a bit of C, the python C API, and the OpenSSL API, plus you would have to build and override the module everywhere you deploy.

You could patch gevent with your own code to wrap a socket with SSL. This can be done entirely in python with a library like pyOpenSSL, but getting it right is probably a fair amount of work.

Finally, you could just use an unencrypted key. Perhaps putting the key on a RAM disk or on an encrypted file system (or both) would address whatever security requirements you have.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top