首页 > 解决方案 > Python kubernetes 客户端请求因“无法获取本地颁发者证书”而失败

问题描述

我正在使用受支持的 python Kubernetes 库(pip install kubernetes,v12.0.1)。当我向我的集群请求任何选项(例如创建一个Namespace)时,它会失败并出现以下错误:

import kubernetes.client

def create_namespace(self, namespace_key):                            
    with kubernetes.client.ApiClient(self.configuration) as client:
        api = kubernetes.client.CoreV1Api(client)                  
        api.create_namespace(                                      
            {                                                      
                "apiVersion": "v1",                                
                "kind": "Namespace",                               
                "metadata": {                                      
                    "name": namespace_key,                        
                },                                                 
            }                                                      
        )                                                          


urllib3.exceptions.MaxRetryError: HTTPSConnectionPool(host='<url of my cluster>', port=443): Max retries exceeded with url: /api/v1/namespaces (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer
 certificate (_ssl.c:1123)')))

如果我在客户端中使用关闭 SSL 配置self.configuration.verify_ssl = False,请求会起作用,但它会给我一个警告,告诉我强烈建议不要禁用 SSL。

我的设置:

我需要在本地和/或最终将在生产中运行这些请求的 docker 容器在系统范围内执行什么操作吗?

编辑 更多信息:

>>> import ssl                
>>> print(ssl.OPENSSL_VERSION)
OpenSSL 1.1.1f  31 Mar 2020   

标签: pythonsslkubernetes

解决方案


这最终是我的错误:我的托管 Kubernetes 提供商(Digital Ocean)提供了一个带有 Kubernetes 凭据的自定义 TLS 证书,并且需要用作 k8s python 客户端的证书。

通常,如果您的客户端中使用的证书与 k8s API 的预期不匹配,那么无论托管服务提供商如何,您都可能会看到这一点。

具体来说,这就是我解决 Digital Ocean 问题的方法:

    headers = {
        "Content-Type": "application/json",
        "Authorization": f"Bearer {DIGITAL_OCEAN_ACCESS_TOKEN}",
    }
    url = (
        f"https://api.digitalocean.com/v2/kubernetes/clusters/{cluster_id}/credentials"
    )
    response = requests.get(url, headers=headers)
    data = json.loads(response.content.decode("utf-8"))
    if response.status_code != 200:
        raise Exception(f"Unable to authenticate to cluster {cluster_id}")
    my_clusters_host_url = data["server"]
    my_new_api_key = data["token"]

    # Decode and save certificate
    decoded_certificate = base64.b64decode(data["certificate_authority_data"])
    certificate_authority_data = str(decoded_certificate, "utf-8")

据我所知,客户端不允许您直接设置证书数据;它需要保存到文件中。所以我在下面创建了一个 tmp 文件:

        filepath = f"/tmp/cluster_{cluster.id}.crt"
        with open(filepath, "w+") as f:
            f.write(certificate_authority_data)
        configuration = kubernetes.client.Configuration(
            host=host_url,
            api_key={
                "authorization": f"Bearer {api_key}",
            },
        )
        configuration.ssl_ca_cert = filepath

然后可以在其他 API 请求中使用该配置对象。例如:

        with kubernetes.client.ApiClient(configuration) as client:
            api = kubernetes.client.CoreV1Api(client)
            api.create_namespace(payload)

推荐阅读