在升级到 v1.23 之前确保网络钩子证书的兼容性


1.23 版开始,Kubernetes 不再支持使用证书中的 X.509 通用名称 (CN) 字段执行服务器身份验证。Kubernetes 已改为仅依赖 X.509 主题备用名称 (SAN) 字段中的信息。

为了防止您的集群受到影响,在将集群升级到 Kubernetes 版本 1.23 之前,您必须为网络钩子和聚合 API 服务器后端替换没有 SAN 的不兼容证书。

Kubernetes 为什么不再支持没有 SAN 的后端证书

GKE 运行的是开源 Kubernetes,它使用 kube-apiserver 组件,通过传输层安全协议 (TLS) 联系您的 webhook 和聚合 API 服务器后端。kube-apiserver 组件是使用 Go 编程语言编写的。

在 Go 1.15 之前,TLS 客户端使用两个步骤来验证所连接的服务器的身份:

  1. 检查服务器的 DNS 名称(或 IP 地址)是否呈现为服务器证书上的 SAN 之一。
  2. 作为后备,请检查服务器的 DNS 名称(或 IP 地址)是否与服务器证书上的 CN 相同。

2011 年,RFC 6125 已完全弃用了基于 CN 字段的服务器身份验证。各种浏览器和其他安全关键型应用均不再使用该字段。

为了与更广泛的 TLS 生态系统保持一致,Go 1.15 移除了其验证过程中的第 2 步,但留下了一个调试开关 (x509ignoreCN=0) 来启用旧行为,从而简化迁移过程。Kubernetes 1.19 版是使用 Go 1.15 构建的第一个版本。版本 1.19 到 1.22 的 GKE 集群默认会启用此调试开关,以便给客户留出更充裕的时间来替换受影响的网络钩子和聚合 API 服务器后端的证书。

Kubernetes 1.23 版是使用 Go 1.17 构建的,其中移除了该调试开关。GKE 将您的集群升级到 1.23 版后,调用将无法从集群的控制平面连接到未提供具有适当 SAN 的有效 X.509 证书的 webhook 或聚合 API 服务。

确定受影响的集群

对于运行版本至少为 1.21.9 或 1.22.3 的补丁程序的集群

对于启用了 Cloud Logging 且使用补丁程序版本 1.21.9 和 1.22.3 或更高版本的集群,GKE 提供 Cloud Audit Logs 日志来识别从集群调用受影响后端的操作。您可以使用以下过滤条件来搜索日志:

logName =~ "projects/.*/logs/cloudaudit.googleapis.com%2Factivity"
resource.type = "k8s_cluster"
operation.producer = "k8s.io"
"invalid-cert.webhook.gke.io"

如果您的集群未调用使用受影响证书的后端,则不会看到任何日志。如果您的确看到此类审核日志,便会发现其中包含受影响后端的主机名。

以下是由 default 命名空间中名为 example-webhook 的服务托管的网络钩子后端的日志条目示例:

{
  ...
  resource {
    type: "k8s_cluster",
    "labels": {
      "location": "us-central1-c",
      "cluster_name": "example-cluster",
      "project_id": "example-project"
    }
  },
  labels: {
    invalid-cert.webhook.gke.io/example-webhook.default.svc: "No subjectAltNames returned from example-webhook.default.svc:8443",
    ...
  },
  logName: "projects/example-project/logs/cloudaudit.googleapis.com%2Factivity",
  operation: {
    ...
    producer: "k8s.io",
    ...
  },
  ...
}

受影响服务(例如 example-webhook.default.svc)的主机名会作为后缀添加到以 invalid-cert.webhook.gke.io/ 开头的标签名称中。您还可以从 resource.labels.cluster_name 标签获得进行调用的集群的名称,在此示例中为 example-cluster

弃用提示

您可以通过弃用提示了解哪些集群使用了不兼容的证书。弃用提示适用于运行 1.22.6-gke.1000 或更高版本的集群。

其他集群版本

如果您的集群使用的是 1.22 次要版本上 1.22.3 之前的补丁程序版本,或者早于 1.21.9 的任何补丁程序版本,您可以通过以下两种方式确定您的集群是否受到此弃用的影响:

方法 1(推荐):将集群升级到支持使用日志识别受影响的证书的补丁程序版本。确保您的集群已启用 Cloud Logging。集群升级后,每次集群尝试调用未提供具备适当 SAN 的证书的 Service 时,都会生成标识性 Cloud Audit Logs 日志。由于日志仅在调用尝试时生成,因此建议在升级后等待 30 天,以便有足够的时间来调用所有调用路径。

建议使用日志来识别受影响的服务,因为这种方法将自动生成日志,显示受影响的服务,从而最大限度地减少手动工作量。

方法 2:检查集群中网络钩子或聚合 API 服务器使用的证书,以确定它们是否因没有 SAN 而受到影响:

  1. 获取集群中的网络钩子和聚合 API 服务器列表,并确定其后端(服务或网址)。
  2. 检查这些后端服务使用的证书

鉴于以这种方式检查所有证书所需的手动工作量,只有在将集群升级到 Kubernetes 1.21 版之前需要评估 Kubernetes 1.23 版中相关弃用项的影响的情况下,才应使用此方法。如果您可以将集群升级到 1.21,则应先升级到此版本,然后按照方法 1 中的说明避免手动操作。

确定要检查的后端服务

要确定可能受此次弃用影响的后端,请获取网络钩子和聚合 API Service 及其在集群中的关联后端的列表。

如需列出集群中的所有相关网络钩子,请使用以下 kubectl 命令:

kubectl get mutatingwebhookconfigurations -A   # mutating admission webhooks

