ServerSelectionTimeoutError使用PyMongo和x509 SSL证书连接到MongoDB数据库时

时间:2018-08-13 21:27:02

标签: python mongodb ssl pymongo

我正在尝试连接到另一台服务器上的MongoDB数据库。唯一的问题是,连接到服务器需要特定版本的Kerberos。为了解决这个问题,我使用SSH隧道打开了Pymongo与数据库连接的本地端口,并且我们为此特别设计了安全证书,以防万一。我知道隧道工作正常,因为Mongo Shell和Robo 3T都可以连接到数据库并显示数据。但是,在PyMongo 3.7.1版中,出现以下错误:

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

如果必须将MongoDB移至域中的另一个服务器位置,则将在x509证书的DNS列表中使用显式<server name>*.server_domain.com来设置证书。我们还为域外需要使用SSH隧道访问数据库的少数用户添加了127.0.0.1

使用PyMongo,我们会遇到以下错误:

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'

此错误最重要的部分是hostname '127.0.0.1' doesn't match '127.0.0.1'。这对我来说意义为零,因为它显然确实匹配,并且使用此x509 SSL证书连接到数据库时,Mongo Shell和Robo 3T都具有零资格。

使用来自Domain外部的Mongo Shell,似乎没有问题:

$  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

因此,隧道可以正常运行,并且MongoDB的SSL x509证书没有任何问题。这就引出了一个问题,为什么Pymongo无法处理给定的x509证书?我没有在主机名列表中使用任何前导或尾随点,这似乎是所有线程在搜索此错误时所关注的。我明确给出了x509证书中列为备用DNS主机名之一的确切主机名。

任何人都可以为我提供有关此错误的任何帮助,我将不胜感激。预先感谢。

1 个答案:

答案 0 :(得分:0)

这是驱动程序中的一些代码。 您的证书已解析,并尝试从 subjectAltName 部分加载DNS名称 https://github.com/mongodb/mongo-python-driver/blob/749c1a2f0bde87a6e6d8df9366e4c90666efd189/pymongo/ssl_match_hostname.py#L103-L113

请注意,驱动程序在subjectAltName中区分“ DNS”和“ IP地址”键输入。 我想您已经在证书中添加了“ 127.0.0.1”作为DNS hostanme,而驱动程序将字符串“ 127.0.0.1”视为IP地址 因此没有匹配项。

这就是为什么代码失败并出现令人困惑的错误的原因。匹配失败不取决于直接值-其原因在于 一个是IP地址,另一个是主机名。

混乱的发生要早一些,在该变量上分配了一个变量host_ip,以确定是否可以将指定的主机名解析为 是否提供IP地址。 https://github.com/mongodb/mongo-python-driver/blob/749c1a2f0bde87a6e6d8df9366e4c90666efd189/pymongo/ssl_match_hostname.py#L98-L102 对于主机名“ 127.0.0.1”,我想它可以正确解析并分配给host_ip variable

现在此检查将失败 https://github.com/mongodb/mongo-python-driver/blob/749c1a2f0bde87a6e6d8df9366e4c90666efd189/pymongo/ssl_match_hostname.py#L107 并且与证书的DNS主机名部分中的“ 127.0.0.1”不匹配。

修复

您的证书的subjectAltName字段应该看起来像这样:
subjectAltName = DNS:<server1>, DNS:<server2>, IP:127.0.0.1