如何使用 urllib3 下载服务器 SSL 证书?

How to download server SSL certificate with urllib3?

标题确实是个问题 — 在尝试建立 HTTPS 连接时,我如何urllib3 从远程服务器下载 SSL 证书?

背景 在 ServerVault 领域,我试图弄清 a problem with AWS autoscaling groups 无法下载 phpMyAdmin 作为其初始化进程的一部分。作为尝试诊断的一部分,我现在在 Whosebug 的土地上,使用 python 脚本。我在这里:

#! /usr/bin/python3
import cfnbootstrap
from cfnbootstrap.packages import requests
from requests import utils
from requests.utils import DEFAULT_CA_BUNDLE_PATH
from requests.packages import urllib3

conn = urllib3.connection_from_url("https://dev.mysql.com/", retries=False)
conn.cert_reqs = 'CERT_REQUIRED'
conn.ca_certs = DEFAULT_CA_BUNDLE_PATH
response = conn.request("GET", "/get/")
conn.close()
print(response.status)

效果很好:

$ ./urltest.py 
404

但是,如果我把它改成

conn = urllib3.connection_from_url("https://www.phpmyadmin.net", retries=False)
response = conn.request("GET", "/downloads/")

我运行进入我的问题:

$ ./urltest.py 
Traceback (most recent call last):
  File "/usr/lib/python3.7/site-packages/cfnbootstrap/packages/requests/packages/urllib3/connectionpool.py", line 544, in urlopen
    body=body, headers=headers)
  File "/usr/lib/python3.7/site-packages/cfnbootstrap/packages/requests/packages/urllib3/connectionpool.py", line 341, in _make_request
    self._validate_conn(conn)
  File "/usr/lib/python3.7/site-packages/cfnbootstrap/packages/requests/packages/urllib3/connectionpool.py", line 762, in _validate_conn
    conn.connect()
  File "/usr/lib/python3.7/site-packages/cfnbootstrap/packages/requests/packages/urllib3/connection.py", line 238, in connect
    ssl_version=resolved_ssl_version)
  File "/usr/lib/python3.7/site-packages/cfnbootstrap/packages/requests/packages/urllib3/util/ssl_.py", line 265, in ssl_wrap_socket
    return context.wrap_socket(sock, server_hostname=server_hostname)
  File "/usr/lib64/python3.7/ssl.py", line 423, in wrap_socket
    session=session
  File "/usr/lib64/python3.7/ssl.py", line 870, in _create
    self.do_handshake()
  File "/usr/lib64/python3.7/ssl.py", line 1139, in do_handshake
    self._sslobj.do_handshake()
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: certificate has expired (_ssl.c:1091)

好像不太可能是证书过期了,好像我用浏览器手动去查了一下,万事大吉;但是,我想确切地了解 urllib3 认为哪些证书已过期。我该怎么做?

注意:我没有直接调用 certify.where(),因为我不确定 AWS 在后台执行什么 shananigans,但因为他们的脚本是 auto-installed 作为启动实例的一部分,我对此无能为力,所以我尝试使用他们的内部进程。

问题是 Let's Encrypt DST Root CA X3 于 9 月 30 日到期 -- https://letsencrypt.org/docs/dst-root-ca-x3-expiration-september-2021/。请求包不信任 ISRG Root X1 证书,并尝试使用过期的证书对其进行验证。

https://letsencrypt.org/certificates/ 下载 ISRG X1 PEM 并将其添加到 cfnbootstrap 库的 cacert.pem 文件。

cat isgrootx1.pem >> /path/to/your/python/env/lib/python3.9/site-packages/cfnbootstrap/packages/requests/cacert.pem

您可以通过以下方式找到 cacert.pem 的位置:

from cfnbootstrap.packages.requests.certs import where
print(where())

另外...我会更改您的代码以确保您实际使用的是 cfn 打包请求模块而不是独立请求。否则,您可能需要在这两个位置进行更新。

from cfnbootstrap.packages.requests.utils import DEFAULT_CA_BUNDLE_PATH
from cfnbootstrap.packages.requests.packages import urllib3

conn = urllib3.connection_from_url("https://www.phpmyadmin.net/", retries=False)
conn.cert_reqs = 'CERT_REQUIRED'
conn.ca_certs = DEFAULT_CA_BUNDLE_PATH
response = conn.request("GET", "/get/")
conn.close()
print(response.status)

如果您确实想要更新独立请求和使用 cfnbootstrap 打包的请求,请使用以下命令查找并更新两个 cacert 路径:

import requests
import cfnbootstrap.packages.requests

print(requests.certs.where())
print(cfnbootstrap.packages.requests.certs.where())