Patching ssl.py to workaround ssl lack of relocability
Hi there, This is hopefully the right mailing for this question. We are shipping our own python interpreter in our product, and following some discussions on https://mail.python.org/pipermail/python-dev/2017-January/147282.html, we understand shipping the certificate from certifi in our python is the best approach on Linux/OS X. Unfortunately, ssl hardcodes at compilation time the default location of certificate. I could workaround this at the python level by patching ssl.SSLContext.load_default_certs to look as follows: def load_default_certs(...): .... if sys.platform == "win32": ... else: prefix = os.path.normpath(sys.prefix) default_cert = os.path.join(prefix, "ssl", "cert.pem") if os.path.isfile(default_cert): self.load_verify_locations(default_cert) else: self.set_default_verify_paths() While this seems to work, my lack of knowledge in all things related to security and ssl in particular makes me worry to patch anything in there. Is this a sane approach ? If not, is there a better way ? Thank you, David
On 2017-03-01 15:59, David Cournapeau wrote:
Hi there,
This is hopefully the right mailing for this question. We are shipping our own python interpreter in our product, and following some discussions on https://mail.python.org/pipermail/python-dev/2017-January/147282.html, we understand shipping the certificate from certifi in our python is the best approach on Linux/OS X.
Yeah, it's the easiest option for you. I don't necessarily agree it's the best option.
Unfortunately, ssl hardcodes at compilation time the default location of certificate. I could workaround this at the python level by patching ssl.SSLContext.load_default_certs to look as follows:
Small correction: The location of the certs is hard-coded in OpenSSL. The ssl module simply uses OpenSSL's defaults on non-Windows platforms.
def load_default_certs(...): ....
if sys.platform == "win32": ... else: prefix = os.path.normpath(sys.prefix) default_cert = os.path.join(prefix, "ssl", "cert.pem") if os.path.isfile(default_cert): self.load_verify_locations(default_cert) else: self.set_default_verify_paths()
While this seems to work, my lack of knowledge in all things related to security and ssl in particular makes me worry to patch anything in there. Is this a sane approach ? If not, is there a better way ?
You can override the default verify paths already. There is no need to monkey patch load_default_certs(). You have two options: 1) Create your own custom SSLContext with a custom trust anchor, e.g. ctx = ssl.create_default_context(cafile='...') and pass the context along. 2) Set SSL_CERT_FILE env var to override OpenSSL's default setting, see https://docs.python.org/3/library/ssl.html#ssl.get_default_verify_paths The second option may work for you. You can set the env var at any time before you create a new SSLContext object. Christian
On Wed, Mar 1, 2017 at 3:51 PM, Christian Heimes <christian@python.org> wrote:
On 2017-03-01 15:59, David Cournapeau wrote:
Hi there,
This is hopefully the right mailing for this question. We are shipping our own python interpreter in our product, and following some discussions on https://mail.python.org/pipermail/python-dev/2017-January/147282.html, we understand shipping the certificate from certifi in our python is the best approach on Linux/OS X.
Yeah, it's the easiest option for you. I don't necessarily agree it's the best option.
We are certainly interested in a better option ! Our constraints are: 1. we cannot control where python actually get installed 2. on Linux, we cannot afford to have distribution-specific builds (we use something similar to the manylinux setup)
Unfortunately, ssl hardcodes at compilation time the default location of certificate. I could workaround this at the python level by patching ssl.SSLContext.load_default_certs to look as follows:
Small correction: The location of the certs is hard-coded in OpenSSL. The ssl module simply uses OpenSSL's defaults on non-Windows platforms.
def load_default_certs(...):
....
if sys.platform == "win32": ... else: prefix = os.path.normpath(sys.prefix) default_cert = os.path.join(prefix, "ssl", "cert.pem") if os.path.isfile(default_cert): self.load_verify_locations(default_cert) else: self.set_default_verify_paths()
While this seems to work, my lack of knowledge in all things related to security and ssl in particular makes me worry to patch anything in there. Is this a sane approach ? If not, is there a better way ?
You can override the default verify paths already. There is no need to monkey patch load_default_certs(). You have two options:
1) Create your own custom SSLContext with a custom trust anchor, e.g. ctx = ssl.create_default_context(cafile='...') and pass the context along.
2) Set SSL_CERT_FILE env var to override OpenSSL's default setting, see https://docs.python.org/3/library/ssl.html#ssl.get_default_verify_paths
The second option may work for you. You can set the env var at any time before you create a new SSLContext object.
Just to clarify: I am aware that for code we write/distribute, there are better ways (we tend to always use requests for http(s) handling). But as we are distributing python as a distribution, the goal is to make things work by default for 3rd party users, without compromising security, so 1. is not applicable. 2. is not a good solution in our opinion because it may cause trouble when people use programs linked against openssl through subprocess. David Christian
_______________________________________________ Security-SIG mailing list Security-SIG@python.org https://mail.python.org/mailman/listinfo/security-sig
-- blog: http://cournape.wordpress.com code: https://github.com/cournape twitter: @cournape
On 2 March 2017 at 02:44, David Cournapeau <davidc@enthought.com> wrote:
Just to clarify: I am aware that for code we write/distribute, there are better ways (we tend to always use requests for http(s) handling).
Christian may have missed the domain on your email address, so calling out the redistribution context explicitly: https://www.enthought.com/products/epd/ And yes, since the Linux distros can't agree on a reliable way for third party applications to find the system certificate store without distro-specific patches, I'd agree that bundling certifi and patching your Python builds is your best currently available option for getting good "out of the box" behaviour. Donald tried to make location autodetection work for pip, but the distros unfortunately not only can't agree on how the default certs should be located, they also don't make sure the other potential locations reliable give a detectable error :( Given your context of use though, the one potential incompatibility you're going to have to watch out for is losing access to any custom CA certificates that are installed into the system trust stores (since certifi won't have any knowledge of those). Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
On Thu, Mar 2, 2017 at 7:06 AM, Nick Coghlan <ncoghlan@gmail.com> wrote:
Given your context of use though, the one potential incompatibility you're going to have to watch out for is losing access to any custom CA certificates that are installed into the system trust stores (since certifi won't have any knowledge of those).
That's indeed a cons of this approach, at least on Linux. My understanding is that the PEP worked on by Cory Benfield would improve this. We will use this hack for now, keeping a look on the progress being made David
participants (3)
-
Christian Heimes
-
David Cournapeau
-
Nick Coghlan