I am in the process of porting my Python 2 code to Python 3. This specific section deals with my local machine posting to a webpage when running my code in Python 3 (everything works great in Python 2). I believe the root cause of my issue is the certificate on my side is failing to be authenticated by the server. Below is the error I see.
Error(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self signed certificate in certificate chain (_ssl.c:1056)')))
request Error: HTTPSConnectionPool(host='my/personal.server', port=443): Max retries exceeded with url: my/personal/url/and/data (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self signed certificate in certificate chain (_ssl.c:1056)')))
The error occurs on my requests.post(url, headers)
line in my program. I've made sure the server URL's are correct.
I believe the issue is as follows. In macOS's native Python 2 environment (what I had used to code before), the ssl
module is different than the one located in the directory Python 3 uses for its imports. My questions are:
- Is it possible to use
LibreSSL
with my Python 3 instance? - If not, how can I pass the
cafile
and other path arguments when I import thessl
module in Python 3?
FWIW, I downloaded Python 3 from Python.org.
Python 2
⇒ python
Python 2.7.10 (default, Feb 22 2019, 21:55:15)
[GCC 4.2.1 Compatible Apple LLVM 10.0.1 (clang-1001.0.37.14)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import ssl, sys
>>> sys.executable
'/usr/bin/python'
>>> ssl.OPENSSL_VERSION
'LibreSSL 2.2.7'
>>> ssl.get_default_verify_paths()
DefaultVerifyPaths(cafile='/private/etc/ssl/cert.pem', capath='/private/etc/ssl/certs', openssl_cafile_env='SSL_CERT_FILE', openssl_cafile='/private/etc/ssl/cert.pem', openssl_capath_env='SSL_CERT_DIR', openssl_capath='/private/etc/ssl/certs')
Python 3
⇒ python3
Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 16:52:21)
[Clang 6.0 (clang-600.0.57)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import ssl, sys
>>> sys.executable
'/usr/local/bin/python3'
>>> ssl.OPENSSL_VERSION
'OpenSSL 1.1.0j 20 Nov 2018'
>>> ssl.get_default_verify_paths()
DefaultVerifyPaths(cafile='/Library/Frameworks/Python.framework/Versions/3.7/etc/openssl/cert.pem', capath=None, openssl_cafile_env='SSL_CERT_FILE', openssl_cafile='/Library/Frameworks/Python.framework/Versions/3.7/etc/openssl/cert.pem', openssl_capath_env='SSL_CERT_DIR', openssl_capath='/Library/Frameworks/Python.framework/Versions/3.7/etc/openssl/certs')
I believe this is the issue why I have this error, though I am not 100% sure. Any other pointers or input would be welcome!
EDIT
Following ivan_pozdeev's suggested duplicate question, I am able to run openssl s_client -connect mywebsite.com:443 -verify 9
and I get Verify return code: 0 (ok)
. Though I am not directly calling openssl
in my python/bash scripts, I have been able to verify my Python 2 configuration works.
⇒ openssl
OpenSSL> version
LibreSSL 2.6.5
OpenSSL> ca
Using configuration from /private/etc/ssl/openssl.cnf
I am looking into how to utilize what I did above with my Python 3 interpreter and its libraries. However, this seem to be more of a test to see if I can talk to the server rather than tackling the arguments/environment issue.
EDIT 2
Perhaps this is a case of looking into this too long, but I have found something strange. The Python 2/3 instances above were done in zsh
and not in my program. When I run my script with these print
statements, I get this ssl
information:
Python 2
import ssl
print ssl.OPENSSL_VERSION
print ssl.get_default_verify_paths()
Output
LibreSSL 2.2.7
DefaultVerifyPaths(cafile='/usr/local/etc/openssl/cert.pem', capath='/private/etc/ssl/certs', openssl_cafile_env='SSL_CERT_FILE', openssl_cafile='/private/etc/ssl/cert.pem', openssl_capath_env='SSL_CERT_DIR', openssl_capath='/private/etc/ssl/certs')
Python 3
import ssl
print(ssl.OPENSSL_VERSION)
print(ssl.get_default_verify_paths())
Output
OpenSSL 1.1.0j 20 Nov 2018
DefaultVerifyPaths(cafile=None, capath=None, openssl_cafile_env='SSL_CERT_FILE', openssl_cafile='/Library/Frameworks/Python.framework/Versions/3.7/etc/openssl/cert.pem', openssl_capath_env='SSL_CERT_DIR', openssl_capath='/Library/Frameworks/Python.framework/Versions/3.7/etc/openssl/certs')
The Python 2 script prints what I would expect. The Python 3 script has my cafile
argument set to None
, which is different than what ssl.get_default_verify_paths()
returns when I run it in my zsh
. I use python
and python3
aliases in my bash
script to call my Python scripts and am absolutely sure they reference the same interpreters as my zsh
. What exactly is going on here?