You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
How can I use a custom SSLContext / PyOpenSSLContext when creating a Client? #924
[x ] I looked up "How to do ... in HTTPX" on a search engine and didn't find any information.
I asked the community chat for help but didn't get an answer.
Question
Can I create a httpx.Client with my own ssl.SSLContext or urllib3.contrib.pyopenssl.PyOpenSSLContext instead of passing in cert/key/verify?
Background
I found httpx while looking for requests syntax + asyncio support. It looks like a great project, thanks for all the work you've put into it.
One very oft-asked feature for requests.py was making requests with user-provided SSLContexts 2118. Eventually that was resolved by allowing us to pass SSLContexts to Adapters, then mounting the adapter onto a session. As a real world example, I have used pypki2 and requests_pkcs12 at different times to create Session's from PKCS12/X509 certificates instead of PEM format that I believe vanilla requests and httpx require. There is a requests_pkcs12 author blog post with more background.
My understanding from reading httpx documentation is that a httpx.Client is roughly similar to a requests.Session and the Dispatcher API will be roughly similar to Adapters. @tomchristie mentions configuring an ssl_context in 768 - Dispatcher API but I didn't understand how to use that in practice.
@sethmlarson suggests httpx.Client(verify=ssl_context) in 469, which looked similar to my use-case but not identical. When I tried that with httpx 0.12.1 and 0.13.0.dev, I got TypeError: expected str, bytes or os.PathLike object, not PyOpenSSLContext on Client init.
Thanks.
(courtesy tagging @rashley-iqt (requests_pkcs12) and @gershwinlabs (pypki2) )
Thanks for bringing this use-case to my attention. Got me thinking how this use case can be reconciled with my other thoughts on safe high level TLS APIs.
Reacted by Matt Kafonek, Florimond Manca, Ryan and Kim Christie
How can I use a custom SSLContext / PyOpenSSLContext when creating a Client?
Yup, the verify argument optionally takes an SSLContext instance. So let's thrash out if there's an issue here or not.
One thing that I did notice prompted by this ticket, is that even though that's a supported usage, and is type annotated as bool|str|SSLContext, the SSLContext usage isn't actually currently noted in the docs. https://www.python-httpx.org/advanced/#ssl-certificates
That's something we should clearly improve. We ought to include a very minimal possible example, such as...
I got TypeError: expected str, bytes or os.PathLike object, not PyOpenSSLContext on Client init.
Could you include a traceback with that?
We don't have any instances of PathLike in the httpx codebase, and the example above works as expected, so it's not immediately obvious how to replicate this?
Thanks for the reply @tomchristie. I went back and did some more tests on httpx 0.12.1 and 0.13.0.dev. It looks like the verify= syntax does work for both httpx.get(url, verify=context) and httpx.Client(verify=context).get(url) for ssl.SSLContext objects generated by pypki2. I should have tested that initially.
For requests_pkcs12 generated urllib3.contrib.pyopenssl.PyOpenSSLContext objects, I get the same general error. Here's the full traceback from httpx 0.12.1:
If it helps, I'm using my own internal CA to sign the .p12 certs, and that CA is used by the internal server I'm connecting to. I can get to the internal site using the same .p12 and CA with requests_pkcs12. It's still possible I'm messing up the cafile loading though?
If you want custom SSL context support, we do have that, but you need to be using an SSLContext instance, from the stdlib.
We don't have support for PyOpenSSLContext, and the SSL configuration is (correctly) falling through the if isinstance(self.verify, ssl.SSLContext) check, and raising an error at the point that it attempts to treat the context as a string.
# Allow passing in our own SSLContext object that's pre-configured.
context=self.verify
self._load_client_certs(context)
returncontext
elifisinstance(self.verify, bool):
ca_bundle_path=self.DEFAULT_CA_BUNDLE_PATH
elifPath(self.verify).exists():
ca_bundle_path=Path(self.verify)
I'd suggest you start by looking into setting up a custom SSLContext instance, and passing that to verify=....
If there's some capabilities exposed by the third party pyopenssl package, that aren't also present in Python 3's ssl implementation, then it's feasible that there's a valid feature request in here that we could work towards, tho it looks like pyopenssl is more aimed at improving SSL capabilities for Python 2.7-3.5.
Reacted by Matt Kafonek, Ben Eshed, whiteowl3 and Pavel Nikonorov
If there's some capabilities exposed by the third party pyopenssl package, that aren't also present in Python 3's ssl implementation, then it's feasible that there's a valid feature request in here that we could work towards, tho it looks like pyopenssl is more aimed at improving SSL capabilities for Python 2.7-3.5.
There absolutely is such functionality.
from urllib3.contrib.pyopenssl import inject_into_urllib3
from urllib3 import PoolManager
from OpenSSL import SSL
#cert, key = generate with pyopenssl, or convert from cryptography, generate with certauth or other tools
#cert is a SSL.X509, key is a SSL.PKey
ctx = SSL.Context()
ctx.use_cert(cert)
ctx.use_privatekey(key)
inject_into_urllib3()
with PoolManager(ssl_context=ctx) as pool:
pool.request(...)
This demonstrates the generation and use of an ssl context which never writes its certs or keys to disk, something that, in all of pythonland, is only possible using pyopenssl, because only pyopenssl uses the cffi bindings to the openssl functionality. important peps were proposed and withdrawn, and to date, we appear to be no closer to loading keys from memory in std python than we were ten years ago. httpx solving for this use case, especially async, would be a big deal, as people usually move to other languages to get this done.
Activity
sethmlarson commentedon May 2, 2020
Thanks for bringing this use-case to my attention. Got me thinking how this use case can be reconciled with my other thoughts on safe high level TLS APIs.
lovelydinosaur commentedon May 4, 2020
Heya,
Yup, the
verifyargument optionally takes anSSLContextinstance. So let's thrash out if there's an issue here or not.One thing that I did notice prompted by this ticket, is that even though that's a supported usage, and is type annotated as
bool|str|SSLContext, the SSLContext usage isn't actually currently noted in the docs. https://www.python-httpx.org/advanced/#ssl-certificatesThat's something we should clearly improve. We ought to include a very minimal possible example, such as...
Could you include a traceback with that?
We don't have any instances of
PathLikein thehttpxcodebase, and the example above works as expected, so it's not immediately obvious how to replicate this?kafonek commentedon May 4, 2020
Thanks for the reply @tomchristie. I went back and did some more tests on httpx 0.12.1 and 0.13.0.dev. It looks like the
verify=syntax does work for bothhttpx.get(url, verify=context)andhttpx.Client(verify=context).get(url)forssl.SSLContextobjects generated bypypki2. I should have tested that initially.For
requests_pkcs12generatedurllib3.contrib.pyopenssl.PyOpenSSLContextobjects, I get the same general error. Here's the full traceback from httpx 0.12.1:If it helps, I'm using my own internal CA to sign the .p12 certs, and that CA is used by the internal server I'm connecting to. I can get to the internal site using the same .p12 and CA with requests_pkcs12. It's still possible I'm messing up the cafile loading though?
Thanks.
lovelydinosaur commentedon May 4, 2020
If you want custom SSL context support, we do have that, but you need to be using an
SSLContextinstance, from the stdlib.We don't have support for
PyOpenSSLContext, and the SSL configuration is (correctly) falling through theif isinstance(self.verify, ssl.SSLContext)check, and raising an error at the point that it attempts to treat the context as a string.httpx/httpx/_config.py
Lines 107 to 115 in d34c89a
I'd suggest you start by looking into setting up a custom
SSLContextinstance, and passing that toverify=....If there's some capabilities exposed by the third party
pyopensslpackage, that aren't also present in Python 3'ssslimplementation, then it's feasible that there's a valid feature request in here that we could work towards, tho it looks likepyopensslis more aimed at improving SSL capabilities for Python 2.7-3.5.beneshed commentedon Nov 11, 2020
Seconding this, pyopenssl allows the use of loading certificates from memory instead of from disk
whiteowl3 commentedon Feb 26, 2022
There absolutely is such functionality.
This demonstrates the generation and use of an ssl context which never writes its certs or keys to disk, something that, in all of pythonland, is only possible using pyopenssl, because only pyopenssl uses the cffi bindings to the openssl functionality. important peps were proposed and withdrawn, and to date, we appear to be no closer to loading keys from memory in std python than we were ten years ago. httpx solving for this use case, especially async, would be a big deal, as people usually move to other languages to get this done.