Repository: pypa/cibuildwheel

Issue:
Windows: urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed
Hello,

Can you, please, help me to debug https://travis-ci.org/github/ets-labs/python-dependency-injector/jobs/739050288?

It occurs only on Windows. Error says: ``urllib.error.URLError: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1108)>``. I'm unclear whos certificate has expired.

Best,
Roman



Discussion:
Please bump cibuildwheel to the most recent version. it should be fixed in #447 
Got it, will try. Thank you.
I'm currently seeing this as well on travis ci windows builds only, already running the latest version (1.6.3). I did a release yesterday and things worked fine, so somethings changed in the last day (likely the certs on the windows box).

I've worked around this by installing `certifi` and configuring the build to use certifi's certs, see here:

https://github.com/jcrist/quickle/pull/58/files#diff-6ac3f79fc25d95cd1e3d51da53a4b21b939437392578a35ae8cd6d5366ca5485R31
@jcrist , cool, thanks for sharing.
oh. So it is outdated certif on this python 
Could you try to bump python version in travis file from 3.8.0 to 3.8.2 
```
        - choco install python --version 3.8.2
```

@jcrist  @rmk135?
I'm already using 3.8.6 (in that PR), which didn't fix things. Installing certifi and forcing the build to use certifi certs did fix things.
starnge
@jcrist , did you face such issue on windows ``The command "python3 -m pip install certifi cibuildwheel==1.6.3" failed and exited with 127 during .``?

The reason looks strange: 
```
Your build has been stopped. 
/c/Users/travis/.travis/functions: line 109: python3: command not found
```

https://travis-ci.com/github/ets-labs/python-dependency-injector/jobs/411227719

I have it after tried your solution.

PS: I have recently migrated from `travis-ci.org` to `travis-ci.com`.

Fixed `python3: command not found` by adding a line from README into `before_install`:

```
      - ln -s /c/Python38/python.exe /c/Python38/python3.exe
```

Seems that wasn't needed when I initially configured windows builds.
Thanks everybody for the help and special thanks to this lib creators and contributors for the great tool.
I just spotted this in an unrelated PR, this time on Appveyor:

https://ci.appveyor.com/project/joerick/cibuildwheel/builds/36032538/job/n4duoutlja3nbqit

It's happening in the host process, not in a version of Python installed by cibuildwheel. But still on that initial nuget download. 
Perhaps cibuildwheel should require `certifi` as a dependency and explicitly use certifi's certs in e.g. `download` instead of relying on the system certs? `requests` does this, and it seems to work well: https://requests.readthedocs.io/en/master/user/advanced/#ca-certificates.
It looks rather like outdated windows images from CI provider. There is possibility to change certificate source after scripts run? 

Resolving patch:
diff --git a/cibuildwheel/util.py b/cibuildwheel/util.py
--- a/cibuildwheel/util.py
+++ b/cibuildwheel/util.py
@@ -1,6 +1,8 @@
 import os
 import textwrap
+import certifi
 import urllib.request
+import ssl
 from fnmatch import fnmatch
 from pathlib import Path
 from time import sleep
@@ -66,10 +68,14 @@ def download(url: str, dest: Path) -> None:
     if not dest_dir.exists():
         dest_dir.mkdir(parents=True)
 
+    # we've had issues when relying on the host OS' CA certificates on Windows,
+    # so we use certifi (this sounds odd but requests also does this by default)
+    cafile = os.environ.get('SSL_CERT_FILE', certifi.where())
+    context = ssl.create_default_context(cafile=cafile)
     repeat_num = 3
     for i in range(repeat_num):
         try:
-            response = urllib.request.urlopen(url)
+            response = urllib.request.urlopen(url, context=context)
         except Exception:
             if i == repeat_num - 1:
                 raise
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -12,7 +12,7 @@
 setup(
     name='cibuildwheel',
     version='1.6.3',
-    install_requires=['bashlex!=0.13', 'toml'],
+    install_requires=['bashlex!=0.13', 'toml', 'certifi'],
     description="Build Python wheels on CI with minimal configuration.",
     long_description=long_description,
     long_description_content_type='text/markdown',


Test patch:
diff --git a/unit_test/download_test.py b/unit_test/download_test.py
new file mode 100644
--- /dev/null
+++ b/unit_test/download_test.py
@@ -0,0 +1,31 @@
+import certifi
+import pytest
+import ssl
+
+from cibuildwheel.util import download
+
+
+DOWNLOAD_URL = 'https://raw.githubusercontent.com/joerick/cibuildwheel/v1.6.3/requirements-dev.txt'
+
+
+def test_download(monkeypatch, tmp_path):
+    monkeypatch.delenv('SSL_CERT_FILE', raising=False)
+    dest = tmp_path / 'file.txt'
+    download(DOWNLOAD_URL, dest)
+    assert len(dest.read_bytes()) == 134
+
+
+def test_download_good_ssl_cert_file(monkeypatch, tmp_path):
+    monkeypatch.setenv('SSL_CERT_FILE', certifi.where())
+    dest = tmp_path / 'file.txt'
+    download(DOWNLOAD_URL, dest)
+    assert len(dest.read_bytes()) == 134
+
+
+def test_download_bad_ssl_cert_file(monkeypatch, tmp_path):
+    bad_cafile = tmp_path / 'ca.pem'
+    bad_cafile.write_text('bad certificates')
+    monkeypatch.setenv('SSL_CERT_FILE', str(bad_cafile))
+    dest = tmp_path / 'file.txt'
+    with pytest.raises(ssl.SSLError):
+        download(DOWNLOAD_URL, dest)