This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: SSLSocket.context cannot be changed on non-connected sockets
Type: behavior Stage:
Components: Library (Lib), SSL Versions: Python 3.9, Python 3.8, Python 3.7, Python 3.6, Python 2.7
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: christian.heimes Nosy List: christian.heimes, vincent-nexedi
Priority: low Keywords:

Created on 2018-09-20 07:39 by vincent-nexedi, last changed 2022-04-11 14:59 by admin.

Messages (4)
msg325847 - (view) Author: Vincent Pelletier (vincent-nexedi) Date: 2018-09-20 07:39
From ssl.py, both on 2.7.15 and 3.6.6:
class SSLSocket(...):
...
    @context.setter
    def context(self, ctx):
        self._context = ctx
        self._sslobj.context = ctx

_sslobj is only set when socket is connected. While this is not a big issue for client sockets as user could just wrap the socket with correct context to begin with, and not a big issue for server sockets for the same reason, it is an issue for listening sockets: they are never connected, by definition, and do not care about _sslobj: upon accept() they only use self._context to wrap created socket.

Suggested fix:
    @context.setter
    def context(self, ctx):
        self._context = ctx
        if self._sslobj:
            self._sslobj.context = ctx
(consistently with how _sslobj is evaluated as a boolean elsewhere in the same class)

Suggested workaround (ex: if this fix is not backported to 2.7):
    try:
        ssl_socket.context = new_context
    except AttributeError:
        pass
as _context is changed first, and it's all that matters.
msg327737 - (view) Author: Christian Heimes (christian.heimes) * (Python committer) Date: 2018-10-15 08:36
How did you run into the issue? There is no reason to ever replace the context of a listening socket. The context setter is designed for SNI-based virtual hosting to replace the context of a bound server-side connection. You never change the listening socket.

We can probably fix the issue, though.
msg327740 - (view) Author: Vincent Pelletier (vincent-nexedi) Date: 2018-10-15 09:31
The reason which led me into this is server certificate renewal: my service crashed on that setter 2 months after starting, when it received a new certificate.

I toyed with the idea of closing the listening sockets, but without closing would be even better - code changing socket certificate is much simpler than having to stop all worker treads (SocketServer.ThreadingMixIn), close the listening sockets, and start them all again in new threads with a new wrap_socket'ed listening socket.
msg389272 - (view) Author: Vincent Pelletier (vincent-nexedi) Date: 2021-03-22 02:40
Added: affects Python 3.9

This bug is still preventing (...or shall I say "discourages", as the setter is effective but raises) server-side SSL certificate reloading on long-running services.
This codepath on listening sockets is necessary for seamless certificate renewal, so the new certificate is used on any accepted connection past the setter call.

Is there anything that needs to be changed to the fix I proposed ?
Should I open a merge/pull request somewhere ?
History
Date User Action Args
2022-04-11 14:59:06adminsetgithub: 78928
2021-03-22 02:40:15vincent-nexedisetmessages: + msg389272
versions: + Python 3.9
2018-10-15 09:31:06vincent-nexedisetmessages: + msg327740
2018-10-15 08:36:48christian.heimessetpriority: normal -> low

messages: + msg327737
versions: + Python 3.7, Python 3.8
2018-10-15 06:53:31vincent-nexedisetassignee: christian.heimes

components: + Library (Lib), SSL
nosy: + christian.heimes
2018-09-20 07:39:05vincent-nexedicreate