Skip to content

Commit

Permalink
PYTHON-2798 Workaround windows cert issue with SSL_CERT_FILE (mongodb…
Browse files Browse the repository at this point in the history
…#670)

(cherry picked from commit 6d1ebf4)
  • Loading branch information
ShaneHarvey committed Jul 12, 2021
1 parent 4d531d1 commit dd126aa
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 63 deletions.
14 changes: 8 additions & 6 deletions .evergreen/run-tests.sh
Expand Up @@ -166,13 +166,15 @@ if [ -n "$TEST_ENCRYPTION" ]; then
. $DRIVERS_TOOLS/.evergreen/csfle/set-temp-creds.sh

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

PYTHON_IMPL=$($PYTHON -c "import platform, sys; sys.stdout.write(platform.python_implementation())")
Expand Down
26 changes: 25 additions & 1 deletion test/__init__.py
Expand Up @@ -50,7 +50,7 @@
from pymongo.common import partition_node
from pymongo.hello_compat import HelloCompat
from pymongo.server_api import ServerApi
from pymongo.ssl_support import HAVE_SSL, validate_cert_reqs
from pymongo.ssl_support import HAVE_SSL, _ssl
from pymongo.uri_parser import parse_uri
from test.version import Version

Expand Down Expand Up @@ -885,6 +885,10 @@ def setUpClass(cls):
else:
cls.credentials = {}

def patch_system_certs(self, ca_certs):
patcher = SystemCertsPatcher(ca_certs)
self.addCleanup(patcher.disable)


# Use assertRaisesRegex if available, otherwise use Python 2.7's
# deprecated assertRaisesRegexp, with a 'p'.
Expand Down Expand Up @@ -1030,3 +1034,23 @@ def clear_warning_registry():
for name, module in list(sys.modules.items()):
if hasattr(module, "__warningregistry__"):
setattr(module, "__warningregistry__", {})


class SystemCertsPatcher(object):
def __init__(self, ca_certs):
if sys.version_info < (2, 7, 9):
raise SkipTest("Can't load system CA certificates.")
if (ssl.OPENSSL_VERSION.lower().startswith('libressl') and
sys.platform == 'darwin' and not _ssl.IS_PYOPENSSL):
raise SkipTest(
"LibreSSL on OSX doesn't support setting CA certificates "
"using SSL_CERT_FILE environment variable.")
self.original_certs = os.environ.get('SSL_CERT_FILE')
# Tell OpenSSL where CA certificates live.
os.environ['SSL_CERT_FILE'] = ca_certs

def disable(self):
if self.original_certs is None:
os.environ.pop('SSL_CERT_FILE')
else:
os.environ['SSL_CERT_FILE'] = self.original_certs
48 changes: 28 additions & 20 deletions test/test_encryption.py
Expand Up @@ -19,7 +19,6 @@
import os
import traceback
import socket
import ssl
import sys
import textwrap
import uuid
Expand Down Expand Up @@ -51,10 +50,14 @@
WriteError)
from pymongo.mongo_client import MongoClient
from pymongo.operations import InsertOne
from pymongo.ssl_support import _ssl
from pymongo.write_concern import WriteConcern
from test.test_ssl import CA_PEM

from test import unittest, IntegrationTest, PyMongoTestCase, client_context
from test import (unittest,
client_context,
IntegrationTest,
PyMongoTestCase,
SystemCertsPatcher)
from test.utils import (TestCreator,
camel_to_snake_args,
OvertCommandListener,
Expand All @@ -63,7 +66,26 @@
rs_or_single_client,
wait_until)
from test.utils_spec_runner import SpecRunner
from test.test_ssl import CA_PEM

try:
import certifi
HAVE_CERTIFI = True
except ImportError:
HAVE_CERTIFI = False

patcher = None


def setUpModule():
if sys.platform == 'win32' and HAVE_CERTIFI:
# Remove after BUILD-13574.
global patcher
patcher = SystemCertsPatcher(certifi.where())


def tearDownModule():
if patcher:
patcher.disable()


