Skip to content

[Bug]: Python Client does not enforce its required TLS connection policy #30061

Closed

Description

@dengn

Existing issue check

  • Checked relevant Python SDK issues; this report concerns the SDK client path.

Environment

Repository main 3e50e1b32d8da61bbbf70c89989fb08b671809de, Python 3.12, SQLAlchemy 1.4/2.x. Initial findings were reproduced offline with synthetic inputs; real-service validation will be linked from the implementation PR.

Priority: P0.

Actual behavior and reproduction

Capture arguments passed by _create_engine() after connect(..., ssl_mode='required', ssl_ca=None). The stored required/certificate/identity flags are not propagated. ssl_mode='disabled' is also not consistently sent to the driver. Driver versions differ in opportunistic TLS/fallback behavior, so storing a mode alone does not enforce it.

Expected behavior

Required mode must verify certificate trust and hostname, use system roots if no CA file is supplied, and refuse peers without TLS before authentication. Disabled mode must disable TLS. Explicit certificate settings must have a defined policy.

Design and acceptance tests

Use an explicit validating SSLContext for required mode and a compatibility guard against pre-authentication plaintext fallback on older supported PyMySQL versions. Verify supplied/system CA handling, hostname mismatch, untrusted certificates, no-TLS peers and driver mode propagation with actual transport tests.

This is distinct from server handshake issue #29928. Public evidence must use locally generated test certificates and synthetic credentials, not real secrets.

Implementation tracking

#30074 — draft implementation with regression cases and recorded local validation. Pending review/merge; the issue closes on merge.

TLS stress also encountered the existing server transport issue #29928; that failed run is explicitly retained in the PR. Dedicated certificate/no-downgrade tests use precise failure assertions.

Activity

  1. Ariznawlll commented on Oct 20, 2026

    @Ariznawlll
    Collaborator

    测试版本

    • 分支:官方 main,完整 commit 21966565287edf9f7d7f519af60d269711608945;服务端 git_version()=2196656528。
    • 修复:PR fix(python-sdk): enforce handshake, TLS, redaction and async deadlines #30074,merge commit 8ef2eaf4d923bdb4136dd81a50850e8eced0f368;GitHub main compare 已确认包含该修复。
    • 部署方式:隔离 macOS arm64 mo-service(1 CN / 1 TN / 1 LogService,MySQL 127.0.0.1:6001),分别用独立的 TLS-enabled 与明文配置运行;本地生成仅用于验收的短期自签名证书,SAN 为 IP:127.0.0.1。Python 3.12 SDK,SQLAlchemy 2.1.5,分别使用 PyMySQL 1.2.3 与 1.0.0。未使用生产证书或凭据。
    • 测试日期:2026-10-20。

    原问题验证

    在真实 TLS-enabled MO 协议连接上,对两版 PyMySQL 各连续三轮运行 test_sdk_connection_contracts.py 的四个 TLS 用例:

    1. ssl_mode='required' + 指定受信本地 CA:认证和 SELECT 1 成功,底层 socket 实测为 ssl.SSLSocket。
    2. 同一证书改用主机名 localhost(证书仅含 IP SAN):证书主机名校验失败,SDK 未报告已连接。
    3. 不提供该 CA、仅使用系统根证书:本地自签名服务端被拒绝,SDK 未报告已连接。
    4. ssl_mode='disabled':底层 socket 确认为非 ssl.SSLSocket。

    以上四项在 PyMySQL 1.2.3 为每项 3/3,在 PyMySQL 1.0.0 也为每项 3/3。随后在同一隔离服务的明文配置下,对两版驱动分别三轮验证 required 连接被明确拒绝(不发生明文回退)、disabled 连接仍可用;两版各 2 项 × 3/3 通过。旧版 PyMySQL 的回退风险被实际覆盖。

    Happy Path 覆盖

    1. 受信 CA + 匹配 IP 的 required TLS 连接在两版驱动各 3/3 返回 SELECT 1 = 1,并确认为加密 socket。
    2. disabled 模式在 TLS-capable 和明文端点均 3/3 使用明文 socket,符合显式禁用策略。
    3. ssl_ca=None 时构造的 SDK SSLContext 实际加载 188 个当前系统根证书,verify_mode=CERT_REQUIRED、check_hostname=True;这是系统信任库配置检查,不声称连接了公网受信证书服务端。

    Unhappy Path 覆盖

    1. 主机名不匹配、自签名证书未被系统信任:两版驱动各路径 3/3 拒绝,连接状态保持未连接。
    2. 非 TLS 服务端:两版驱动各 3/3 在 required 模式明确拒绝,未降级为明文认证。
    3. 明文服务端上的合成错误密码路径(PyMySQL 1.2.3)3/3 抛连接错误并清除连接状态;未将失败后的 lazy engine 误判为成功连接。

    白盒验证

    当前 main 的 tls_options 使用 ssl.create_default_context(cafile=...) 建立证书和主机名校验;TLSRequiredConnection._request_authentication 在服务端不宣告 TLS 时于认证请求前抛错。离线 test_connection_contracts.py 与本轮实际协议测试覆盖两者。未运行 Python -race(不适用)。

    回归测试

    • PR fix(python-sdk): enforce handshake, TLS, redaction and async deadlines #30074 已加入 SDK 连接策略与错误处理回归;本轮两版 PyMySQL 的真实 TLS/明文定向三轮及当前 main 的 SDK offline 定向三轮均通过。本问题在 SDK transport 层,无法用普通 SQL MOTR 表达证书校验或认证前降级,因此未追加 MOTR。
    • PR #30074 的 CI 六个 SDK offline cell 和 CI Required 成功;SDK online job 被跳过。本轮补跑上述本地 online TLS 路径;没有运行完整 SDK online suite,也没有公网受信 CA 服务器的正向系统根握手测试。

    测试证据

    • 隔离 main 服务的 git_version()=2196656528;PyMySQL 1.2.3 和 1.0.0 的 TLS-enabled 四项用例各轮均显示 4 passed,明文两项各轮均显示 2 passed。成功路径断言 ssl.SSLSocket,失败路径断言证书错误/非 TLS 拒绝与状态清理。
    • 在线 TLS 定向用例源码 与 PR #30074。
    • 验收目标为 main SDK,不声称 v4.2.5 发布包或生产证书环境已通过。

    测试结论

    测试通过。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

kind/bugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions