Python3上使用Pymongo时SSL握手问题

27
尝试连接Azure CosmosDB mongo服务器会导致SSL握手错误。我正在使用Python3和Pymongo来连接我的Azure CosmosDB。如果我使用Python27运行代码,则连接正常工作,但在使用Python3时会导致以下错误:
import pymongo
from pymongo import MongoClient
import json
import sys

def check_server_status(client, data):
   '''check the server status of the connected endpoint'''
   db = client.result_DB
   server_status = db.command('serverStatus')
   print('Database server status:')
   print(json.dumps(server_status, sort_keys=False, indent=2, separators=(',', ': ')))
   coll = db.file_result
   print (coll)
   coll.insert_one(data)

def main():
    uri = "mongodb://KEY123@backend.documents.azure.com:10255/?ssl=true&replicaSet=globaldb"
    client = pymongo.MongoClient(uri)
    emp_rec1 = {
        "name":"Mr.Geek",
        "eid":24,
        "location":"delhi"
        }
    check_server_status(client, emp_rec1)

if __name__ == "__main__":
    main()

在Python3上运行此代码会导致以下错误: pymongo.errors.ServerSelectionTimeoutError: SSL握手失败:backendstore.documents.azure.com:10255: [SSL:CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:749)
当我使用Python27运行相同的代码时,这是我的成功输出:
Database server status: { "_t": "OKMongoResponse", "ok": 1 } Collection(Database(MongoClient(host=['backend.documents.azure.com:10255'], document_class=dict, tz_aware=False, connect=True, ssl=True, replicaset='globaldb'), u'result_DB'), u'file_result')

4
非常重要:这里有几个答案建议使用 ssl_cert_reqs=ssl.CERT_NONEtlsAllowInvalidCertificates=True。它们并不能解决问题,只是通过连接而没有进行任何证书验证来消除错误,这是不安全的。如果您想为测试而这样做,那么可以,但是请不要在任何情况下在生产环境中使用! - Victor Schröder
12个回答

33

在Windows上可以这样做

pip install certifi

然后在代码中使用:

import certifi
ca = certifi.where()

client = pymongo.MongoClient(
"mongodb+srv://username:password@cluster0.xxxxx.mongodb.net/xyzdb?retryWrites=true&w=majority", tlsCAFile=ca)

2
这到底是做什么的?我尝试了一下,它确实解决了我的问题,但我很难理解问题是什么(因为它是在一夜之间发生的),以及这个技巧如何解决它。 - vianmixt
4
MongoClient需要TLS证书进行安全通信,有时Python无法通过TLS发出请求,因此我们在这里明确要求MongoClient使用certifi包请求连接mongodb。 - Ankit Parmar
需要在URI中添加tls=true吗? - Himal Acharya
@HimalAcharya 是的,如果您正在使用证书进行身份验证。 - Ankit Parmar
它对我起作用了。我遇到了一个与 SSL 证书失败相关的问题。我应用了这个解决方案,它起作用了。 - djangodeveloper
显示剩余3条评论

16

通过这个修改解决了问题:

client = pymongo.MongoClient(uri, ssl_cert_reqs=ssl.CERT_NONE)

6
这是否意味着它不使用 TLS 握手?这似乎不是在生产中使用的最好想法。 - axme100
2
这对于测试以及在开发场景下知道你正在连接到什么时是可以的。然而,这不是我所说的解决方案。 - Douglas Plumley
1
解决 ServerSelectionTimeoutError 的绝佳方案!当然,这主要用于测试和开发目的,不要将此解决方案用于生产环境! - adir abargil

11

PyMongo官方文档的 TLS/SSL and PyMongo 部分介绍了以下问题:

TLS错误通常分为两类,证书验证失败或协议版本不匹配。类似于以下错误消息意味着 OpenSSL 无法验证服务器的证书:

