证书和证书签名请求

Kubernetes 证书和信任包 API 通过为 Kubernetes API 的客户端提供程序化接口来请求和获取 X.509 证书颁发机构 (CA) 的 X.509 证书,从而实现 X.509 凭据供应的自动化。

对于分发信任包,还存在实验性(alpha)支持。

证书签名请求

特性状态: Kubernetes v1.19 [stable]

一个CertificateSigningRequest (CSR) 资源用于请求由指定 signer 签署证书,该请求在最终签署前可能被批准或拒绝。

请求签署流程

CertificateSigningRequest 资源类型允许客户端基于签名请求来请求颁发 X.509 证书。CertificateSigningRequest 对象在 spec.request 字段中包含一个 PEM 编码的 PKCS#10 签名请求。CertificateSigningRequest 使用 spec.signerName 字段指定 signer(即接收请求的实体)。注意,在 API 版本 certificates.k8s.io/v1 之后,spec.signerName 是必需字段。在 Kubernetes v1.22 及更高版本中,客户端可以可选地设置 spec.expirationSeconds 字段,以请求颁发证书的特定生命周期。此字段的最小有效值为 600,即十分钟。

创建后,CertificateSigningRequest 必须在签署前被批准。根据所选的 signer,CertificateSigningRequest 可能会被控制器自动批准。否则,CertificateSigningRequest 必须通过 REST API(或 client-go)手动批准,或通过运行 kubectl certificate approve 命令批准。同样,CertificateSigningRequest 也可能被拒绝,这将告知配置的 signer 不得签署该请求。

对于已批准的证书,下一步是签署。相关的签署控制器首先验证签署条件是否满足,然后创建一个证书。签署控制器随后更新 CertificateSigningRequest,将新证书存储到现有 CertificateSigningRequest 对象的 status.certificate 字段中。status.certificate 字段要么为空,要么包含一个 X.509 证书(采用 PEM 格式编码)。CertificateSigningRequest 的 status.certificate 字段在 signer 执行此操作之前为空。

一旦填充了 status.certificate 字段,请求即已完成,客户端现在可以从 CertificateSigningRequest 资源中获取已签署的证书 PEM 数据。如果批准条件不满足,signer 也可以拒绝证书签署。

为了减少集群中遗留的旧 CertificateSigningRequest 资源数量,垃圾回收控制器会定期运行。垃圾回收会移除在一定时间内未改变状态的 CertificateSigningRequest

  • 已批准的请求:1 小时后自动删除
  • 已拒绝的请求:1 小时后自动删除
  • 失败的请求:1 小时后自动删除
  • Pending 的请求:24 小时后自动删除
  • 所有请求:颁发证书过期后自动删除

证书签署授权

允许创建和检索任何 CertificateSigningRequest

  • 动作 (Verbs): create, get, list, watch, 组 (group): certificates.k8s.io, 资源 (resource): certificatesigningrequests

例如

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: csr-creator
rules:
- apiGroups:
  - certificates.k8s.io
  resources:
  - certificatesigningrequests
  verbs:
  - create
  - get
  - list
  - watch

