Skip to content

InsecureCacheControlAdapter raises ValueError when check_hostname enabled with trusted-host #13624

@andrewleech

Description

@andrewleech

InsecureCacheControlAdapter raises ValueError with certain SSL contexts

Summary

InsecureCacheControlAdapter (and InsecureHTTPAdapter) override cert_verify() to force verify=False but don't override get_connection_with_tls_context(). This creates a state where ssl_context.check_hostname=True and cert_reqs=CERT_NONE are set simultaneously, causing a ValueError during SSL connection setup.

Note: I do not believe this currently affects any "normal" pip usage, so I don't neccesarily expect it to be fixed here, I just thought it was worth raising in case you / the maintainers are interested and think it might affect pip directly.

Environment

  • Python: 3.10, 3.11, 3.12
  • pip: 24.2+
  • Triggered when using trusted-host configuration with certain SSL context modifications

Bug Report

Observed Behavior

When using pip install with a trusted-host configuration after certain global SSL modifications (e.g., packages that call truststore.inject_into_ssl()), pip fails with:

ValueError: Cannot set verify_mode to CERT_NONE when check_hostname is enabled.

Root Cause

The InsecureCacheControlAdapter and InsecureHTTPAdapter classes in pip/_internal/network/session.py only override cert_verify():

class InsecureCacheControlAdapter(CacheControlAdapter):
    def cert_verify(self, conn, url, verify, cert):
        super().cert_verify(conn=conn, url=url, verify=False, cert=cert)

The execution flow in HTTPAdapter.send():

  1. Calls get_connection_with_tls_context(request, verify, ...) with original verify value
  2. This creates an SSL context potentially with check_hostname=True
  3. Then calls cert_verify(conn, ..., verify, ...) which forces verify=False
  4. urllib3's connection code attempts: context.verify_mode = CERT_NONE
  5. Python's ssl module raises ValueError if check_hostname=True

Reproduction

This bug is exposed by the pip-system-certs package which calls truststore.inject_into_ssl(), but the underlying issue exists in pip's adapter implementation.

Minimal reproduction:

  1. Create pip.ini:
[global]
trusted-host = pypi.org
  1. Install a package that modifies SSL globally (e.g., pip-system-certs)

  2. Set environment: export PIP_CONFIG_FILE=/path/to/pip.ini

  3. Run: pip install anyio

  4. Observe: ValueError: Cannot set verify_mode to CERT_NONE when check_hostname is enabled.

Impact

  • Affects users combining trusted-host configuration with packages that modify global SSL behavior
  • Breaks pip functionality in corporate/institutional environments using custom CA certificates and trusted hosts
  • Currently affecting pip-system-certs users (see https://gitlab.com/alelec/pip-system-certs/-/issues/39)

Proposed Fix

Override get_connection_with_tls_context() to force verify=False consistently:

class InsecureCacheControlAdapter(CacheControlAdapter):
    def cert_verify(self, conn, url, verify, cert):
        super().cert_verify(conn=conn, url=url, verify=False, cert=cert)

    def get_connection_with_tls_context(self, request, verify, proxies=None, cert=None):
        # Force verify=False to prevent check_hostname conflict
        return super().get_connection_with_tls_context(
            request, verify=False, proxies=proxies, cert=cert
        )

The same fix should be applied to InsecureHTTPAdapter.

Credit

Analysis and proposed fix by @kjmrr: https://gitlab.com/alelec/pip-system-certs/-/issues/39#note_2812939884

Workaround

pip-system-certs has implemented a defensive runtime patch as a temporary workaround. The proper fix belongs upstream in pip.


Additional context:

This is a latent bug that requires specific conditions to trigger:

  1. Global SSL context modification (e.g., via truststore)
  2. Usage of trusted-host configuration
  3. The modified SSL contexts having check_hostname=True

While exposed by pip-system-certs, the bug exists in pip's implementation and could affect other scenarios where SSL contexts are modified globally.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions