Skip to content

Commit d5aa6d9

Browse files
committed
PYTHON-2798 Workaround windows cert issue with SSL_CERT_FILE (#670)
(cherry picked from commit 6d1ebf4)
1 parent a10cbbf commit d5aa6d9

File tree

4 files changed

+81
-63
lines changed

4 files changed

+81
-63
lines changed

.evergreen/run-tests.sh

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -166,13 +166,15 @@ if [ -n "$TEST_ENCRYPTION" ]; then
166166
. $DRIVERS_TOOLS/.evergreen/csfle/set-temp-creds.sh
167167

168168
# Start the mock KMS servers.
169-
if [ "$OS" != "Windows_NT" ]; then
170-
pushd ${DRIVERS_TOOLS}/.evergreen/csfle
171-
python -u lib/kms_http_server.py --ca_file ../x509gen/ca.pem --cert_file ../x509gen/expired.pem --port 8000 &
172-
python -u lib/kms_http_server.py --ca_file ../x509gen/ca.pem --cert_file ../x509gen/wrong-host.pem --port 8001 &
173-
trap 'kill $(jobs -p)' EXIT HUP
174-
popd
169+
if [ "$OS" = "Windows_NT" ]; then
170+
# Remove after BUILD-13574.
171+
python -m pip install certifi
175172
fi
173+
pushd ${DRIVERS_TOOLS}/.evergreen/csfle
174+
python -u lib/kms_http_server.py --ca_file ../x509gen/ca.pem --cert_file ../x509gen/expired.pem --port 8000 &
175+
python -u lib/kms_http_server.py --ca_file ../x509gen/ca.pem --cert_file ../x509gen/wrong-host.pem --port 8001 &
176+
trap 'kill $(jobs -p)' EXIT HUP
177+
popd
176178
fi
177179

178180
PYTHON_IMPL=$($PYTHON -c "import platform, sys; sys.stdout.write(platform.python_implementation())")

test/__init__.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
from pymongo.common import partition_node
5151
from pymongo.hello_compat import HelloCompat
5252
from pymongo.server_api import ServerApi
53-
from pymongo.ssl_support import HAVE_SSL, validate_cert_reqs
53+
from pymongo.ssl_support import HAVE_SSL, _ssl
5454
from pymongo.uri_parser import parse_uri
5555
from test.version import Version
5656

@@ -885,6 +885,10 @@ def setUpClass(cls):
885885
else:
886886
cls.credentials = {}
887887

888+
def patch_system_certs(self, ca_certs):
889+
patcher = SystemCertsPatcher(ca_certs)
890+
self.addCleanup(patcher.disable)
891+
888892

889893
# Use assertRaisesRegex if available, otherwise use Python 2.7's
890894
# deprecated assertRaisesRegexp, with a 'p'.
@@ -1030,3 +1034,23 @@ def clear_warning_registry():
10301034
for name, module in list(sys.modules.items()):
10311035
if hasattr(module, "__warningregistry__"):
10321036
setattr(module, "__warningregistry__", {})
1037+
1038+
1039+
class SystemCertsPatcher(object):
1040+
def __init__(self, ca_certs):
1041+
if sys.version_info < (2, 7, 9):
1042+
raise SkipTest("Can't load system CA certificates.")
1043+
if (ssl.OPENSSL_VERSION.lower().startswith('libressl') and
1044+
sys.platform == 'darwin' and not _ssl.IS_PYOPENSSL):
1045+
raise SkipTest(
1046+
"LibreSSL on OSX doesn't support setting CA certificates "
1047+
"using SSL_CERT_FILE environment variable.")
1048+
self.original_certs = os.environ.get('SSL_CERT_FILE')
1049+
# Tell OpenSSL where CA certificates live.
1050+
os.environ['SSL_CERT_FILE'] = ca_certs
1051+
1052+
def disable(self):
1053+
if self.original_certs is None:
1054+
os.environ.pop('SSL_CERT_FILE')
1055+
else:
1056+
os.environ['SSL_CERT_FILE'] = self.original_certs

test/test_encryption.py

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import os
2020
import traceback
2121
import socket
22-
import ssl
2322
import sys
2423
import textwrap
2524
import uuid
@@ -51,10 +50,14 @@
5150
WriteError)
5251
from pymongo.mongo_client import MongoClient
5352
from pymongo.operations import InsertOne
54-
from pymongo.ssl_support import _ssl
5553
from pymongo.write_concern import WriteConcern
54+
from test.test_ssl import CA_PEM
5655

57-
from test import unittest, IntegrationTest, PyMongoTestCase, client_context
56+
from test import (unittest,
57+
client_context,
58+
IntegrationTest,
59+
PyMongoTestCase,
60+
SystemCertsPatcher)
5861
from test.utils import (TestCreator,
5962
camel_to_snake_args,
6063
OvertCommandListener,
@@ -63,7 +66,26 @@
6366
rs_or_single_client,
6467
wait_until)
6568
from test.utils_spec_runner import SpecRunner
66-
from test.test_ssl import CA_PEM
69+
70+
try:
71+
import certifi
72+
HAVE_CERTIFI = True
73+
except ImportError:
74+
HAVE_CERTIFI = False
75+
76+
patcher = None
77+
78+
79+
def setUpModule():
80+
if sys.platform == 'win32' and HAVE_CERTIFI:
81+
# Remove after BUILD-13574.
82+
global patcher
83+
patcher = SystemCertsPatcher(certifi.where())
84+
85+
86+
def tearDownModule():
87+
if patcher:
88+
patcher.disable()
6789

