Create SSL context using PROTOCOL_TLS, fallback to highest supported version
Zuul test: tests.unit.test_scheduler.TestSchedulerSSL.test_jobs_executed fails on ubuntu focal with the following exception: Traceback (most recent call last): File "/home/gchauvel/zuul/zuul/.tox/py38/lib/python3.8/site-packages/gear/__init__.py", line 2835, in _doConnectLoop self.connectLoop() File "/home/gchauvel/zuul/zuul/.tox/py38/lib/python3.8/site-packages/gear/__init__.py", line 2865, in connectLoop c = context.wrap_socket(c, server_side=True) File "/usr/lib/python3.8/ssl.py", line 500, in wrap_socket return self.sslsocket_class._create( File "/usr/lib/python3.8/ssl.py", line 1040, in _create self.do_handshake() File "/usr/lib/python3.8/ssl.py", line 1309, in do_handshake self._sslobj.do_handshake() ssl.SSLError: [SSL] internal error (_ssl.c:1108) This is due to libssl1.1 being compiled with "-DOPENSSL_TLS_SECURITY_LEVEL=2" and gear forcing TLSv1.0 Extracted from ubuntu source package: The default security level for TLS connections was increased from level 1 to level 2. This moves from the 80 bit security level to the 112 bit security level and will require 2048 bit or larger RSA and DHE keys, 224 bit or larger ECC keys, SHA-2, TLSv1.2 or DTLSv1.2. Allowing to negotiate TLS to the highest available version between server and client solves the issue, provided that TLSv1.2 is useable. The option is supported by in the latest version of all pythons >=3.5 [1][2][3]. Unfortunately Xenial doesn't have latest 3.5 and lacks the ssl.PROTOCOL_TLS definition. We provide a fallback to select the highest version of TLS supported in that case. There is some risk using the fallback beacuse both the client and server need to agree on the version supported in this case. Xenial python 3.5 does support TLSv1_2 which means that for all practical purposes TLS v1.2 should be available on all platforms that gear runs avoiding this problem. Disable TLSv1.3: According to https://bugs.python.org/issue43622#msg389497, an event on ssl socket can happen without data being available at application level. As gear is using a polling loop with multiple file descriptors and ssl socket used as a blocking one, a blocked state could happen. This is highlighted by Zuul SSL test: TestSchedulerSSL, where such blocked state appears consistently. note: gear tests and zuul tests are ok when using TLSv1.2 but the previous behavior could also happen [1] https://docs.python.org/2.7/library/ssl.html?highlight=protocol_tls#ssl.PROTOCOL_TLS [2] https://docs.python.org/3.5/library/ssl.html?highlight=protocol_tls#ssl.PROTOCOL_TLS [3] https://docs.python.org/3/library/ssl.html?highlight=protocol_tls#ssl.PROTOCOL_TLS Change-Id: I5efb6c0576987815c5b93f8bc4020cdee2898d04
This commit is contained in:
parent
29e9d1fa99
commit
66ba8442dc
@ -91,6 +91,44 @@ def convert_to_bytes(data):
|
||||
return data
|
||||
|
||||
|
||||
def best_tls_version():
|
||||
if hasattr(ssl, 'PROTOCOL_TLS'):
|
||||
return ssl.PROTOCOL_TLS
|
||||
# Note there is some risk in selecting tls 1.2 if available
|
||||
# as both the client and server may not support it and need 1.1
|
||||
# or 1.0. However, a xenial installation with python 3.5 does
|
||||
# support 1.2 which is probably as old a setup as we need to worry
|
||||
# about.
|
||||
elif hasattr(ssl, 'PROTOCOL_TLSv1_2'):
|
||||
return ssl.PROTOCOL_TLSv1_2
|
||||
elif hasattr(ssl, 'PROTOCOL_TLSv1_1'):
|
||||
return ssl.PROTOCOL_TLSv1_1
|
||||
elif hasattr(ssl, 'PROTOCOL_TLSv1'):
|
||||
return ssl.PROTOCOL_TLSv1
|
||||
else:
|
||||
raise ConnectionError('No supported TLS version available.')
|
||||
|
||||
|
||||
def create_ssl_context():
|
||||
tls_version = best_tls_version()
|
||||
context = ssl.SSLContext(tls_version)
|
||||
|
||||
# Disable TLSv1.3
|
||||
# According to https://bugs.python.org/issue43622#msg389497, an event on
|
||||
# ssl socket can happen without data being available at application level.
|
||||
# As gear is using a polling loop with multiple file descriptors and ssl
|
||||
# socket used as a blocking one, a blocked state could happen.
|
||||
# This is highlighted by Zuul SSL test: TestSchedulerSSL, where such
|
||||
# blocked state appears consistently.
|
||||
# note: gear tests and zuul tests are ok for TLSv1.2 but this behavior
|
||||
# could also happen
|
||||
if (hasattr(ssl, 'PROTOCOL_TLS') and
|
||||
tls_version == ssl.PROTOCOL_TLS):
|
||||
context.options |= ssl.OP_NO_TLSv1_3
|
||||
|
||||
return context
|
||||
|
||||
|
||||
class Task(object):
|
||||
def __init__(self):
|
||||
self._wait_event = threading.Event()
|
||||
@ -209,7 +247,7 @@ class Connection(object):
|
||||
|
||||
if self.use_ssl:
|
||||
self.log.debug("Using SSL")
|
||||
context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
|
||||
context = create_ssl_context()
|
||||
context.verify_mode = ssl.CERT_REQUIRED
|
||||
context.check_hostname = False
|
||||
context.load_cert_chain(self.ssl_cert, self.ssl_key)
|
||||
@ -2862,7 +2900,7 @@ class Server(BaseClientServer):
|
||||
self.log.debug("Accepting new connection")
|
||||
c, addr = self.socket.accept()
|
||||
if self.use_ssl:
|
||||
context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
|
||||
context = create_ssl_context()
|
||||
context.verify_mode = ssl.CERT_REQUIRED
|
||||
context.load_cert_chain(self.ssl_cert, self.ssl_key)
|
||||
context.load_verify_locations(self.ssl_ca)
|
||||
|
Loading…
Reference in New Issue
Block a user