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.
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.
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()