允许批准 CertificateSigningRequest

  • 动作 (Verbs): get, list, watch, 组 (group): certificates.k8s.io, 资源 (resource): certificatesigningrequests
  • 动作 (Verbs): update, 组 (group): certificates.k8s.io, 资源 (resource): certificatesigningrequests/approval
  • 动作 (Verbs): approve, 组 (group): certificates.k8s.io, 资源 (resource): signers, 资源名称 (resourceName): <signerNameDomain>/<signerNamePath><signerNameDomain>/*

例如

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: csr-approver
rules:
- apiGroups:
  - certificates.k8s.io
  resources:
  - certificatesigningrequests
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - certificates.k8s.io
  resources:
  - certificatesigningrequests/approval
  verbs:
  - update
- apiGroups:
  - certificates.k8s.io
  resources:
  - signers
  resourceNames:
  - example.com/my-signer-name # example.com/* can be used to authorize for all signers in the 'example.com' domain
  verbs:
  - approve

允许签署 CertificateSigningRequest

  • 动作 (Verbs): get, list, watch, 组 (group): certificates.k8s.io, 资源 (resource): certificatesigningrequests
  • 动作 (Verbs): update, 组 (group): certificates.k8s.io, 资源 (resource): certificatesigningrequests/status
  • 动作 (Verbs): sign, 组 (group): certificates.k8s.io, 资源 (resource): signers, 资源名称 (resourceName): <signerNameDomain>/<signerNamePath><signerNameDomain>/*
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: csr-signer
rules:
- apiGroups:
  - certificates.k8s.io
  resources:
  - certificatesigningrequests
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - certificates.k8s.io
  resources:
  - certificatesigningrequests/status
  verbs:
  - update
- apiGroups:
  - certificates.k8s.io
  resources:
  - signers
  resourceNames:
  - example.com/my-signer-name # example.com/* can be used to authorize for all signers in the 'example.com' domain
  verbs:
  - sign

Signer

Signer 抽象地表示可能签署或已签署安全证书的实体。

任何特定集群之外可用的 signer 都应提供有关其工作方式的信息,以便使用者了解这对 CertificateSigningRequests 和(如果已启用)ClusterTrustBundles 意味着什么。这包括

  1. 信任分发:信任锚点(CA 证书或证书包)如何分发。
  2. 允许的主体:当请求不允许的主体时,存在的任何限制和行为。
  3. 允许的 X.509 扩展:包括 IP subjectAltNames、DNS subjectAltNames、Email subjectAltNames、URI subjectAltNames 等,以及当请求不允许的扩展时,存在的行为。
  4. 允许的密钥用途 / 扩展密钥用途:当 CSR 中指定的用途与 signer 确定的用途不同时,存在的任何限制和行为。
  5. 到期/证书生命周期:它是由 signer 固定、由管理员配置、由 CSR 的 spec.expirationSeconds 字段确定等,以及当 signer 确定的到期时间与 CSR 的 spec.expirationSeconds 字段不同时,存在的行为。
  6. CA 位允许/不允许:以及当 signer 不允许时,如果 CSR 包含 CA 证书的请求,存在的行为。

通常,一旦 CSR 被批准并颁发证书,CertificateSigningRequest 的 status.certificate 字段包含单个 PEM 编码的 X.509 证书。有些 signer 将多个证书存储到 status.certificate 字段中。在这种情况下,signer 的文档应说明附加证书的含义;例如,这可能是证书加上在 TLS 握手期间要呈现的中间证书。

如果您希望使信任锚点(根证书)可用,这应与 CertificateSigningRequest 及其 status.certificate 字段分开进行。例如,您可以使用 ClusterTrustBundle。

PKCS#10 签名请求格式没有指定证书到期或生命周期的标准机制。因此,到期或生命周期必须通过 CSR 对象的 spec.expirationSeconds 字段进行设置。内置的 signer 在未指定 spec.expirationSeconds 时,使用 ClusterSigningDuration 配置选项(默认为 1 年,即 kube-controller-manager 的 --cluster-signing-duration 命令行标志)作为默认值。当指定了 spec.expirationSeconds 时,使用 spec.expirationSecondsClusterSigningDuration 的最小值。

Kubernetes signer

Kubernetes 提供了内置的 signer,每个 signer 都有一个知名的 signerName

  1. kubernetes.io/kube-apiserver-client:签署将被 API 服务器视为客户端证书的证书。绝不会被kube-controller-manager自动批准。

    1. 信任分发:已签署的证书必须被 API 服务器视为客户端证书。CA 包不会通过任何其他方式分发。
    2. 允许的主体 - 没有主体限制,但审批者和 signer 可能选择不批准或不签署。某些主体(如 cluster-admin 级别的用户或组)在不同分发和安装中有所不同,但在批准和签署之前应受到额外的审查。CertificateSubjectRestriction 准入插件默认启用,用于限制 system:masters,但这通常不是集群中唯一的 cluster-admin 主体。
    3. 允许的 X.509 扩展 - 支持 subjectAltName 和密钥用途扩展,并丢弃其他扩展。
    4. 允许的密钥用途 - 必须包含 ["client auth"]。除了 ["digital signature", "key encipherment", "client auth"] 之外,不得包含其他密钥用途。
    5. 到期/证书生命周期 - 对于此 signer 的 kube-controller-manager 实现,设置为 --cluster-signing-duration 选项的最小值,或者(如果已指定)CSR 对象的 spec.expirationSeconds 字段。
    6. CA 位允许/不允许 - 不允许。
  2. kubernetes.io/kube-apiserver-client-kubelet:签署将被 API 服务器视为客户端证书的证书。可能被kube-controller-manager自动批准。

    1. 信任分发:已签署的证书必须被 API 服务器视为客户端证书。CA 包不会通过任何其他方式分发。
    2. 允许的主体 - 组织完全是 ["system:nodes"],通用名是 "system:node:${NODE_NAME}"。
    3. 允许的 X.509 扩展 - 支持密钥用途扩展,禁止 subjectAltName 扩展并丢弃其他扩展。
    4. 允许的密钥用途 - ["key encipherment", "digital signature", "client auth"]["digital signature", "client auth"]
    5. 到期/证书生命周期 - 对于此 signer 的 kube-controller-manager 实现,设置为 --cluster-signing-duration 选项的最小值,或者(如果已指定)CSR 对象的 spec.expirationSeconds 字段。
    6. CA 位允许/不允许 - 不允许。
  3. kubernetes.io/kubelet-serving:签署将被 API 服务器视为有效 kubelet 服务证书的服务证书,但没有其他保证。绝不会被kube-controller-manager自动批准。

    1. 信任分发:已签署的证书必须被 API 服务器视为有效,以终止与 kubelet 的连接。CA 包不会通过任何其他方式分发。
    2. 允许的主体 - 组织完全是 ["system:nodes"],通用名是 "system:node:${NODE_NAME}"。
    3. 允许的 X.509 扩展 - 支持密钥用途和 DNSName/IPAddress subjectAltName 扩展,禁止 EmailAddress 和 URI subjectAltName 扩展,丢弃其他扩展。必须至少存在一个 DNS 或 IP subjectAltName。
    4. 允许的密钥用途 - ["key encipherment", "digital signature", "server auth"]["digital signature", "server auth"]
    5. 到期/证书生命周期 - 对于此 signer 的 kube-controller-manager 实现,设置为 --cluster-signing-duration 选项的最小值,或者(如果已指定)CSR 对象的 spec.expirationSeconds 字段。
    6. CA 位允许/不允许 - 不允许。
  4. kubernetes.io/legacy-unknown:完全不保证信任。一些第三方 Kubernetes 分发版可能会接受由它签署的客户端证书。稳定的 CertificateSigningRequest API(版本 certificates.k8s.io/v1 及更高版本)不允许将 signerName 设置为 kubernetes.io/legacy-unknown。绝不会被kube-controller-manager自动批准。

    1. 信任分发:无。在 Kubernetes 集群中,此 signer 没有标准的信任或分发方式。
    2. 允许的主体 - 任何
    3. 允许的 X.509 扩展 - 支持 subjectAltName 和密钥用途扩展,并丢弃其他扩展。
    4. 允许的密钥用途 - 任何
    5. 到期/证书生命周期 - 对于此 signer 的 kube-controller-manager 实现,设置为 --cluster-signing-duration 选项的最小值,或者(如果已指定)CSR 对象的 spec.expirationSeconds 字段。
    6. CA 位允许/不允许 - 不允许。

kube-controller-manager 为每个内置 signer 实现控制平面签署。所有这些的失败仅在 kube-controller-manager 日志中报告。

这些 signer 的信任分发是带外发生的。任何超出上述描述的信任都纯属巧合。例如,一些分发版可能会将 kubernetes.io/legacy-unknown 视为 kube-apiserver 的客户端证书,但这并不是标准。这些用法与 ServiceAccount token Secrets 的 .data[ca.crt] 没有任何关系。该 CA 包仅保证验证使用默认服务 (kubernetes.default.svc) 到 API 服务器的连接。

自定义 signer

你也可以引入自己的自定义 signer,它应该具有类似的带前缀名称,但使用你自己的域名。例如,如果你代表一个使用 open-fictional.example 域名的开源项目,那么你可能会使用 issuer.open-fictional.example/service-mesh 作为 signer 名称。

自定义 signer 使用 Kubernetes API 颁发证书。参见基于 API 的 signer

签署

控制平面 signer

Kubernetes 控制平面将每个Kubernetes signer 作为 kube-controller-manager 的一部分来实现。

基于 API 的 signer

REST API 的用户可以通过向待签署 CSR 的 status 子资源提交 UPDATE 请求来签署 CSR。

作为此请求的一部分,应设置 status.certificate 字段以包含已签署的证书。此字段包含一个或多个 PEM 编码的证书。

所有 PEM 数据块都必须具有 "CERTIFICATE" 标签,不包含头部,并且编码的数据必须是 RFC5280 第 4 节 中描述的 BER 编码的 ASN.1 Certificate 结构。

证书内容示例

-----BEGIN CERTIFICATE-----
MIIDgjCCAmqgAwIBAgIUC1N1EJ4Qnsd322BhDPRwmg3b/oAwDQYJKoZIhvcNAQEL
BQAwXDELMAkGA1UEBhMCeHgxCjAIBgNVBAgMAXgxCjAIBgNVBAcMAXgxCjAIBgNV
BAoMAXgxCjAIBgNVBAsMAXgxCzAJBgNVBAMMAmNhMRAwDgYJKoZIhvcNAQkBFgF4
MB4XDTIwMDcwNjIyMDcwMFoXDTI1MDcwNTIyMDcwMFowNzEVMBMGA1UEChMMc3lz
dGVtOm5vZGVzMR4wHAYDVQQDExVzeXN0ZW06bm9kZToxMjcuMC4wLjEwggEiMA0G
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDne5X2eQ1JcLZkKvhzCR4Hxl9+ZmU3
+e1zfOywLdoQxrPi+o4hVsUH3q0y52BMa7u1yehHDRSaq9u62cmi5ekgXhXHzGmm
kmW5n0itRECv3SFsSm2DSghRKf0mm6iTYHWDHzUXKdm9lPPWoSOxoR5oqOsm3JEh
Q7Et13wrvTJqBMJo1GTwQuF+HYOku0NF/DLqbZIcpI08yQKyrBgYz2uO51/oNp8a
sTCsV4OUfyHhx2BBLUo4g4SptHFySTBwlpRWBnSjZPOhmN74JcpTLB4J5f4iEeA7
2QytZfADckG4wVkhH3C2EJUmRtFIBVirwDn39GXkSGlnvnMgF3uLZ6zNAgMBAAGj
YTBfMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDAjAMBgNVHRMB
Af8EAjAAMB0GA1UdDgQWBBTREl2hW54lkQBDeVCcd2f2VSlB1DALBgNVHREEBDAC
ggAwDQYJKoZIhvcNAQELBQADggEBABpZjuIKTq8pCaX8dMEGPWtAykgLsTcD2jYr
L0/TCrqmuaaliUa42jQTt2OVsVP/L8ofFunj/KjpQU0bvKJPLMRKtmxbhXuQCQi1
qCRkp8o93mHvEz3mTUN+D1cfQ2fpsBENLnpS0F4G/JyY2Vrh19/X8+mImMEK5eOy
o0BMby7byUj98WmcUvNCiXbC6F45QTmkwEhMqWns0JZQY+/XeDhEcg+lJvz9Eyo2
aGgPsye1o3DpyXnyfJWAWMhOz7cikS5X2adesbgI86PhEHBXPIJ1v13ZdfCExmdd
M1fLPhLyR54fGaY+7/X8P9AZzPefAkwizeXwe9ii6/a08vWoiE4=
-----END CERTIFICATE-----

非 PEM 内容可以出现在 CERTIFICATE PEM 数据块之前或之后,且未经验证,以便如 RFC7468 第 5.2 节 中描述的允许解释性文本。

当编码为 JSON 或 YAML 时,此字段是 base-64 编码的。包含上述示例证书的 CertificateSigningRequest 将如下所示

apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
...
status:
  certificate: "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JS..."

批准或拒绝

signer 基于 CertificateSigningRequest 颁发证书之前,signer 通常会检查该 CSR 的颁发是否已获得批准

控制平面自动批准

kube-controller-manager 附带一个内置的批准者,用于 signerName 为 kubernetes.io/kube-apiserver-client-kubelet 的证书,该批准者将节点凭据的 CSR 上的各种权限委托给授权。kube-controller-manager 向 API 服务器 POST SubjectAccessReview 资源,以便检查证书批准的授权。

使用 kubectl 进行批准或拒绝

Kubernetes 管理员(具有适当权限)可以使用 kubectl certificate approvekubectl certificate deny 命令手动批准(或拒绝)CertificateSigningRequests。

使用 kubectl 批准 CSR

kubectl certificate approve <certificate-signing-request-name>

同样,要拒绝 CSR

kubectl certificate deny <certificate-signing-request-name>

使用 Kubernetes API 进行批准或拒绝

REST API 的用户可以通过向待批准 CSR 的 approval 子资源提交 UPDATE 请求来批准 CSR。例如,你可以编写一个operator,该 operator 监视特定类型的 CSR,然后发送 UPDATE 请求来批准它们。

当你发起批准或拒绝请求时,根据你确定的状态设置 ApprovedDenied 状态条件

对于 Approved 的 CSR

apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
...
status:
  conditions:
  - lastUpdateTime: "2020-02-08T11:37:35Z"
    lastTransitionTime: "2020-02-08T11:37:35Z"
    message: Approved by my custom approver controller
    reason: ApprovedByMyPolicy # You can set this to any string
    type: Approved

对于 Denied 的 CSR

apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
...
status:
  conditions:
  - lastUpdateTime: "2020-02-08T11:37:35Z"
    lastTransitionTime: "2020-02-08T11:37:35Z"
    message: Denied by my custom approver controller
    reason: DeniedByMyPolicy # You can set this to any string
    type: Denied

通常将 status.conditions.reason 设置为使用 TitleCase 格式的机器友好原因代码;这是一种约定,但你可以将其设置为任何你喜欢的值。如果你想添加供人阅读的备注,请使用 status.conditions.message 字段。

集群信任包

特性状态: Kubernetes v1.27 [alpha]

ClusterTrustBundles 是一种集群范围的对象,用于将 X.509 信任锚点(根证书)分发给集群内的工作负载。它们旨在与 CertificateSigningRequests 中的signer概念很好地协同工作。

ClusterTrustBundles 可以采用两种模式使用:signer 关联模式signer 非关联模式

共同属性和验证

所有 ClusterTrustBundle 对象对其 trustBundle 字段的内容都具有强验证。该字段必须包含一个或多个 X.509 证书,这些证书经过 DER 序列化,每个都包含在 PEM CERTIFICATE 数据块中。证书必须解析为有效的 X.509 证书。

冷门的 PEM 特性(如块间数据和块内头部)在对象验证期间会被拒绝,或者可以被对象的消费者忽略。此外,允许消费者以自己任意但稳定的顺序重新排序包中的证书。

ClusterTrustBundle 对象在集群内应被视为全局可读。如果你的集群使用RBAC 授权,所有 ServiceAccount 都拥有默认的授权,允许它们对所有 ClusterTrustBundle 对象进行 getlistwatch 操作。如果你使用自己的授权机制并在集群中启用了 ClusterTrustBundles,则应设置等效的规则,使这些对象在集群内公开,以便它们按预期工作。

如果你的集群默认没有列出集群信任包的权限,可以模拟你有权访问的服务账号来查看可用的 ClusterTrustBundles

kubectl get clustertrustbundles --as='system:serviceaccount:mynamespace:default'

Signer 关联的 ClusterTrustBundles

Signer 关联的 ClusterTrustBundles 与signer 名称相关联,如下所示

apiVersion: certificates.k8s.io/v1alpha1
kind: ClusterTrustBundle
metadata:
  name: example.com:mysigner:foo
spec:
  signerName: example.com/mysigner
  trustBundle: "<... PEM data ...>"

这些 ClusterTrustBundles 旨在由集群中特定于 signer 的控制器维护,因此它们具有一些安全特性

  • 要创建或更新 signer 关联的 ClusterTrustBundle,你必须被允许对 signer 进行 attest 操作(自定义授权动词 attest,API 组 certificates.k8s.io;资源路径 signers)。你可以为特定的资源名称 <signerNameDomain>/<signerNamePath> 或匹配模式(如 <signerNameDomain>/*)配置授权。
  • Signer 关联的 ClusterTrustBundles 必须使用从其 spec.signerName 字段派生的前缀命名。斜杠 (/) 被冒号 (:) 替换,并追加一个最终的冒号。其后是任意名称。例如,signer example.com/mysigner 可以关联到 ClusterTrustBundle example.com:mysigner:<任意名称>

Signer 关联的 ClusterTrustBundles 通常会在工作负载中通过 signer 名称上的字段选择器与单独的标签选择器组合来使用。

Signer 非关联的 ClusterTrustBundles

Signer 非关联的 ClusterTrustBundles 具有空的 spec.signerName 字段,如下所示

apiVersion: certificates.k8s.io/v1alpha1
kind: ClusterTrustBundle
metadata:
  name: foo
spec:
  # no signerName specified, so the field is blank
  trustBundle: "<... PEM data ...>"

它们主要用于集群配置用例。每个 signer 非关联的 ClusterTrustBundle 都是一个独立的对象,这与 signer 关联的 ClusterTrustBundles 的惯例分组行为形成对比。

Signer 非关联的 ClusterTrustBundles 没有 attest 动词要求。相反,你可以使用通常的机制直接控制对它们的访问,例如基于角色的访问控制。

为了将它们与 signer 关联的 ClusterTrustBundles 区分开来,signer 非关联的 ClusterTrustBundles 的名称不得包含冒号 (:)。

从 Pod 访问 ClusterTrustBundles

特性状态: Kubernetes v1.29 [alpha]

ClusterTrustBundles 的内容可以注入到容器文件系统中,类似于 ConfigMap 和 Secret。有关更多详细信息,请参见clusterTrustBundle 投射卷源

接下来

上次修改时间:2025 年 3 月 4 日下午 8:22 PST:将客户端证书的 CSR 移至任务章节 (2a3a72e16c)