Commit c4f093d2 authored by Vincent Pelletier's avatar Vincent Pelletier

http: Fix server socket context renewal.

Work around a python bug (both present in 2.7.15 and 3.6.6) which prevents
changing the ssl context on a listening socket.
Reported: https://bugs.python.org/issue34747
parent b22a50a8
......@@ -204,6 +204,7 @@ def getSSLContext(
hostname,
cau,
cas,
renew=False, # Force renewal when True - used in tests
):
"""
Build a new SSLContext with updated CA certificates, CRL and server key pair,
......@@ -239,7 +240,9 @@ def getSSLContext(
if os.path.exists(server_key_path):
old_crt_pem = utils.getCert(server_key_path)
old_crt = utils.load_certificate(old_crt_pem, cas_certificate_list, None)
if old_crt.not_valid_after - threshold_delta < datetime.datetime.utcnow():
if renew or (
old_crt.not_valid_after - threshold_delta < datetime.datetime.utcnow()
):
new_key = utils.generatePrivateKey(key_len)
new_key_pem = utils.dump_privatekey(new_key)
new_crt_pem = cas.renew(
......@@ -609,9 +612,21 @@ def main(argv=None, until=utils.until):
hostname=hostname,
cau=cau,
cas=cas,
renew=True,
)
for https in https_list:
https.socket.context = ssl_context
ssl_socket = https.socket
try:
ssl_socket.context = ssl_context
except AttributeError:
# Workaround for python bug 34747: changing a listening
# SSLSocket's SSL context fails on
# "self._sslobj.context = context" while "self._sslobj" is only
# set on connected sockets.
# Luckily is it done just after updating "self._context" which is
# what is actually used when accepting a connection - so the update
# is actually successful.
pass
if next_backup is None:
next_deadline = next_ssl_update
else:
......
......@@ -31,6 +31,7 @@ import random
import shutil
import socket
import sqlite3
import ssl
import sys
import tempfile
import threading
......@@ -1976,6 +1977,30 @@ class CaucaseTest(unittest.TestCase):
updater_event.set()
updater_thread.join(2)
def testHttpSSLRenewal(self):
# http server is started without backups by default, so deadline is ssl
# renewal deadline. So fake expiration and verify the certificate presented
# by https server changed.
parsed_url = urlparse.urlparse(self._caucase_url)
# Sanity check
assert parsed_url.scheme == 'http', parsed_url.scheme
address = (
parsed_url.hostname,
443 if parsed_url.port == 80 else parsed_url.port + 1,
)
# Wait for server to sleep, and clear.
self.assertTrue(self._server_until.wait())
# Pull certificate.
before = ssl.get_server_certificate(address)
# Wake server so it renews certificate.
self._server_until.action = ON_EVENT_EXPIRE
self._server_event.set()
# Wait for it to be back to sleep.
self.assertTrue(self._server_until.wait())
# Verify renewal happened.
after = ssl.get_server_certificate(address)
self.assertNotEqual(before, after)
def testHttpNetlocIPv6(self):
"""
Test that it is possible to use a literal IPv6 as netloc.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment