Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

2-way SSL with CherryPy

From CherryPy 3.0 and onwards, one-way SSL can be turned on simply by pointing to the server certificate and private key, like this:

import cherrypy

class HelloWorld(object):
    def index(self):
        return "Hello SSL World!"
    index.exposed = True

cherrypy.server.ssl_certificate = "keys/server.crt"
cherrypy.server.ssl_private_key = "keys/server.crtkey" 
cherrypy.quickstart(HelloWorld())

This enables clients to validate the server's authenticity. Does anyone know whether CherryPy supports 2-way ssl, e.g. where the server can also check client authenticity by validating a client certificate?

If yes, could anyone give an example how is that done? Or post a reference to an example?

like image 751
tpj Avatar asked Dec 13 '10 09:12

tpj


2 Answers

It doesn't out of the box. You'd have to patch the wsgiserver to provide that feature. There is a ticket (and patches) in progress at http://www.cherrypy.org/ticket/1001.

like image 85
fumanchu Avatar answered Oct 16 '22 02:10

fumanchu


I have been looking for the same thing. I know there are some patches on the CherryPy site.

I also found the following at CherryPy SSL Client Authentication. I haven't compared this vs the CherryPy patches but maybe the info will be helpful.

We recently needed to develop a quick but resilient REST application and found that CherryPy suited our needs better than other Python networking frameworks, like Twisted. Unfortunately, its simplicity lacked a key feature we needed, Server/Client SSL certificate validation. Therefore we spent a few hours writing a few quick modifications to the current release, 3.1.2. The following code snippets are the modifications we made:

cherrypy/_cpserver.py

@@ -55,7 +55,6 @@ instance = None ssl_certificate = None ssl_private_key
= None
+ ssl_ca_certificate = None nodelay = True

def __init__(self):

cherrypy/wsgiserver/__init__.py

@@ -1480,6 +1480,7 @@
# Paths to certificate and private key files ssl_certificate = None ssl_private_key = None
+    ssl_ca_certificate = None

def __init__(self, bind_addr, wsgi_app, numthreads=10, server_name=None, max=-1, request_queue_size=5, timeout=10, shutdown_timeout=5):

@@ -1619,7 +1620,9 @@

self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) if self.nodelay: self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
-        if self.ssl_certificate and self.ssl_private_key:
+        if self.ssl_certificate and self.ssl_private_key and \
+            self.ssl_ca_certificate:
+ if SSL is None: raise ImportError("You must install pyOpenSSL to use HTTPS.")

@@ -1627,6 +1630,11 @@ ctx = SSL.Context(SSL.SSLv23_METHOD) ctx.use_privatekey_file(self.ssl_private_key) ctx.use_certificate_file(self.ssl_certificate)
+            x509 = crypto.load_certificate(crypto.FILETYPE_PEM,
+                open(self.ssl_ca_certificate).read())
+            store = ctx.get_cert_store()
+            store.add_cert(x509)
+            ctx.set_verify(SSL.VERIFY_PEER | SSL.VERIFY_FAIL_IF_NO_PEER_CERT, lambda *x:True) self.socket = SSLConnection(ctx, self.socket) self.populate_ssl_environ()

The above patches require the inclusion of a new configuration option inside of the CherryPy server configuration, server.ssl_ca_certificate. This option identifies the certificate authority file that connecting clients will be validated against, if the client does not present a valid client certificate it will close the connection immediately.

Our solution has advantages and disadvantages, the primary advantage being if the connecting client doesn’t present a valid certificate it’s connection is immediately closed. This is good for security concerns as it does not permit the client any access into the CherryPy application stack. However, since the restriction is done at the socket level the CherryPy application can never see the client connecting and hence the solution is somewhat inflexible.

An optimal solution would allow the client to connect to the CherryPy socket and send the client certificate up into the application stack. Then a custom CherryPy Tool would validate the certificate inside of the application stack and close the connection if necessary; unfortunately because of the structure of CherryPy’s pyOpenSSL implementation it is difficult to retrieve the client certificate inside of the application stack.

Of course the patches above should only be used at your own risk. If you come up with a better solution please let us know.

like image 28
TWA Avatar answered Oct 16 '22 00:10

TWA