This is an automated email from the ASF dual-hosted git repository. cmcfarlen pushed a commit to branch 10.1.x in repository https://gitbox.apache.org/repos/asf/trafficserver.git
commit b99e662cc9f1173bbc14540449c1be5925f2e102 Author: Bryan Call <[email protected]> AuthorDate: Fri Feb 13 15:33:13 2026 -0800 Check Curl support for TLS 1.0 and 1.1 (#12887) Skip TLSv1/TLSv1.1 subtests when curl cannot negotiate those protocols. Adds a curl-based capability probe alongside the existing OpenSSL check. (cherry picked from commit 8def184a6fca011141ee5470e78608ae31e24f9c) --- tests/gold_tests/autest-site/conditions.test.ext | 62 ++++++++++++++++++++++ tests/gold_tests/tls/tls_client_versions.test.py | 31 ++++++----- .../tls/tls_client_versions_minmax.test.py | 47 ++++++++-------- 3 files changed, 105 insertions(+), 35 deletions(-) diff --git a/tests/gold_tests/autest-site/conditions.test.ext b/tests/gold_tests/autest-site/conditions.test.ext index e01fecdb85..e228b19fae 100644 --- a/tests/gold_tests/autest-site/conditions.test.ext +++ b/tests/gold_tests/autest-site/conditions.test.ext @@ -146,6 +146,67 @@ def HasCurlOption(self, option): return self.CheckOutput(['curl', '--help', 'all'], default, "Curl needs to support option: {option}".format(option=option)) +def HasCurlTLSVersionSupport(self, tls_version): + """Check whether curl can attempt a given TLS version. + + This probes curl directly because OpenSSL capability checks do not always + reflect curl runtime policy behavior on hardened systems. + """ + + def check_curl_tls_support(): + # Map semantic versions used by tests to curl flags. + version_map = { + "1.0": ("--tlsv1", "1.0"), + "1.1": ("--tlsv1.1", "1.1"), + "1.2": ("--tlsv1.2", "1.2"), + "1.3": ("--tlsv1.3", "1.3"), + } + if tls_version not in version_map: + return False + + tls_flag, tls_max = version_map[tls_version] + try: + # Connect to localhost closed port to avoid network dependencies. + # "connection refused" means curl accepted the TLS flags and tried. + result = subprocess.run( + [ + "curl", + "-svk", + "--connect-timeout", + "2", + "--max-time", + "3", + tls_flag, + "--tls-max", + tls_max, + "https://127.0.0.1:1", + ], + capture_output=True, + text=True, + timeout=5, + ) + output = (result.stdout + result.stderr).lower() + unsupported_markers = [ + "unsupported protocol", + "no protocols available", + "option --tlsv", + "unknown option", + "is unknown", + ] + if any(marker in output for marker in unsupported_markers): + return False + + # Any attempt to connect implies curl accepted the TLS setting. + return True + except subprocess.TimeoutExpired: + return False + except Exception: + return False + + return self.Condition( + check_curl_tls_support, "Curl does not support TLSv{version} in this environment".format(version=tls_version)) + + def HasATSFeature(self, feature): val = self.Variables.get(feature, None) @@ -175,5 +236,6 @@ ExtendCondition(HasATSFeature) ExtendCondition(HasCurlVersion) ExtendCondition(HasCurlFeature) ExtendCondition(HasCurlOption) +ExtendCondition(HasCurlTLSVersionSupport) ExtendCondition(PluginExists) ExtendCondition(CurlUsingUnixDomainSocket) diff --git a/tests/gold_tests/tls/tls_client_versions.test.py b/tests/gold_tests/tls/tls_client_versions.test.py index 1f0343d9cd..134124eabf 100644 --- a/tests/gold_tests/tls/tls_client_versions.test.py +++ b/tests/gold_tests/tls/tls_client_versions.test.py @@ -25,6 +25,7 @@ Test TLS protocol offering based on SNI Test.SkipUnless(Condition.HasOpenSSLVersion("1.1.1")) Test.SkipUnless(Condition.HasLegacyTLSSupport()) +has_curl_tlsv1 = Condition.HasCurlTLSVersionSupport("1.0") # Define default ATS ts = Test.MakeATSProcess("ts", enable_tls=True) @@ -85,22 +86,24 @@ tr.ReturnCode = 35 tr.StillRunningAfter = ts # Target foo.com for TLSv1. Should succeed -tr = Test.AddTestRun("foo.com TLSv1") -tr.MakeCurlCommand( - "-v --ciphers DEFAULT@SECLEVEL=0 --tls-max 1.0 --tlsv1 --resolve 'foo.com:{0}:127.0.0.1' -k https://foo.com:{0}".format( - ts.Variables.ssl_port), - ts=ts) -tr.ReturnCode = 0 -tr.StillRunningAfter = ts +if has_curl_tlsv1: + tr = Test.AddTestRun("foo.com TLSv1") + tr.MakeCurlCommand( + "-v --ciphers DEFAULT@SECLEVEL=0 --tls-max 1.0 --tlsv1 --resolve 'foo.com:{0}:127.0.0.1' -k https://foo.com:{0}".format( + ts.Variables.ssl_port), + ts=ts) + tr.ReturnCode = 0 + tr.StillRunningAfter = ts # Target bar.com for TLSv1. Should fail -tr = Test.AddTestRun("bar.com TLSv1") -tr.MakeCurlCommand( - "-v --ciphers DEFAULT@SECLEVEL=0 --tls-max 1.0 --tlsv1 --resolve 'bar.com:{0}:127.0.0.1' -k https://bar.com:{0}".format( - ts.Variables.ssl_port), - ts=ts) -tr.ReturnCode = 35 -tr.StillRunningAfter = ts +if has_curl_tlsv1: + tr = Test.AddTestRun("bar.com TLSv1") + tr.MakeCurlCommand( + "-v --ciphers DEFAULT@SECLEVEL=0 --tls-max 1.0 --tlsv1 --resolve 'bar.com:{0}:127.0.0.1' -k https://bar.com:{0}".format( + ts.Variables.ssl_port), + ts=ts) + tr.ReturnCode = 35 + tr.StillRunningAfter = ts # Target bar.com for TLSv1_2. Should succeed tr = Test.AddTestRun("bar.com TLSv1_2") 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 9c63d0700d..4c0d1742e8 100644 --- a/tests/gold_tests/tls/tls_client_versions_minmax.test.py +++ b/tests/gold_tests/tls/tls_client_versions_minmax.test.py @@ -25,6 +25,8 @@ Test TLS protocol offering based on SNI Test.SkipUnless(Condition.HasOpenSSLVersion("1.1.1")) Test.SkipUnless(Condition.HasLegacyTLSSupport()) +has_curl_tlsv1 = Condition.HasCurlTLSVersionSupport("1.0") +has_curl_tlsv1_1 = Condition.HasCurlTLSVersionSupport("1.1") # Define default ATS ts = Test.MakeATSProcess("ts", enable_tls=True) @@ -90,31 +92,34 @@ tr.ReturnCode = 35 tr.StillRunningAfter = ts # Target foo.com for TLSv1. Should succeed -tr = Test.AddTestRun("foo.com TLSv1") -tr.MakeCurlCommand( - "-v --ciphers DEFAULT@SECLEVEL=0 --tls-max 1.0 --tlsv1 --resolve 'foo.com:{0}:127.0.0.1' -k https://foo.com:{0}".format( - ts.Variables.ssl_port), - ts=ts) -tr.ReturnCode = 0 -tr.StillRunningAfter = ts +if has_curl_tlsv1: + tr = Test.AddTestRun("foo.com TLSv1") + tr.MakeCurlCommand( + "-v --ciphers DEFAULT@SECLEVEL=0 --tls-max 1.0 --tlsv1 --resolve 'foo.com:{0}:127.0.0.1' -k https://foo.com:{0}".format( + ts.Variables.ssl_port), + ts=ts) + tr.ReturnCode = 0 + tr.StillRunningAfter = ts # Target foo.com for TLSv1_1. Should succeed -tr = Test.AddTestRun("foo.com TLSv1_1") -tr.MakeCurlCommand( - "-v --ciphers DEFAULT@SECLEVEL=0 --tls-max 1.1 --tlsv1.1 --resolve 'foo.com:{0}:127.0.0.1' -k https://foo.com:{0}".format( - ts.Variables.ssl_port), - ts=ts) -tr.ReturnCode = 0 -tr.StillRunningAfter = ts +if has_curl_tlsv1_1: + tr = Test.AddTestRun("foo.com TLSv1_1") + tr.MakeCurlCommand( + "-v --ciphers DEFAULT@SECLEVEL=0 --tls-max 1.1 --tlsv1.1 --resolve 'foo.com:{0}:127.0.0.1' -k https://foo.com:{0}".format( + ts.Variables.ssl_port), + ts=ts) + tr.ReturnCode = 0 + tr.StillRunningAfter = ts # Target bar.com for TLSv1. Should fail -tr = Test.AddTestRun("bar.com TLSv1") -tr.MakeCurlCommand( - "-v --ciphers DEFAULT@SECLEVEL=0 --tls-max 1.0 --tlsv1 --resolve 'bar.com:{0}:127.0.0.1' -k https://bar.com:{0}".format( - ts.Variables.ssl_port), - ts=ts) -tr.ReturnCode = 35 -tr.StillRunningAfter = ts +if has_curl_tlsv1: + tr = Test.AddTestRun("bar.com TLSv1") + tr.MakeCurlCommand( + "-v --ciphers DEFAULT@SECLEVEL=0 --tls-max 1.0 --tlsv1 --resolve 'bar.com:{0}:127.0.0.1' -k https://bar.com:{0}".format( + ts.Variables.ssl_port), + ts=ts) + tr.ReturnCode = 35 + tr.StillRunningAfter = ts # Target bar.com for TLSv1_2. Should succeed tr = Test.AddTestRun("bar.com TLSv1_2")
