AppEngine urlfetch validate_certificate=False/None 未被尊重

AppEngine urlfetch validate_certificate=False/None not being respected

在 AppEngine 开发者应用程序服务器中,我收到如下错误:

SSLCertificateError: Invalid and/or missing SSL certificate for URL ...

当我像这样使用自签名证书对 https 服务器进行提取时(几乎总是 localhost 通过 ssh 端口转发到虚拟机):

result = urlfetch.fetch(url=url, method=method, payload=payload,
                        deadline=DEADLINE, validate_certificate=None)

对于 validate_certificateFalse 的无效证书,人们不会期望 SSL 失败,尽管这很可能是 Python 中 2.7.9 策略的副作用验证 ssl 证书。

请注意,为 validate_certificate 传递 False(而不是 None)也不起作用。

此问题发生在 Python 2.7.9-10 上,通过 Homebrew/XCode 在 OS X 10.10.2-4 上,AppEngine 1.9.18 到 1.19.26。

在 Google App Engine 上存在关于此的问题(例如 12096),但我正在寻找解决方法。

以下是我尝试解决的问题:

  1. 将证书添加到 Mac 的登录钥匙串(在浏览器中工作,而不是来自 Python)

  2. 将证书添加到 app-engine-python/lib/cacerts/cacerts.txt and/or ./lib/cacerts/urlfetch_cacerts.txt(虽然这可能需要打开验证 on 才能工作,因为这似乎是唯一使用它们的情况)例如

    $ echo >> /usr/local/share/app-engine-python/lib/cacerts/urlfetch_cacerts.txt

    $ openssl x509 -subject -in server.crt >> /usr/local/share/app-engine-python/lib/cacerts/urlfetch_cacerts.txt

  3. 使用 PEP-0476 解决方法禁用 ssl HTTPs 检查,即

    ssl._create_default_https_context = ssl._create_unverified_context

    google/appengine/dist27/python_std_lib/httplib.py

  4. import ssl 或之后(第 1149 行附近)

这在 Mac 上尤其成问题,因为从 XCode 7/OS X El Capital 开始降级不再是一个实际的选择。

更好的解决方法是不涉及每次更新开发应用程序服务器时适当地猴子修补 AppEngine 代码。


编辑

请注意,Mac 内置 OpenSSL 证书存储在 /System/Library/OpenSSL 中,它受到 SIP/rootlessness 的保护,f运行kly 很难处理并且如果可以的话,这是一个值得保留的功能。

我已验证证书使用 openssl s_client -connect localhost:7500 -CAfile server.pem 有效。

它已被添加到钥匙串和 /usr/local/etc/openssl/certs 中,格式为 hash.#,其中散列来自 openssl x509 -subject_hash -in server.pem(或自制 ssl,即 /usr/local/Cellar/openssl/1.0.2d_1/bin/openssl)。在这种情况下 /usr/local/Cellar/openssl/1.0.2d_1/bin/openssl s_client -connect localhost:7500 会验证证书(但 python 仍然不会)。

我试过使用 python 和 openssl 的自制版本,但无济于事。 运行 Python 中的以下内容似乎总是失败;

./pve/bin/python -c "import requests; requests.get('https://localhost:7500')"

这也会在 SSL_CERT_FILE 设置为服务器证书的情况下失败(即,为了增加措施,人们可能希望它起作用,因为 openssl 命令基本上是这样工作的),并且在 SSL_CERT_PATH 设置为 /usr/local/etc/openssl/certs.

注意,pve 是一个虚拟环境,其中 help(ssl) 显示 FILE/usr/local/Cellar/python/2.7.10_2/Frameworks/Python.framework/Versions/2.7/lib/python2.7/ssl.py

进一步验证自制软件 Python 的 _ssl.so 链接到自制软件的 openssl I 运行:

xcrun otool -L /usr/local/Cellar/python/2.7.10_2/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-dynload/_ssl.so

哪个returns

./Cellar/python/2.7.10_2/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-dynload/_ssl.so:

/usr/local/opt/openssl/lib/libssl.1.0.0.dylib (compatibility version 1.0.0, current version 1.0.0)

/usr/local/opt/openssl/lib/libcrypto.1.0.0.dylib (compatibility version 1.0.0, current version 1.0.0)

/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1225.1.1)

如果运行 brew info openssl 它会在 CAVEATS 下备注:

A CA file has been bootstrapped using certificates from the system keychain. To add additional certificates, place .pem files in /usr/local/etc/openssl/certs

但显然出于某种原因 python 没有使用自制程序的 openssl 算法来查找证书。

所以我仍然不知道为什么 Python 标准库不验证文档中指定的 OpenSSL 目录中的证书以及钥匙串(在 .pem.p12 格式,"always trust" 对应 Secure Sockets Layer (SSL)).

这是一个 dev_appserver 错误,由最近 Python 版本(我相信 2.7.9)中的 httplib.HTTPSConnection 行为更改(默认打开证书检查)引起。

由于错误存在于内部 dev_appserver 代码(appengine SDK 的文件 google_appengine/google/appengine/api/urlfetch_stub.py)中 运行 独立于测试的应用程序,因此无法修复将在 SDK 更新后继续存在。

我能想到的唯一永久解决方法是启用 validate_certificate 并将 CA 证书添加到 urlfetch_cacerts.txt 文件。作为临时修复,您可以使用解决方法 #3 修补 urlfetch_stub.py

我 运行 在 Windows 上遇到了同样的问题。我使用的是旧版本的 Python (2.7)。当我升级到 Python 2.7.11 时,问题就消失了。

在我的案例中,解决此问题的解决方法是将.../google-cloud-sdk/platform/lib/third_party/fancy_urllib 文件夹复制到项目文件夹。

Othervise urlfetch_stub.py 在 _SetupSSL 中调用导入 fancy_urllib 调用 导入错误:没有名为 fancy_urllib

的模块

SW:

  • Ubuntu 18.04
  • Python 2.7.17 / 2.7.12
  • Google 云 SDK 319.0.0

警告:root:/Users/<用户名>/Library/Python/2.7/lib/python/site-packages/lib/cacerts/urlfetch_cacerts.txt 丢失;没有这个 urlfetch 将无法验证 SSL 证书。

这是对我有用的方法

cd /Users/<username>/Library/Python/2.7/lib/python/site-packages/lib

Google Cloud SDK 位于 /Users/<用户名>/Documents/libraries/google-cloud-sdk/

sudo ln -s /Users/<username>/Documents/libraries/google-cloud-sdk/platform/google_appengine/lib/cacerts cacerts