证书和证书签名请求
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 意味着什么。这包括
- 信任分发:信任锚点(CA 证书或证书包)如何分发。
- 允许的主体:当请求不允许的主体时,存在的任何限制和行为。
- 允许的 X.509 扩展:包括 IP subjectAltNames、DNS subjectAltNames、Email subjectAltNames、URI subjectAltNames 等,以及当请求不允许的扩展时,存在的行为。
- 允许的密钥用途 / 扩展密钥用途:当 CSR 中指定的用途与 signer 确定的用途不同时,存在的任何限制和行为。
- 到期/证书生命周期:它是由 signer 固定、由管理员配置、由 CSR 的
spec.expirationSeconds
字段确定等,以及当 signer 确定的到期时间与 CSR 的spec.expirationSeconds
字段不同时,存在的行为。 - 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.expirationSeconds
和 ClusterSigningDuration
的最小值。
注意
spec.expirationSeconds
字段是在 Kubernetes v1.22 中添加的。早期版本的 Kubernetes 不支持此字段。v1.22 之前的 Kubernetes API 服务器在创建对象时将默默地丢弃此字段。Kubernetes signer
Kubernetes 提供了内置的 signer,每个 signer 都有一个知名的 signerName
kubernetes.io/kube-apiserver-client
:签署将被 API 服务器视为客户端证书的证书。绝不会被kube-controller-manager自动批准。- 信任分发:已签署的证书必须被 API 服务器视为客户端证书。CA 包不会通过任何其他方式分发。
- 允许的主体 - 没有主体限制,但审批者和 signer 可能选择不批准或不签署。某些主体(如 cluster-admin 级别的用户或组)在不同分发和安装中有所不同,但在批准和签署之前应受到额外的审查。
CertificateSubjectRestriction
准入插件默认启用,用于限制system:masters
,但这通常不是集群中唯一的 cluster-admin 主体。 - 允许的 X.509 扩展 - 支持 subjectAltName 和密钥用途扩展,并丢弃其他扩展。
- 允许的密钥用途 - 必须包含
["client auth"]
。除了["digital signature", "key encipherment", "client auth"]
之外,不得包含其他密钥用途。 - 到期/证书生命周期 - 对于此 signer 的 kube-controller-manager 实现,设置为
--cluster-signing-duration
选项的最小值,或者(如果已指定)CSR 对象的spec.expirationSeconds
字段。 - CA 位允许/不允许 - 不允许。
kubernetes.io/kube-apiserver-client-kubelet
:签署将被 API 服务器视为客户端证书的证书。可能被kube-controller-manager自动批准。- 信任分发:已签署的证书必须被 API 服务器视为客户端证书。CA 包不会通过任何其他方式分发。
- 允许的主体 - 组织完全是
["system:nodes"]
,通用名是 "system:node:${NODE_NAME}
"。 - 允许的 X.509 扩展 - 支持密钥用途扩展,禁止 subjectAltName 扩展并丢弃其他扩展。
- 允许的密钥用途 -
["key encipherment", "digital signature", "client auth"]
或["digital signature", "client auth"]
。 - 到期/证书生命周期 - 对于此 signer 的 kube-controller-manager 实现,设置为
--cluster-signing-duration
选项的最小值,或者(如果已指定)CSR 对象的spec.expirationSeconds
字段。 - CA 位允许/不允许 - 不允许。
kubernetes.io/kubelet-serving
:签署将被 API 服务器视为有效 kubelet 服务证书的服务证书,但没有其他保证。绝不会被kube-controller-manager自动批准。- 信任分发:已签署的证书必须被 API 服务器视为有效,以终止与 kubelet 的连接。CA 包不会通过任何其他方式分发。
- 允许的主体 - 组织完全是
["system:nodes"]
,通用名是 "system:node:${NODE_NAME}
"。 - 允许的 X.509 扩展 - 支持密钥用途和 DNSName/IPAddress subjectAltName 扩展,禁止 EmailAddress 和 URI subjectAltName 扩展,丢弃其他扩展。必须至少存在一个 DNS 或 IP subjectAltName。
- 允许的密钥用途 -
["key encipherment", "digital signature", "server auth"]
或["digital signature", "server auth"]
。 - 到期/证书生命周期 - 对于此 signer 的 kube-controller-manager 实现,设置为
--cluster-signing-duration
选项的最小值,或者(如果已指定)CSR 对象的spec.expirationSeconds
字段。 - CA 位允许/不允许 - 不允许。
kubernetes.io/legacy-unknown
:完全不保证信任。一些第三方 Kubernetes 分发版可能会接受由它签署的客户端证书。稳定的 CertificateSigningRequest API(版本certificates.k8s.io/v1
及更高版本)不允许将signerName
设置为kubernetes.io/legacy-unknown
。绝不会被kube-controller-manager自动批准。- 信任分发:无。在 Kubernetes 集群中,此 signer 没有标准的信任或分发方式。
- 允许的主体 - 任何
- 允许的 X.509 扩展 - 支持 subjectAltName 和密钥用途扩展,并丢弃其他扩展。
- 允许的密钥用途 - 任何
- 到期/证书生命周期 - 对于此 signer 的 kube-controller-manager 实现,设置为
--cluster-signing-duration
选项的最小值,或者(如果已指定)CSR 对象的spec.expirationSeconds
字段。 - CA 位允许/不允许 - 不允许。
kube-controller-manager 为每个内置 signer 实现控制平面签署。所有这些的失败仅在 kube-controller-manager 日志中报告。
注意
spec.expirationSeconds
字段是在 Kubernetes v1.22 中添加的。早期版本的 Kubernetes 不支持此字段。v1.22 之前的 Kubernetes API 服务器在创建对象时将默默地丢弃此字段。这些 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 的一部分来实现。
注意
在 Kubernetes v1.18 之前,kube-controller-manager 会签署所有标记为已批准的 CSR。注意
spec.expirationSeconds
字段是在 Kubernetes v1.22 中添加的。早期版本的 Kubernetes 不支持此字段。v1.22 之前的 Kubernetes API 服务器在创建对象时将默默地丢弃此字段。基于 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 approve
和 kubectl 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 请求来批准它们。
当你发起批准或拒绝请求时,根据你确定的状态设置 Approved
或 Denied
状态条件
对于 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 对象进行 get、list 和 watch 操作。如果你使用自己的授权机制并在集群中启用了 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
字段派生的前缀命名。斜杠 (/
) 被冒号 (:
) 替换,并追加一个最终的冒号。其后是任意名称。例如,signerexample.com/mysigner
可以关联到 ClusterTrustBundleexample.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 投射卷源。
接下来
- 阅读在集群中管理 TLS 证书
- 阅读使用 CertificateSigningRequest 为 Kubernetes API 客户端颁发证书
- 查看 kube-controller-manager 内置signer的源代码
- 查看 kube-controller-manager 内置批准者的源代码
- 有关 X.509 本身的详细信息,请参阅 RFC 5280 第 3.1 节
- 有关 PKCS#10 证书签名请求语法的详细信息,请参阅 RFC 2986
- 阅读关于 ClusterTrustBundle API 的内容
- %!s(
)
- %!s(