InsecureCacheControlAdapter raises ValueError when check_hostname enabled with trusted-host
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-hostconfiguration 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():
- Calls
get_connection_with_tls_context(request, verify, ...)with originalverifyvalue - This creates an SSL context potentially with
check_hostname=True - Then calls
cert_verify(conn, ..., verify, ...)which forcesverify=False - urllib3's connection code attempts:
context.verify_mode = CERT_NONE - Python's ssl module raises
ValueErrorifcheck_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:
- Create
pip.ini:
[global]
trusted-host = pypi.org
-
Install a package that modifies SSL globally (e.g.,
pip-system-certs) -
Set environment:
export PIP_CONFIG_FILE=/path/to/pip.ini -
Run:
pip install anyio -
Observe:
ValueError: Cannot set verify_mode to CERT_NONE when check_hostname is enabled.
Impact
- Affects users combining
trusted-hostconfiguration 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:
- Global SSL context modification (e.g., via truststore)
- Usage of
trusted-hostconfiguration - 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.
Hi @VaradharajSudhakar, thanks for taking the time to report this issue and provide details.
I tried reproducing the behavior using your steps, but wasn’t able to trigger the error. As you mentioned, it might depend on specific environment or SSL configuration differences.
I think it’s best to wait for a maintainer or someone more familiar with pip’s SSL handling to take a closer look and confirm whether there’s an underlying issue here.
Thanks, yep the main known way to trigger it is to have truststore injected into the python environment before pip itself runs, as is done with the current versions of https://gitlab.com/alelec/pip-system-certs
While I'm the author/maintainer of pip-system-certs I no longer actively use it myself so am not that clear on the severity of this issue / or whether it really is an issue at all. But anything relating to potential corner-cases in ssl interactions is worth at least briefly looking at I feel.
Hi @andrewleech thanks for your report and authoring the package pip-system-certs, I used it a little around 2019 and have seen it used a bunch in companies I worked for.
I wasn't aware it was still relevant with truststore now available, I guess it's hooking into additional System APIs?
@sethmlarson if you have a moment at some point, would you mind reviewing the above report?
@andrewleech Thanks for maintaining pip-system-certs and for adopting truststore for the library :) Looking at the project, I was wondering what the long-term plan was for the project, given pip has adopted native truststore support: could pip-system-certs detect pip that supports truststore by default and not enable the patching?
I wasn't aware it was still relevant with truststore now available
Yeah I'd thought it might fade out of use, however the point of difference has been for it to enable system certificates (now via truststore) in the entire python environment by default so is useful for adding that support to third party libraries / applications without modifying their code.
pip has adopted native truststore support: could pip-system-certs detect pip that supports truststore by default and not enable the patching?
Yes that would have been preferable now, however the architecture of pip-system-certs is to just run the truststore.inject_into_ssl() function at python environment start up, before any code has been run.
Thinking now as I type this however, sys.argv should be populated already at the point it runs, so perhaps detecting that pip is being run would be possible that way to avoid running truststore.inject_into_ssl() that way.
That would avoid this issue certainly, though the suggestion was that this could still be considered a hidden bug in pip