def get_client_opts(client):
Expand Down Expand Up @@ -1635,25 +1657,11 @@ def test_bypassAutoEncryption(self):

# https://github.com/mongodb/specifications/tree/master/source/client-side-encryption/tests#kms-tls-tests
class TestKmsTLSProse(EncryptionIntegrationTest):
@unittest.skipIf(sys.platform == 'win32',
"Can't test system ca certs on Windows")
@unittest.skipIf(ssl.OPENSSL_VERSION.lower().startswith('libressl') and
sys.platform == 'darwin' and not _ssl.IS_PYOPENSSL,
"LibreSSL on OSX doesn't support setting CA certificates "
"using SSL_CERT_FILE environment variable.")
@unittest.skipUnless(any(AWS_CREDS.values()),
'AWS environment credentials are not set')
def setUp(self):
self.original_certs = os.environ.get('SSL_CERT_FILE')
def restore_certs():
if self.original_certs is None:
os.environ.pop('SSL_CERT_FILE')
else:
os.environ['SSL_CERT_FILE'] = self.original_certs
# Tell OpenSSL where CA certificates live.
os.environ['SSL_CERT_FILE'] = CA_PEM
self.addCleanup(restore_certs)

super(TestKmsTLSProse, self).setUp()
self.patch_system_certs(CA_PEM)
self.client_encrypted = ClientEncryption(
{'aws': AWS_CREDS}, 'keyvault.datakeys', self.client, OPTS)
self.addCleanup(self.client_encrypted.close)
Expand Down
56 changes: 20 additions & 36 deletions test/test_ssl.py
Expand Up @@ -460,48 +460,32 @@ def test_validation_with_system_ca_certs(self):
# --sslCAFile=/path/to/pymongo/test/certificates/ca.pem
# --sslWeakCertificateValidation
#
if sys.platform == "win32":
raise SkipTest("Can't test system ca certs on Windows.")

if sys.version_info < (2, 7, 9):
raise SkipTest("Can't load system CA certificates.")

if (ssl.OPENSSL_VERSION.lower().startswith('libressl') and
sys.platform == 'darwin' and not _ssl.IS_PYOPENSSL):
raise SkipTest(
"LibreSSL on OSX doesn't support setting CA certificates "
"using SSL_CERT_FILE environment variable.")

# Tell OpenSSL where CA certificates live.
os.environ['SSL_CERT_FILE'] = CA_PEM
try:
with self.assertRaises(ConnectionFailure):
# Server cert is verified but hostname matching fails
connected(MongoClient('server',
ssl=True,
serverSelectionTimeoutMS=100,
**self.credentials))

# Server cert is verified. Disable hostname matching.
self.patch_system_certs(CA_PEM)
with self.assertRaises(ConnectionFailure):
# Server cert is verified but hostname matching fails
connected(MongoClient('server',
ssl=True,
ssl_match_hostname=False,
serverSelectionTimeoutMS=100,
**self.credentials))

# Server cert and hostname are verified.
connected(MongoClient('localhost',
ssl=True,
serverSelectionTimeoutMS=100,
**self.credentials))
# Server cert is verified. Disable hostname matching.
connected(MongoClient('server',
ssl=True,
ssl_match_hostname=False,
serverSelectionTimeoutMS=100,
**self.credentials))

# Server cert and hostname are verified.
connected(
MongoClient(
'mongodb://localhost/?ssl=true&serverSelectionTimeoutMS=100',
**self.credentials))
finally:
os.environ.pop('SSL_CERT_FILE')
# Server cert and hostname are verified.
connected(MongoClient('localhost',
ssl=True,
serverSelectionTimeoutMS=100,
**self.credentials))

# Server cert and hostname are verified.
connected(
MongoClient(
'mongodb://localhost/?ssl=true&serverSelectionTimeoutMS=100',
**self.credentials))

def test_system_certs_config_error(self):
ctx = get_ssl_context(
Expand Down

0 comments on commit dd126aa

Please sign in to comment.