mark nottingham

Python 2 and TLS SNI

Saturday, 27 December 2014

Python 2.7.9 was recently released, and that means that it supports TLS Server Name Indication.

I’ve updated REDbot to send SNI. Once you figure out how to do it, it’s pretty straightforward, but just to give people a little help, I thought I’d write down what I did.

REDbot uses Thor, which gets TLS support using the ever-helpful wrap_socket function. SNI is available via wrap_socket but NOT the top-level one in the module; instead, you need to use the new SSLContext object’s wrap_socket. The module has ssl.create_default_context() to give you one of these, or you can roll your own. I did the latter to get things going.

try:
    self.tls_context = sys_ssl.SSLContext(sys_ssl.PROTOCOL_SSLv23)
except AttributeError:
    self.tls_context = None
# ...
if self.tls_context:
    self.sock = self.tls_context.wrap_socket(
        self.sock,
        do_handshake_on_connect=False,
        server_hostname=self.host
    )
else: # server_hostname requires 2.7.9
    self.sock = sys_ssl.wrap_socket(
        self.sock,
        cert_reqs=sys_ssl.CERT_NONE,
        do_handshake_on_connect=False
    )

Note that it falls back if SSLContext isn’t present, because this object is new in 2.7.9. The important part here is passing server_hostname to wrap_socket when appropriate; that’s what gets you SNI.

Hope this helps; lots of Python apps still don’t send SNI from what I can see. If you maintain one, please update; it’s a small but important way to help improve the Web.