Skip to content
New issue

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

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

Already on GitHub? Sign in 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

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

No branches or pull requests

3 participants