6890

6991
def get_client_opts(client):
@@ -1635,25 +1657,11 @@ def test_bypassAutoEncryption(self):
16351657

16361658
# https://github.com/mongodb/specifications/tree/master/source/client-side-encryption/tests#kms-tls-tests
16371659
class TestKmsTLSProse(EncryptionIntegrationTest):
1638-
@unittest.skipIf(sys.platform == 'win32',
1639-
"Can't test system ca certs on Windows")
1640-
@unittest.skipIf(ssl.OPENSSL_VERSION.lower().startswith('libressl') and
1641-
sys.platform == 'darwin' and not _ssl.IS_PYOPENSSL,
1642-
"LibreSSL on OSX doesn't support setting CA certificates "
1643-
"using SSL_CERT_FILE environment variable.")
16441660
@unittest.skipUnless(any(AWS_CREDS.values()),
16451661
'AWS environment credentials are not set')
16461662
def setUp(self):
1647-
self.original_certs = os.environ.get('SSL_CERT_FILE')
1648-
def restore_certs():
1649-
if self.original_certs is None:
1650-
os.environ.pop('SSL_CERT_FILE')
1651-
else:
1652-
os.environ['SSL_CERT_FILE'] = self.original_certs
1653-
# Tell OpenSSL where CA certificates live.
1654-
os.environ['SSL_CERT_FILE'] = CA_PEM
1655-
self.addCleanup(restore_certs)
1656-
1663+
super(TestKmsTLSProse, self).setUp()
1664+
self.patch_system_certs(CA_PEM)
16571665
self.client_encrypted = ClientEncryption(
16581666
{'aws': AWS_CREDS}, 'keyvault.datakeys', self.client, OPTS)
16591667
self.addCleanup(self.client_encrypted.close)

test/test_ssl.py

Lines changed: 20 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -460,48 +460,32 @@ def test_validation_with_system_ca_certs(self):
460460
# --sslCAFile=/path/to/pymongo/test/certificates/ca.pem
461461
# --sslWeakCertificateValidation
462462
#
463-
if sys.platform == "win32":
464-
raise SkipTest("Can't test system ca certs on Windows.")
465-
466-
if sys.version_info < (2, 7, 9):
467-
raise SkipTest("Can't load system CA certificates.")
468-
469-
if (ssl.OPENSSL_VERSION.lower().startswith('libressl') and
470-
sys.platform == 'darwin' and not _ssl.IS_PYOPENSSL):
471-
raise SkipTest(
472-
"LibreSSL on OSX doesn't support setting CA certificates "
473-
"using SSL_CERT_FILE environment variable.")
474-
475-
# Tell OpenSSL where CA certificates live.
476-
os.environ['SSL_CERT_FILE'] = CA_PEM
477-
try:
478-
with self.assertRaises(ConnectionFailure):
479-
# Server cert is verified but hostname matching fails
480-
connected(MongoClient('server',
481-
ssl=True,
482-
serverSelectionTimeoutMS=100,
483-
**self.credentials))
484-
485-
# Server cert is verified. Disable hostname matching.
463+
self.patch_system_certs(CA_PEM)
464+
with self.assertRaises(ConnectionFailure):
465+
# Server cert is verified but hostname matching fails
486466
connected(MongoClient('server',
487467
ssl=True,
488-
ssl_match_hostname=False,
489468
serverSelectionTimeoutMS=100,
490469
**self.credentials))
491470

492-
# Server cert and hostname are verified.
493-
connected(MongoClient('localhost',
494-
ssl=True,
495-
serverSelectionTimeoutMS=100,
496-
**self.credentials))
471+
# Server cert is verified. Disable hostname matching.
472+
connected(MongoClient('server',
473+
ssl=True,
474+
ssl_match_hostname=False,
475+
serverSelectionTimeoutMS=100,
476+
**self.credentials))
497477

498-
# Server cert and hostname are verified.
499-
connected(
500-
MongoClient(
501-
'mongodb://localhost/?ssl=true&serverSelectionTimeoutMS=100',
502-
**self.credentials))
503-
finally:
504-
os.environ.pop('SSL_CERT_FILE')
478+
# Server cert and hostname are verified.
479+
connected(MongoClient('localhost',
480+
ssl=True,
481+
serverSelectionTimeoutMS=100,
482+
**self.credentials))
483+
484+
# Server cert and hostname are verified.
485+
connected(
486+
MongoClient(
487+
'mongodb://localhost/?ssl=true&serverSelectionTimeoutMS=100',
488+
**self.credentials))
505489

506490
def test_system_certs_config_error(self):
507491
ctx = get_ssl_context(

0 commit comments

Comments
 (0)