Repository navigation
Conversation
Python built against OpenSSL 4 no longer defines the deprecated ssl.PROTOCOL_TLSv1, PROTOCOL_TLSv1_1 and PROTOCOL_TLSv1_2 constants. The classic, asyncio and X DevAPI network modules read them at import time, so `import mysql.connector` and `import mysqlx` fail with AttributeError before any connection is attempted. When tls_versions is given, build the context from ssl.PROTOCOL_TLS_CLIENT and express the requested versions as a minimum_version/maximum_version range, replacing the version-specific protocols and the deprecated OP_NO_TLSv1* options. Both APIs are present on every supported Python (3.10+) with OpenSSL 1.1.1, 3.x and 4. The range is only ever narrowed: the minimum is raised and the maximum lowered, never the reverse. A stricter system-wide policy, such as OpenSSL's MinProtocol (which distribution Pythons honor), therefore still applies, just as it did with the version-specific protocols. Negotiation is unchanged: a request limited to TLSv1.2 is still capped at TLSv1.2, and a request that includes TLSv1.3 stays uncapped as it was with PROTOCOL_TLS. Certificate verification is unchanged too. The classic connectors already set check_hostname and verify_mode explicitly, and the X DevAPI keeps CERT_NONE unless VERIFY_IDENTITY or ssl-ca is used. The TLSv1 and TLSv1.1 map entries are removed. They were unreachable, because option validation rejects both versions as unacceptable. Add tests that assert the exact negotiated TLS version for each tls_versions combination, and that a stricter default minimum is kept. The existing tls_versions tests are skipped on servers newer than 8.0.27, so this path had no coverage on current servers.
faratech
force-pushed
the
openssl4-tls-versions
branch
from
October 10, 2026 06:32
40a253b to
d7a79c8
Compare
|
Hi, thank you for submitting this pull request. In order to consider your code we need you to sign the Oracle Contribution Agreement (OCA). Please review the details and follow the instructions at https://oca.opensource.oracle.com/ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
When Python is linked against OpenSSL 4 (Ubuntu 26.10's Python 3.14.8, for example), the
sslmodule no longer defines the deprecatedPROTOCOL_TLSv1,PROTOCOL_TLSv1_1orPROTOCOL_TLSv1_2. Three modules read these constants at import time:mysql/connector/network.pymysql/connector/aio/network.pymysqlx/connection.pyThe
tryblocks around them only catchImportError, so both packages fail to import:This happens with the latest release (26.7.0) and with current
trunk, and it affects every application, including ones that never passtls_versions.Fix
When
tls_versionsis given, build the context fromssl.PROTOCOL_TLS_CLIENTand express the requested versions withSSLContext.minimum_version/maximum_version. This replaces the version-specific protocols and the deprecatedOP_NO_TLSv1*options. Both APIs exist on every supported Python (3.10+, which requires OpenSSL 1.1.1 or newer), so the change also works on OpenSSL 1.1.1 and 3.x.The change is backwards compatible:
["TLSv1.2"]is still capped at TLS 1.2. A list that includesTLSv1.3stays uncapped, as it was withPROTOCOL_TLS.MinProtocol = TLSv1.3(which Debian/Ubuntu and Fedora/RHEL Pythons honor), still applies, as it did with the version-specific protocols. Settingminimum_versionunconditionally would silently allow TLS 1.2 on such systems.check_hostnameandverify_modeexplicitly after creating the context. The X DevAPI path explicitly keepsCERT_NONEunlessVERIFY_IDENTITYorssl-cais used, which matches what the oldPROTOCOL_TLSv1_2/PROTOCOL_TLScontexts did by default.TLSv1/TLSv1.1map entries are removed. They were unreachable, because option validation already rejects both versions asUNACCEPTABLE_TLS_VERSIONS.PROTOCOL_TLSv1_2,PROTOCOL_TLSandOP_NO_*usages, and the DeprecationWarnings they emit on 3.10+, are gone from these paths.Tests
The PR adds:
test_tls_versions_negotiatedintest_connection.py,test_aio_connection.pyandtest_mysqlx_connection.py. Each test connects with everytls_versionscombination and asserts the exact negotiated version fromSsl_version/Mysqlx_ssl_version. The existingtest_get_connection_with_tls_version/test_get_session_with_tls_versiontests are skipped on servers newer than 8.0.27, sotls_versionscurrently has no coverage against current servers.test_build_ssl_context_tls_versionsandtest_build_ssl_context_keeps_stricter_defaultintest_network.py. These check the context's version range, and that a stricter default minimum is not lowered.Upstream suite
Modules
network,connection,aio_connectionandmysqlx_connectionwere run withunittests.pyagainst a bootstrapped MySQL Server 26.7.0, using the pure-Python implementation:AttributeErroron import (all modules)To confirm the mysqlx result, I reran it with a local workaround for that zstd issue (a fresh decompressor per frame). The results then match OpenSSL 3.5.9 except
test_compression_sizes, which fails on the compressing side of the same zstd issue (5174: Invalid compressed frame).A mutation check confirmed the new tests catch both kinds of regression:
["TLSv1.2"]negotiates TLSv1.3.minimum_version:test_build_ssl_context_keeps_stricter_defaultfails.TLS behavior comparison
To compare trunk and this PR directly, I ran their TLS setup code (
build_ssl_context+switch_to_sslfor sync and asyncio,SocketStream.set_sslfor mysqlx) through real handshakes against local TLS servers: 1.2-only, 1.3-only, 1.2–1.3, and 1.2–1.3 with an untrusted certificate. The cases cover every combination oftls_versions(none, 1.2, 1.3, both),ssl_verify_cert,ssl_verify_identity/ X DevAPIssl-mode,ssl_ca, hostname match or mismatch, andtls_ciphersuites. That is 1,360 cases per run, and each records the negotiated version and cipher, or the failure reason:MinProtocol = TLSv1.3, vs trunk's results restricted to TLS 1.3Unrelated pre-existing issues found while testing
These appear on unmodified trunk too, and are not changed here:
tests/data/sslhave expired, sotest_connect_with_unix_socket(sync and asyncio) and mysqlx'stest_ssl_connectionerror withCERTIFICATE_EXPIREDon every configuration.mysqlx/protocol.pyuses the stdlibcompression.zstd. ItsZstdDecompressorhandles a single frame, and the compressor reuses one instance across messages, so X Protocolzstd_streamcompression raisesEOFError: Already at the end of a Zstandard frame.on the second compressed message.MySQLSocket.switch_to_ssl()callsStreamWriter.start_tls()withoutserver_hostname. Withssl_verify_identity=Trueit fails withValueError: check_hostname requires server_hostname, whether or nottls_versionsis given. It works on 3.10, which uses the connector's own workaround. I tested only 3.10 and 3.14, but 3.11+ presumably fails the same way, since that is where the nativestart_tls()is used.OCA
I have signed the Oracle Contributor Agreement (Mike Fara), and it is pending approval.
🤖 Generated with Claude Code