简体   繁体   中英

ServerSelectionTimeoutError When connecting to MongoDB Database with PyMongo and x509 SSL Certificate

I am trying to connect to a MongoDB Database on another server. The only problem is that to connect to the server requires a specialized version of Kerberos. To overcome this, I was using SSH Tunnels to open a local port for Pymongo to interface with the database and we designed our Security Certificate specifically for this eventuality as a precaution. I know that the tunnel is functioning properly as the Mongo Shell and Robo 3T both are able to connect to the database and display the data. However, with PyMongo version 3.7.1, I get the following error:

ServerSelectionTimeoutError: hostname '127.0.0.1' doesn't match either of '<redacted server1>', '<redacted server1 wildcard domain>', '127.0.0.1'

The certificate was setup with the explicit <server name> as well as *.server_domain.com in the DNS list for the x509 certificate in the event we had to move our MongoDB to another server location in the domain. We also added 127.0.0.1 for the few users located outside of the Domain who would need to use SSH Tunnels to access the database.

Using PyMongo, we get the following error:

from pymongo import MongoClient
client = MongoClient('127.0.0.1', 27017, ssl_ca_certs='/Users/<user>/ssl_cert_location/mongodb.pem')
db = client['admin']
db.authenticate('<username>', '<password>')

---------------------------------------------------------------------------
ServerSelectionTimeoutError               Traceback (most recent call last)
<ipython-input-26-ca905a055830> in <module>()
----> 1 db.authenticate('<username>', '<password>')

/Users/<user>/anaconda2/lib/python2.7/site-packages/pymongo/database.pyc in authenticate(self, name, password, source, mechanism, **kwargs)
   1272             self.name,
   1273             credentials,
-> 1274             connect=True)
   1275
   1276         return True

/Users/<user>/anaconda2/lib/python2.7/site-packages/pymongo/mongo_client.pyc in _cache_credentials(self, source, credentials, connect)
    607         if connect:
    608             server = self._get_topology().select_server(
--> 609                 writable_preferred_server_selector)
    610
    611             # get_socket() logs out of the database if logged in with old

/Users/<user>/anaconda2/lib/python2.7/site-packages/pymongo/topology.pyc in select_server(self, selector, server_selection_timeout, address)
    222         return random.choice(self.select_servers(selector,
    223                                                  server_selection_timeout,
--> 224                                                  address))
    225
    226     def select_server_by_address(self, address,

/Users/<user>/anaconda2/lib/python2.7/site-packages/pymongo/topology.pyc in select_servers(self, selector, server_selection_timeout, address)
    181         with self._lock:
    182             server_descriptions = self._select_servers_loop(
--> 183                 selector, server_timeout, address)
    184
    185             return [self.get_server_by_address(sd.address)

/Users/<user>/anaconda2/lib/python2.7/site-packages/pymongo/topology.pyc in _select_servers_loop(self, selector, timeout, address)
    197             if timeout == 0 or now > end_time:
    198                 raise ServerSelectionTimeoutError(
--> 199                     self._error_message(selector))
    200
    201             self._ensure_opened()

ServerSelectionTimeoutError: hostname '127.0.0.1' doesn't match either of '<redacted server1>', '<redacted server1 wildcard domain>', '127.0.0.1'

The most important part of this error being hostname '127.0.0.1' doesn't match '127.0.0.1' . This makes zero sense to me as it clearly does match and both the Mongo Shell and Robo 3T have zero qualms using this x509 SSL Certificate for connecting to the Database.

With the Mongo shell from outside the Domain, there does not seem to be an issue:

$  pkinit -f <user>
     <user> PIN:  *****************

$  /usr/local/ossh/bin/ssh -4K -nNT -L 27017:127.0.0.1:<mongo_port> <user>@<server1>

$ ./mongo --host 127.0.0.1 --port 27017 --ssl --sslCAFile ~/ssl_cert_location/mongodb6.pem

    MongoDB shell version v4.0.1
    connecting to: mongodb://127.0.0.1:27017/
    MongoDB server version: 3.6.5
    WARNING: shell and server versions do not match
  MongoDB Enterprise > use admin
    switched to db admin

So, the tunnel is functioning as it should and MongoDB does not have any issues with the SSL x509 Certificate. So that begs the question of why Pymongo cannot handle the given x509 Certificate? I am not using any leading or trailing dots in the list of hostnames which seems to be what all the threads concentrate on when searching for this error. I explicitly give the exact hostname that is listed as one of the Alternate DNS hostnames from the x509 certificate.

I would greatly appreciate any help anyone can give me concerning this error. Thanks in advance.

Here's some code from the driver. Your cert is parsed and it tries to load DNS names from the subjectAltName section https://github.com/mongodb/mongo-python-driver/blob/749c1a2f0bde87a6e6d8df9366e4c90666efd189/pymongo/ssl_match_hostname.py#L103-L113

Notice that the driver differentiates between 'DNS' and 'IP Address' keyed entries within the subjectAltName. I imagine you've added '127.0.0.1' as a DNS hostanme in the cert, while the driver is treating the string '127.0.0.1' as an IP Address and hence there is no match.

This is why the code fails with the confusing error. The match failure isn't on the direct value- its on the fact that one is an IP address and the other is a hostname.

The confusion happens slightly earlier, where a variable host_ip is assigned on whether the specified hostname can be parsed as an IP address or not. https://github.com/mongodb/mongo-python-driver/blob/749c1a2f0bde87a6e6d8df9366e4c90666efd189/pymongo/ssl_match_hostname.py#L98-L102 In the case of a hostname '127.0.0.1' I imagine it is parsed correctly and assigned to the host_ip variable

Now this check will fail https://github.com/mongodb/mongo-python-driver/blob/749c1a2f0bde87a6e6d8df9366e4c90666efd189/pymongo/ssl_match_hostname.py#L107 and it won't match for '127.0.0.1' from the DNS hostnames section of your cert.

The Fix

Your cert's subjectAltName field should probably look something like this:
subjectAltName = DNS:<server1>, DNS:<server2>, IP:127.0.0.1

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM