2-way SSL with CherryPy
From CherryPy 3.0 and onwards, one-way SSL can be turned on simply by pointing开发者_如何学JAVA 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?
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.
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.
If the current version of CherryPy does not support client certificate verification, it is possible to configure CherryPy to listen to 127.0.0.1:80, install HAProxy to listen to 443 and verify client side certificates and to forward traffic to 127.0.0.1:80 HAProxy is simple, light, fast and reliable. An example of HAProxy configuration
精彩评论