Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

SSLCertVerificationError - unable to get local issuer certificate #6717

Closed
DanSIntel opened this issue May 22, 2024 · 5 comments
Closed

SSLCertVerificationError - unable to get local issuer certificate #6717

DanSIntel opened this issue May 22, 2024 · 5 comments

Comments

@DanSIntel
Copy link

Version 2.32.0 introduced changes and improvements with SSLContext as specified in the release history:

Improvements - verify=True now reuses a global SSLContext which should improve request time variance between first and subsequent requests. It should also minimize certificate load time on Windows systems when using a Python version built with OpenSSL 3.x. (#6667)

We are facing issue making http requests to webservers which are signed by a local root ca.
The certificate chain is installed correctly on the Windows station and version 2.31.1 is working as expected.

Versions 2.32.x are throwing an error: SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1125)')))

This is a code example that works in 2.31.1 and does not in 2.32.x

from requests.adapters import HTTPAdapter
import requests
from requests.packages.urllib3.util.ssl_ import create_urllib3_context

class SSLContextAdapter(HTTPAdapter):
    def init_poolmanager(self, *args, **kwargs):
        context = create_urllib3_context()
        context.load_default_certs()  # this loads the system's CA certificates
        kwargs['ssl_context'] = context
        return super().init_poolmanager(*args, **kwargs)

SESSION = requests.Session()
SESSION.mount('https://', SSLContextAdapter())
SESSION.get(MY_URL_SIGNED_BY_LOCAL_ROOT_CA, headers = headers, verify  = True)

After looking at the lastest changes, if we modifiy our code its working but i dont think that calling the private global _preloaded_ssl_context is the right way:

from requests.adapters import HTTPAdapter, _preloaded_ssl_context
import requests


class SSLContextAdapter(HTTPAdapter):
    def init_poolmanager(self, *args, **kwargs):
        _preloaded_ssl_context.load_default_certs() # this loads the system's CA certificates
        return super().init_poolmanager(*args, **kwargs)

SESSION = requests.Session()
SESSION.mount('https://', SSLContextAdapter())
SESSION.get(MY_URL_SIGNED_BY_LOCAL_ROOT_CA, headers = headers, verify  = True)

What is the recommended way for using load_default_certs() with version 2.32.x if the usage has changed or alternatively can you confirm if this is a bug?

@nateprewitt
Copy link
Member

Hi @DanSIntel, this looks like the same root issue described in #6715. Can you take a look at the proposed patch in #6716 and let us know if the new API proposal meets your use case. From the code you provided, I would think you should be able to do this through pool_kwargs.

I'd propose we close this as a duplicate of #6715 if the above is agreeable and we'll track progress in the PR.

@sigmavirus24
Copy link
Contributor

Duplicate of #6715

@sigmavirus24 sigmavirus24 marked this as a duplicate of #6715 May 22, 2024
@DanSIntel
Copy link
Author

@nateprewitt can you provide an example how using the changes in #6716 can work with my custom sslcontext adapter?

@nateprewitt
Copy link
Member

Do the same thing you're doing now but in your adapters __init__. Then you can patch the return value the same way we're using _preloaded_ssl_context now (ref).

Something like this:

class SSLContextAdapter(HTTPAdapter):
    def __init__(
        self,
        pool_connections=DEFAULT_POOLSIZE,
        pool_maxsize=DEFAULT_POOLSIZE,
        max_retries=DEFAULT_RETRIES,
        pool_block=DEFAULT_POOLBLOCK,
    ):
        super().__init__()
        self.custom_context = create_urllib3_context()
        # Any cert modifications can be done here (if you need this per request,
        # do it in the build_connection_pool_key_attributes below.)
        self.custom_context.load_default_certs()
    
    [...]
    
    def build_connection_pool_key_attributes(self, request, verify, cert=None):
        host_params, pool_kwargs = super().build_connection_pool_key_attributes(request, verify, cert)
        pool_kwargs['ssl_context'] = self.custom_context  # you can put this behind a verify is True conditional too
        return host_params, pool_kwargs

@DanSIntel
Copy link
Author

thanks, i verified that it is working so v2.32.3 should do the job

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants