This is an automated email from the ASF dual-hosted git repository. cmcfarlen pushed a commit to branch 10.2.x in repository https://gitbox.apache.org/repos/asf/trafficserver.git
commit 37219c9b390f4e2d250e872dc17d377213f4c4e2 Author: Bryan Call <[email protected]> AuthorDate: Tue Feb 10 15:09:34 2026 -0800 Fix autest compatibility with Fedora 43 / Python 3.14 (#12857) Fix autest compatibility with Python 3.14, OpenSSL 3.x, and Fedora 43 crypto-policies. Updates deprecated Python APIs, adds socket timeouts, skips legacy TLS tests when TLSv1.0/1.1 are disabled by system policy, and fixes a timing flake. (cherry picked from commit e0694752109acfde70eb09c499e3d8db64642ae3) --- tests/gold_tests/autest-site/conditions.test.ext | 53 ++++++++++++++++++++++ tests/gold_tests/autest-site/microserver.test.ext | 25 ++++++---- tests/gold_tests/autest-site/ports.py | 2 +- .../polite_hook_wait/polite_hook_wait.cc | 3 +- tests/gold_tests/tls/tls_client_versions.test.py | 1 + .../tls/tls_client_versions_minmax.test.py | 1 + 6 files changed, 74 insertions(+), 11 deletions(-) diff --git a/tests/gold_tests/autest-site/conditions.test.ext b/tests/gold_tests/autest-site/conditions.test.ext index ed2656a32c..e01fecdb85 100644 --- a/tests/gold_tests/autest-site/conditions.test.ext +++ b/tests/gold_tests/autest-site/conditions.test.ext @@ -16,6 +16,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import os import subprocess import json import re @@ -51,6 +52,57 @@ def IsOpenSSL(self): "SSL library is not OpenSSL") +def HasLegacyTLSSupport(self): + """Check if the system supports legacy TLS protocols (TLSv1.0 and TLSv1.1). + + Modern OpenSSL 3.x installations often disable these protocols entirely, + even if the openssl binary still accepts the -tls1 flag and lists TLSv1 ciphers. + + On Fedora/RHEL systems, the crypto-policies framework may disable legacy + TLS at runtime even when OpenSSL is compiled with support for it. This + causes 'openssl ciphers -v -tls1' to still list TLSv1 ciphers, but actual + TLS 1.0 connections will fail with "no protocols available". + + We only probe TLSv1.0 (not TLSv1.1 separately) because crypto-policies + always disable both legacy versions together. If TLSv1.0 is unavailable, + TLSv1.1 will be too. + + The check connects to localhost on a closed port to avoid any external + network dependency. A "connection refused" error means the TLS protocol + was available but nothing was listening; "no protocols available" means + the crypto-policy blocked TLSv1.0 entirely. + """ + + def check_tls1_support(): + try: + # Connect to localhost on a port nothing is listening on. + # This avoids external network dependency while still detecting + # whether the crypto-policy allows TLSv1.0. + result = subprocess.run( + ['openssl', 's_client', '-tls1', '-connect', '127.0.0.1:1'], + capture_output=True, + text=True, + timeout=5, + input='' # Don't wait for interactive input + ) + output = result.stdout + result.stderr + # "no protocols available" means TLSv1 is disabled by crypto-policy + if 'no protocols available' in output: + return False + # Connection refused or other errors mean TLSv1 was attempted + # (the protocol is available, just no server listening) + return True + except subprocess.TimeoutExpired: + # Timeout on localhost shouldn't happen, but if it does, + # assume TLSv1 is not available (safer than false positive) + return False + except Exception: + # If we can't determine, assume TLSv1 is not available (safer) + return False + + return self.Condition(check_tls1_support, "System does not support legacy TLS protocols (TLSv1.0/TLSv1.1)") + + def HasCurlVersion(self, version): return self.EnsureVersion(["curl", "--version"], min_version=version) @@ -118,6 +170,7 @@ ExtendCondition(HasOpenSSLVersion) ExtendCondition(HasProxyVerifierVersion) ExtendCondition(IsBoringSSL) ExtendCondition(IsOpenSSL) +ExtendCondition(HasLegacyTLSSupport) ExtendCondition(HasATSFeature) ExtendCondition(HasCurlVersion) ExtendCondition(HasCurlFeature) diff --git a/tests/gold_tests/autest-site/microserver.test.ext b/tests/gold_tests/autest-site/microserver.test.ext index e15547ff18..70c734f42d 100644 --- a/tests/gold_tests/autest-site/microserver.test.ext +++ b/tests/gold_tests/autest-site/microserver.test.ext @@ -17,6 +17,7 @@ # limitations under the License. import json +import os import socket import ssl @@ -109,7 +110,7 @@ def addSessionFromFiles(self, session_dir): # make headers with the key and values provided def makeHeader(self, requestString, **kwargs): headerStr = requestString + '\r\n' - for k, v in kwargs.iteritems(): + for k, v in kwargs.items(): headerStr += k + ': ' + v + '\r\n' headerStr = headerStr + '\r\n' return headerStr @@ -142,14 +143,20 @@ def uServerUpAndRunning(serverHost, port, isSsl, isIPv6, request, clientcert='', sock.sendall(request.encode()) decoded_output = '' - while True: - host.WriteDebug("??") - output = sock.recv(4096) # suggested bufsize from docs.python.org - host.WriteDebug("!!") - if len(output) <= 0: - break - else: - decoded_output += output.decode() + sock.settimeout(10.0) # 10 second timeout for recv + try: + while True: + host.WriteDebug("??") + output = sock.recv(4096) # suggested bufsize from docs.python.org + host.WriteDebug("!!") + if len(output) <= 0: + break + else: + decoded_output += output.decode('utf-8', errors='replace') + except socket.timeout: + host.WriteDebug(['uServerUpAndRunning', 'when'], "Socket timeout waiting for response") + sock.close() + return False sock.close() sock = None diff --git a/tests/gold_tests/autest-site/ports.py b/tests/gold_tests/autest-site/ports.py index bc4de3c1e3..0b1c7c8d6b 100644 --- a/tests/gold_tests/autest-site/ports.py +++ b/tests/gold_tests/autest-site/ports.py @@ -74,7 +74,7 @@ def PortOpen(port: int, address: str = None, listening_ports: Set[int] = None) - host.WriteDebug( 'PortOpen', f"Connection to port {port} succeeded, the port is open, " "and a future connection cannot use it") - except socket.error: + except OSError: host.WriteDebug( 'PortOpen', f"socket error for port {port}, port is closed, " "and therefore a future connection can use it") diff --git a/tests/gold_tests/pluginTest/polite_hook_wait/polite_hook_wait.cc b/tests/gold_tests/pluginTest/polite_hook_wait/polite_hook_wait.cc index 1e071a0dfc..3642bdbac0 100644 --- a/tests/gold_tests/pluginTest/polite_hook_wait/polite_hook_wait.cc +++ b/tests/gold_tests/pluginTest/polite_hook_wait/polite_hook_wait.cc @@ -202,8 +202,9 @@ Blocking_action::_thread_func(void *vba) ba->_cont_mutex_locked.store(true, std::memory_order_release); // This is a stand-in for some blocking call to validate the HTTP request in some way. + // Use a longer delay to account for slower systems and variable scheduling latency. // - std::this_thread::sleep_for(std::chrono::milliseconds(200)); + std::this_thread::sleep_for(std::chrono::milliseconds(500)); // Pass "validation" for first transaction, fail it for second. // diff --git a/tests/gold_tests/tls/tls_client_versions.test.py b/tests/gold_tests/tls/tls_client_versions.test.py index fcd53c265e..1f0343d9cd 100644 --- a/tests/gold_tests/tls/tls_client_versions.test.py +++ b/tests/gold_tests/tls/tls_client_versions.test.py @@ -24,6 +24,7 @@ Test TLS protocol offering based on SNI # for special domain foo.com only offer TLSv1 and TLSv1_1 Test.SkipUnless(Condition.HasOpenSSLVersion("1.1.1")) +Test.SkipUnless(Condition.HasLegacyTLSSupport()) # Define default ATS ts = Test.MakeATSProcess("ts", enable_tls=True) diff --git a/tests/gold_tests/tls/tls_client_versions_minmax.test.py b/tests/gold_tests/tls/tls_client_versions_minmax.test.py index 2ea7853580..9c63d0700d 100644 --- a/tests/gold_tests/tls/tls_client_versions_minmax.test.py +++ b/tests/gold_tests/tls/tls_client_versions_minmax.test.py @@ -24,6 +24,7 @@ Test TLS protocol offering based on SNI # for special domain foo.com only offer TLSv1 and TLSv1_1 Test.SkipUnless(Condition.HasOpenSSLVersion("1.1.1")) +Test.SkipUnless(Condition.HasLegacyTLSSupport()) # Define default ATS ts = Test.MakeATSProcess("ts", enable_tls=True)