kubectl get validatingwebhookconfigurations -A # validating admission webhooks

您可以通过检查 Webhook 配置中的 clientConfig.service 字段webhooks.clientConfig.url 字段来获取给定 Webhook 的关联后端服务或网址:

kubectl get mutatingwebhookconfigurations example-webhook -o yaml

此命令的输出类似以下内容:

apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
webhooks:
- admissionReviewVersions:
  clientConfig:
    service:
        name: example-service
        namespace: default
        port: 443

请注意,clientConfig 可以将其后端指定为 Kubernetes Service (clientConfig.service) 或网址 (clientConfig.url)。

如需列出集群中的所有相关聚合 API Service,请使用以下 kubectl 命令:

kubectl get apiservices -A |grep -v Local      # aggregated API services

此命令的输出类似如下所示:

NAME                     SERVICE                      AVAILABLE   AGE
v1beta1.metrics.k8s.io   kube-system/metrics-server   True        237d

此示例从 kube-system 命名空间返回 metric-server Service。

您可以通过检查 spec.service 字段来获取给定聚合 API 的关联 Service:

kubectl get apiservices v1beta1.metrics.k8s.io -o yaml

此命令的输出类似如下所示:

...
apiVersion: apiregistration.k8s.io/v1
kind: APIService
spec:
  service:
    name: metrics-server
    namespace: kube-system
    port: 443

检查一个 Service 的证书

确定要检查的相关后端 Service 之后,您可以检查每个特定 Service 的证书,例如 example-service

  1. 找到该 Service 的选择器和目标端口:

    kubectl describe service example-service
    

    此命令的输出类似如下所示:

    Name: example-service
    Namespace: default
    Labels: run=nginx
    Selector: run=nginx
    Type: ClusterIP
    IP: 172.21.xxx.xxx
    Port: 443
    TargetPort: 444
    

    在此示例中,example-service 的选择器是 run=nginx,目标端口是 444

  2. 找到与选择器匹配的 Pod:

    kubectl get pods --selector=run=nginx
    

    此命令的输出类似如下所示:

    NAME          READY   STATUS    RESTARTS   AGE
    example-pod   1/1     Running   0          21m
    
  3. 设置端口转发

    (从您的 kubectl localhost 到 pod)。

    kubectl port-forward pods/example-pod LOCALHOST_PORT:TARGET_PORT # port forwarding in background
    

    在该命令中进行以下替换:

    • LOCALHOST_PORT:要侦听的地址。
    • TARGET_PORT:来自第 1 步的 TargetPort
  4. 使用 openssl 输出 Service 所使用的证书:

    openssl s_client -connect localhost:LOCALHOST_PORT </dev/null | openssl x509 -noout -text
    

    此示例输出显示的是一个有效证书(即带有 SAN 条目):

    Subject: CN = example-service.default.svc
    X509v3 extensions:
      X509v3 Subject Alternative Name:
        DNS:example-service.default.svc
    

    此示例输出显示的是一个缺少 SAN 的证书:

    Subject: CN = example-service.default.svc
      X509v3 extensions:
          X509v3 Key Usage: critical
              Digital Signature, Key Encipherment
          X509v3 Extended Key Usage:
              TLS Web Server Authentication
          X509v3 Authority Key Identifier:
              keyid:1A:5F:29:D8:E9:3C:54:3C:35:CC:D8:AB:D1:21:FD:C3:56:25:C0:74
    
  5. 使用以下命令移除在后台运行的端口转发:

    $ jobs
    [1]+  Running                 kubectl port-forward pods/example-pod 8888:444 &
    $ kill %1
    [1]+  Terminated              kubectl port-forward pods/example 8888:444
    

检查网址后端的证书

如果网络钩子使用 url backend,请直接连接到网址中指定的主机名。例如,如果网址为 https://example.com:123/foo/bar,请使用以下 openssl 命令输出后端所使用的证书:

  openssl s_client -connect example.com:123 </dev/null | openssl x509 -noout -text

降低 1.23 升级的风险

一旦您确定受影响的集群及其后端服务使用没有 SAN 的证书,您必须在将集群升级到 1.23 版之前更新网络钩子和聚合 API 服务器后端,以使用具有适当 SAN 的证书。

除非您替换证书或者 1.22 版达到标准支持服务终止期限,否则 GKE 不会自动升级其后端使用不兼容证书的 1.22.6-gke.1000 版或更高版本的集群。

如果您的集群所用的 GKE 版本低于 1.22.6-gke.1000,您可以通过配置维护排除项来暂时阻止自动升级,从而防止次要升级。

资源

如需详细了解此变化,请参阅以下资源:

  • Kubernetes 1.23 版本说明
    • Kubernetes 使用 Go 1.17 构建。此版本的 Go 不再支持使用 GODEBUG=x509ignoreCN=0 环境设置,而此项设置用来重新启用将 X.509 服务证书的 CN 视为主机名的已弃用旧版行为。
  • Kubernetes 1.19Kubernetes 1.20 版本说明
    • 在没有 SAN 的情况下将 X.509 服务证书上的 CN 字段视为主机名的已弃用旧版行为现在默认处于停用状态。
  • Go 1.17 版本说明
    • 临时 GODEBUG=x509ignoreCN=0 标志已被移除。
  • Go 1.15 版本说明
    • 在没有 SAN 的情况下将 X.509 证书上的 CN 字段视为主机的已弃用旧版行为现在默认处于停用状态。
  • RFC 6125(第 46 页)
    • 虽然现有做法支持使用 CN 值,但它已被弃用,我们建议证书授权机构改为提供 subjectAltName 值。
  • 准入网络钩子