Search code examples
pythonopenssltornadosni

SNI (Server Name Indication) with tornado


As the title states, I'd like to figure out how to use SNI (server name indication) with the tornado webserver.

I'd like to be able to present a particular certificate based on the hostname of the given request.


Solution

  • After looking more into this I found the solution using a sni_callback from ssl.SSLContext.

    That will give you a method that contains the hostname before a TLS handshake is established.

    Within the servername_callback method you can then chose what certificate to load based on the hostname.

    Working solution
    import tornado.ioloop
    import tornado.web
    import tornado.httpserver
    import ssl
    
    class MainHandler(tornado.web.RequestHandler):
    
        def get(self):
            self.write('Hello, world')
    
    application = tornado.web.Application([
        (r'/', MainHandler)
    ])
    
    def servername_callback(sock, hostname, cb_context):
        # hostname contains the hostname that the client is requesting
        print("hostname", hostname)
    
        # now that we have the hostname we can dynamically pick the correct certificate
        ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
        # this part is up to you to store via a config file or even in a database
        ssl_context.load_cert_chain(certfile="/path/to/cert", keyfile="/path/to/key")
    
        sock.context = ssl_context
    
    if __name__ == '__main__':
    
        ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
        ssl_context.sni_callback = servername_callback
    
        http_server = tornado.httpserver.HTTPServer(application, ssl_options=ssl_context)
        http_server.listen(443)
    
        tornado.ioloop.IOLoop.instance().start()