PYTHON-834 - Add option to disable match_hostname.
This commit is contained in:
parent
9b1ac9717f
commit
3487b4bfa1
@ -340,6 +340,7 @@ VALIDATORS = {
|
||||
'ssl_certfile': validate_readable,
|
||||
'ssl_cert_reqs': validate_cert_reqs,
|
||||
'ssl_ca_certs': validate_readable,
|
||||
'ssl_match_hostname': validate_boolean,
|
||||
'readpreference': validate_read_preference,
|
||||
'read_preference': validate_read_preference,
|
||||
'readpreferencetags': validate_tag_sets,
|
||||
|
||||
@ -241,6 +241,12 @@ class MongoClient(common.BaseObject):
|
||||
"certification authority" certificates, which are used to validate
|
||||
certificates passed from the other end of the connection.
|
||||
Implies ``ssl=True``. Defaults to ``None``.
|
||||
- `ssl_match_hostname`: If ``True`` (the default), and
|
||||
`ssl_cert_reqs` is not ``ssl.CERT_NONE``, enables hostname
|
||||
verification using the :func:`~ssl.match_hostname` function from
|
||||
python's :mod:`~ssl` module. Think very carefully before setting
|
||||
this to ``False`` as that could make your application vulnerable to
|
||||
man-in-the-middle attacks.
|
||||
|
||||
.. seealso:: :meth:`end_request`
|
||||
|
||||
@ -330,11 +336,12 @@ class MongoClient(common.BaseObject):
|
||||
self.__wait_queue_multiple = options.get('waitqueuemultiple')
|
||||
self.__socket_keepalive = options.get('socketkeepalive', False)
|
||||
|
||||
self.__use_ssl = options.get('ssl', None)
|
||||
self.__ssl_keyfile = options.get('ssl_keyfile', None)
|
||||
self.__ssl_certfile = options.get('ssl_certfile', None)
|
||||
self.__ssl_cert_reqs = options.get('ssl_cert_reqs', None)
|
||||
self.__ssl_ca_certs = options.get('ssl_ca_certs', None)
|
||||
self.__use_ssl = options.get('ssl')
|
||||
self.__ssl_keyfile = options.get('ssl_keyfile')
|
||||
self.__ssl_certfile = options.get('ssl_certfile')
|
||||
self.__ssl_cert_reqs = options.get('ssl_cert_reqs')
|
||||
self.__ssl_ca_certs = options.get('ssl_ca_certs')
|
||||
self.__ssl_match_hostname = options.get('ssl_match_hostname', True)
|
||||
|
||||
ssl_kwarg_keys = [k for k in kwargs.keys()
|
||||
if k.startswith('ssl_') and kwargs[k]]
|
||||
@ -512,6 +519,7 @@ class MongoClient(common.BaseObject):
|
||||
ssl_certfile=self.__ssl_certfile,
|
||||
ssl_cert_reqs=self.__ssl_cert_reqs,
|
||||
ssl_ca_certs=self.__ssl_ca_certs,
|
||||
ssl_match_hostname=self.__ssl_match_hostname,
|
||||
wait_queue_timeout=self.__wait_queue_timeout,
|
||||
wait_queue_multiple=self.__wait_queue_multiple,
|
||||
socket_keepalive=self.__socket_keepalive)
|
||||
|
||||
@ -585,6 +585,12 @@ class MongoReplicaSetClient(common.BaseObject):
|
||||
"certification authority" certificates, which are used to validate
|
||||
certificates passed from the other end of the connection.
|
||||
Implies ``ssl=True``. Defaults to ``None``.
|
||||
- `ssl_match_hostname`: If ``True`` (the default), and
|
||||
`ssl_cert_reqs` is not ``ssl.CERT_NONE``, enables hostname
|
||||
verification using the :func:`~ssl.match_hostname` function from
|
||||
python's :mod:`~ssl` module. Think very carefully before setting
|
||||
this to ``False`` as that could make your application vulnerable to
|
||||
man-in-the-middle attacks.
|
||||
|
||||
.. versionchanged:: 2.5
|
||||
Added additional ssl options
|
||||
@ -667,11 +673,12 @@ class MongoReplicaSetClient(common.BaseObject):
|
||||
self.__wait_queue_timeout = self.__opts.get('waitqueuetimeoutms')
|
||||
self.__wait_queue_multiple = self.__opts.get('waitqueuemultiple')
|
||||
self.__socket_keepalive = self.__opts.get('socketkeepalive', False)
|
||||
self.__use_ssl = self.__opts.get('ssl', None)
|
||||
self.__ssl_keyfile = self.__opts.get('ssl_keyfile', None)
|
||||
self.__ssl_certfile = self.__opts.get('ssl_certfile', None)
|
||||
self.__ssl_cert_reqs = self.__opts.get('ssl_cert_reqs', None)
|
||||
self.__ssl_ca_certs = self.__opts.get('ssl_ca_certs', None)
|
||||
self.__use_ssl = self.__opts.get('ssl')
|
||||
self.__ssl_keyfile = self.__opts.get('ssl_keyfile')
|
||||
self.__ssl_certfile = self.__opts.get('ssl_certfile')
|
||||
self.__ssl_cert_reqs = self.__opts.get('ssl_cert_reqs')
|
||||
self.__ssl_ca_certs = self.__opts.get('ssl_ca_certs')
|
||||
self.__ssl_match_hostname = self.__opts.get('ssl_match_hostname', True)
|
||||
|
||||
ssl_kwarg_keys = [k for k in kwargs.keys()
|
||||
if k.startswith('ssl_') and kwargs[k]]
|
||||
@ -1076,7 +1083,8 @@ class MongoReplicaSetClient(common.BaseObject):
|
||||
ssl_keyfile=self.__ssl_keyfile,
|
||||
ssl_certfile=self.__ssl_certfile,
|
||||
ssl_cert_reqs=self.__ssl_cert_reqs,
|
||||
ssl_ca_certs=self.__ssl_ca_certs)
|
||||
ssl_ca_certs=self.__ssl_ca_certs,
|
||||
ssl_match_hostname=self.__ssl_match_hostname)
|
||||
|
||||
if self.in_request():
|
||||
connection_pool.start_request()
|
||||
|
||||
@ -77,16 +77,16 @@ class SocketInfo(object):
|
||||
self.sock.close()
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
def set_wire_version_range(self, min_wire_version, max_wire_version):
|
||||
self._min_wire_version = min_wire_version
|
||||
self._max_wire_version = max_wire_version
|
||||
|
||||
|
||||
@property
|
||||
def min_wire_version(self):
|
||||
assert self._min_wire_version is not None
|
||||
return self._min_wire_version
|
||||
|
||||
|
||||
@property
|
||||
def max_wire_version(self):
|
||||
assert self._max_wire_version is not None
|
||||
@ -118,7 +118,7 @@ class Pool:
|
||||
use_greenlets, ssl_keyfile=None, ssl_certfile=None,
|
||||
ssl_cert_reqs=None, ssl_ca_certs=None,
|
||||
wait_queue_timeout=None, wait_queue_multiple=None,
|
||||
socket_keepalive=False):
|
||||
socket_keepalive=False, ssl_match_hostname=True):
|
||||
"""
|
||||
:Parameters:
|
||||
- `pair`: a (hostname, port) tuple
|
||||
@ -157,6 +157,12 @@ class Pool:
|
||||
- `socket_keepalive`: (boolean) Whether to send periodic keep-alive
|
||||
packets on connected sockets. Defaults to ``False`` (do not send
|
||||
keep-alive packets).
|
||||
- `ssl_match_hostname`: If ``True`` (the default), and
|
||||
`ssl_cert_reqs` is not ``ssl.CERT_NONE``, enables hostname
|
||||
verification using the :func:`~ssl.match_hostname` function from
|
||||
python's :mod:`~ssl` module. Think very carefully before setting
|
||||
this to ``False`` as that could make your application vulnerable to
|
||||
man-in-the-middle attacks.
|
||||
"""
|
||||
# Only check a socket's health with _closed() every once in a while.
|
||||
# Can override for testing: 0 to always check, None to never check.
|
||||
@ -181,6 +187,7 @@ class Pool:
|
||||
self.ssl_certfile = ssl_certfile
|
||||
self.ssl_cert_reqs = ssl_cert_reqs
|
||||
self.ssl_ca_certs = ssl_ca_certs
|
||||
self.ssl_match_hostname = ssl_match_hostname
|
||||
|
||||
if HAS_SSL and use_ssl and not ssl_cert_reqs:
|
||||
self.ssl_cert_reqs = ssl.CERT_NONE
|
||||
@ -295,7 +302,7 @@ class Pool:
|
||||
keyfile=self.ssl_keyfile,
|
||||
ca_certs=self.ssl_ca_certs,
|
||||
cert_reqs=self.ssl_cert_reqs)
|
||||
if self.ssl_cert_reqs:
|
||||
if self.ssl_cert_reqs and self.ssl_match_hostname:
|
||||
match_hostname(sock.getpeercert(), hostname)
|
||||
|
||||
except ssl.SSLError:
|
||||
|
||||
@ -375,7 +375,7 @@ class TestSSL(unittest.TestCase):
|
||||
"hostname in the certificate")
|
||||
|
||||
uri_fmt = ("mongodb://server/?ssl=true&ssl_certfile=%s&ssl_cert_reqs"
|
||||
"=%s&ssl_ca_certs=%s")
|
||||
"=%s&ssl_ca_certs=%s&ssl_match_hostname=true")
|
||||
client = MongoClient(uri_fmt % (CLIENT_PEM, 'CERT_REQUIRED', CA_PEM))
|
||||
|
||||
db = client.pymongo_ssl_test
|
||||
@ -426,7 +426,7 @@ class TestSSL(unittest.TestCase):
|
||||
self.assertTrue(db.test.find_one()['ssl'])
|
||||
client.drop_database('pymongo_ssl_test')
|
||||
|
||||
def test_cert_ssl_validation_hostname_fail(self):
|
||||
def test_cert_ssl_validation_hostname_matching(self):
|
||||
# Expects the server to be running with the server.pem, ca.pem
|
||||
# and crl.pem provided in mongodb and the server tests eg:
|
||||
#
|
||||
@ -439,6 +439,9 @@ class TestSSL(unittest.TestCase):
|
||||
client = MongoClient(host, port, ssl=True, ssl_certfile=CLIENT_PEM)
|
||||
response = client.admin.command('ismaster')
|
||||
|
||||
uri = ("mongodb://%s/?ssl=true&ssl_certfile=%s&ssl_cert_reqs"
|
||||
"=CERT_REQUIRED&ssl_ca_certs=%s" % (pair, CLIENT_PEM, CA_PEM))
|
||||
|
||||
try:
|
||||
MongoClient(pair,
|
||||
ssl=True,
|
||||
@ -449,11 +452,30 @@ class TestSSL(unittest.TestCase):
|
||||
except CertificateError:
|
||||
pass
|
||||
|
||||
try:
|
||||
MongoClient(uri)
|
||||
self.fail("Invalid hostname should have failed")
|
||||
except CertificateError:
|
||||
pass
|
||||
|
||||
# No error.
|
||||
MongoClient(pair,
|
||||
ssl=True,
|
||||
ssl_certfile=CLIENT_PEM,
|
||||
ssl_cert_reqs=ssl.CERT_REQUIRED,
|
||||
ssl_ca_certs=CA_PEM,
|
||||
ssl_match_hostname=False)
|
||||
|
||||
MongoClient(uri + "&ssl_match_hostname=false")
|
||||
|
||||
if 'setName' in response:
|
||||
name = response['setName']
|
||||
w = len(response['hosts'])
|
||||
uri = uri + "&replicaSet=%s&w=%d" % (name, w)
|
||||
try:
|
||||
MongoReplicaSetClient(pair,
|
||||
replicaSet=response['setName'],
|
||||
w=len(response['hosts']),
|
||||
replicaSet=name,
|
||||
w=w,
|
||||
ssl=True,
|
||||
ssl_certfile=CLIENT_PEM,
|
||||
ssl_cert_reqs=ssl.CERT_REQUIRED,
|
||||
@ -462,6 +484,24 @@ class TestSSL(unittest.TestCase):
|
||||
except CertificateError:
|
||||
pass
|
||||
|
||||
try:
|
||||
MongoReplicaSetClient(uri)
|
||||
self.fail("Invalid hostname should have failed")
|
||||
except CertificateError:
|
||||
pass
|
||||
|
||||
# No error.
|
||||
MongoReplicaSetClient(pair,
|
||||
replicaSet=name,
|
||||
w=w,
|
||||
ssl=True,
|
||||
ssl_certfile=CLIENT_PEM,
|
||||
ssl_cert_reqs=ssl.CERT_REQUIRED,
|
||||
ssl_ca_certs=CA_PEM,
|
||||
ssl_match_hostname=False)
|
||||
|
||||
MongoClient(uri + "&ssl_match_hostname=false")
|
||||
|
||||
def test_mongodb_x509_auth(self):
|
||||
# Expects the server to be running with the server.pem, ca.pem
|
||||
# and crl.pem provided in mongodb and the server tests as well as
|
||||
|
||||
Loading…
Reference in New Issue
Block a user