[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed

这通常是因为 OpenSSL 没有访问系统的根证书或证书已过期。Linux用户应确保已从其Linux供应商安装了最新的根证书更新。使用从python.org下载的Python 3.6.0或更高版本的macOS用户可能需要运行与python一起提供的脚本来安装根证书:

open "/Applications/Python <YOUR PYTHON VERSION>/Install Certificates.command"

旧版PyPy和PyPy3便携式版本的用户可能需要设置环境变量以告诉OpenSSL在哪里找到根证书。可以使用pypi中的certifi模块轻松完成此操作:

$ pypy -m pip install certifi
$ export SSL_CERT_FILE=$(pypy -c "import certifi; print(certifi.where())")

您可以尝试按照上述说明解决您的问题,这似乎是针对Linux和Mac用户的。在Windows上,我无法在Python 3.73.6中重现您的问题。如果您有任何疑虑,请随时让我知道。


谢谢你的回答!对于任何尝试通过Djongo连接的人来说,这是我唯一有效的解决方案。顺便说一下,我使用的是MacOS,Django 1.3和Django 2.2。 - José Ripoll
非常重要的是,在下一次运行时打开一个新的 shell。我碰巧在另一个 Windows 的 SO 回答中看到了它。如果我错过了它,可能会浪费很多时间。 - aydow

6

在尝试从Digital Ocean连接mongodb时遇到相同的问题,通过在MongoClient中使用这个带参数的函数解决:

def get_client(host,port,username,password,db):
      return MongoClient('mongodb://{}:{}/'.format(host,port),
                         username=username,
                         password=password,
                         authSource=db,
                         ssl=True,ssl_cert_reqs=ssl.CERT_NONE)

client = get_client("host-ip","port","username","password","db-name")

4
在 Mac Mojave 10.14.6 上,我使用了(PyMongo 3.10 和 python 3.7)来解决以下问题:
flask pymongo pymongo.errors.ServerSelectionTimeoutError [SSL: CERTIFICATE_VERIFY_FAILED]
在终端中执行以下命令:
sudo /Applications/Python\ 3.7/Install\ Certificates.command

如果你使用其他版本的Python,只需更改版本号即可(在我的情况下,我使用的是Python 3.7)


3
cluster = MongoClient(
    "url",
    ssl=True,
    ssl_cert_reqs=ssl.CERT_NONE,
)

2
虽然这段代码可能解决了问题,但是包括解释它如何以及为什么解决了问题将有助于提高您的帖子质量,并可能导致更多的赞。请记住,您正在回答未来读者的问题,而不仅仅是现在提问的人。请[编辑]您的答案以添加解释并指出适用的限制和假设。 - Yunnosch

3

默认情况下,pymongo 依赖于操作系统的根证书。

可能是 Atlas 自己更新了其证书,也可能是您的操作系统发生了变化。 "certificate verify failed" 经常发生是因为 OpenSSL 没有访问系统的根证书或证书已过期。有关如何进行故障排除,请参阅 TLS/SSL 和 PyMongo — PyMongo 3.12.0 文档 107。

请尝试:

client = pymongo.MongoClient(connection, tlsCAFile=certifi.where())

不要忘记安装certifi


2
确保你在MongoDb Atlas仪表板上将你的IP地址添加到白名单中!
这个问题阻止了我完成SSL握手。

1
将以下代码添加到您的客户端连接中将解决此问题:
tls=True, tlsAllowInvalidCertificates=True

# Example 
client = MongoClient(mongo_uri, tls=True, tlsAllowInvalidCertificates=True)

你的回答可以通过提供更多的支持性信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人可以确认你的回答是否正确。你可以在帮助中心找到关于如何撰写好回答的更多信息。 - undefined

0

导入 ssl

仅仅导入这个就解决了我的问题。在我的 MacBook 上,它在 Windows PC 上也正常工作。


你的回答可以通过提供更多支持性信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人能够确认你的回答是否正确。你可以在帮助中心找到关于如何撰写好回答的更多信息。 - Community
你的回答可以通过提供更多的支持性信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人可以确认你的回答是否正确。你可以在帮助中心找到有关如何撰写好的回答的更多信息。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接