1、概述
在《Kubernetes客户端认证(一)------ 基于CA证书的双向认证方式》和《Kubernetes客户端认证(二)------ 基于ServiceAccount的JWTToken认证》两篇博文中详细介绍了Kubernetes客户端认证方式,包括以证书方式访问的普通用户或进程( 运维人员及 kubectl、 kubelet 等进程);以 ServiceAccount 方式(JWTToken)访问的 Kubernetes 的内部服务进程,它是给运行在 Pod 里的进程用的,它为 Pod 里的进程提供了必要的身份证明,但它并不是给 Kubernetes 集群的用户(系统管理员、 运维人员、租户用户等)用的,对于Kubernetes集群的用户可以通过证书或者接入外部用户系统来提供身份证明。而其中最简单的方式便是为用户签发客户端证书,签发客户端证书有两种方式,一种是基于CA根证书签发证书,另一个种是发起 CSR(Certificate Signing Requests)请求。
在《Kubernetes客户端认证(一)------ 基于CA证书的双向认证方式》这篇博文的3.2章节详细介绍了基于CA根证书签发证书,这种方式因为需要基于根CA证书、根CA私钥进行证书签发,一般需要在集群的master节点上进行配置,但是生产环境下一般情况下我们都没办法进入到集群的master节点机器,所以这种方式用的不多。但是一般情况我们都能拿到对应的一个kubeconfig文件用来访问k8s集群,基于kubeconfig 文件我们也可以进行证书的批准和授权,这就是k8s CSR机制,通过使用CSR方式签发客户端证书,这也是k8s推荐方式,本文主要讲解如果通过k8s CSR机制来为用户签发客户端证书。
2、CertificateSigningRequest(CSR)机制
CertificateSigningRequest(CSR)资源用来向指定的签名者申请证书签名, 在最终签名之前,申请可能被批准,也可能被拒绝。(特性状态: Kubernetes v1.19 [stable])
2.1 请求签名流程
CertificateSigningRequest 资源类型允许客户端基于签名请求申请发放 X.509 证书。 CertificateSigningRequest 对象在 spec.request 字段中包含一个 PEM 编码的 PKCS#10 签名请求。 CertificateSigningRequest 资源类型允许客户端基于签名请求申请发放 X.509 证书。 CertificateSigningRequest 对象在 spec.request 字段中包含一个 PEM 编码的 PKCS#10 签名请求。 CertificateSigningRequest 使用 spec.signerName 字段标示签名者(请求的接收方)。 注意,spec.signerName 在 certificates.k8s.io/v1 之后的 API 版本是必填项。 在 Kubernetes v1.22 和以后的版本,客户可以可选地设置 spec.expirationSeconds 字段来为颁发的证书设定一个特定的有效期。该字段的最小有效值是 600,也就是 10 分钟。
创建完成的 CertificateSigningRequest,要先通过批准,然后才能签名。 根据所选的签名者,CertificateSigningRequest 可能会被控制器自动批准。 否则,就必须人工批准, 人工批准可以使用 REST API(或 go 客户端),也可以执行 kubectl certificate approve 命令。 同样,CertificateSigningRequest 也可能被驳回, 这就相当于通知了指定的签名者,这个证书不能签名。
对于已批准的证书,下一步是签名。 对应的签名控制器首先验证签名条件是否满足,然后才创建证书。 签名控制器然后更新 CertificateSigningRequest, 将新证书保存到现有 CertificateSigningRequest 对象的 status.certificate 字段中。 此时,字段 status.certificate 要么为空,要么包含一个用 PEM 编码的 X.509 证书。 直到签名完成前,CertificateSigningRequest 的字段 status.certificate 都为空。
一旦 status.certificate 字段完成填充,请求既算完成, 客户端现在可以从 CertificateSigningRequest 资源中获取已签名的证书的 PEM 数据。 当然如果不满足签名条件,签名者可以拒签。
为了减少集群中遗留的过时的 CertificateSigningRequest 资源的数量, 一个垃圾收集控制器将会周期性地运行。 此垃圾收集器会清除在一段时间内没有改变过状态的 CertificateSigningRequests:
- 已批准的请求:1 小时后自动删除
- 已拒绝的请求:1 小时后自动删除
- 已失败的请求:1 小时后自动删除
- 挂起的请求:24 小时后自动删除
- 所有请求:在颁发的证书过期后自动删除
2.2 Kubernetes 签名者
Kubernetes 提供了内置的签名者,每个签名者都有一个众所周知的 signerName:
- kubernetes.io/kube-apiserver-client:签名的证书将被 API 服务器视为客户证书。 kube-controller-manager 不会自动批准它。
- kubernetes.io/kube-apiserver-client-kubelet: 签名的证书将被 kube-apiserver 视为客户证书。 kube-controller-manager 可以自动批准它。
- kubernetes.io/kubelet-serving: 签名服务证书,该服务证书被 API 服务器视为有效的 kubelet 服务证书, 但没有其他保证。kube-controller-manager 不会自动批准它。
- kubernetes.io/legacy-unknown: 不保证信任。Kubernetes 的一些第三方发行版可能会使用它签署的客户端证书。
kube-controller-manager 为每个内置签名者实现了控制平面签名。 注意:所有这些故障仅在 kube-controller-manager 日志中报告。
3、通过 CSR 签发客户端证书实例
1)生成用户私钥
openssl genrsa -out zmc.key 2048
2)生成证书请求文件
// 基于用户私钥生成用户证书签名请求文件zmc.csr,其中CN代表k8s用户
openssl req -new -key zmc.key -subj "/CN=zmc" -out zmc.csr
- 通过 kubectl 创建一个 CertificateSigningRequest 并将其提交到 Kubernetes 集群
cat <<EOF | kubectl apply -f -
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
name: zmc
spec:
request: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0KTUlJQ1V6Q0NBVHNDQVFBd0RqRU1NQW9HQTFVRUF3d0RlbTFqTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQwpBUThBTUlJQkNnS0NBUUVBbWFLRWY5ODBIRkhneG9WdXVBL29oNHlvU1JPNG8vbEU1N1J4UmduT1ZlR0tDbXZoCjRSOWZ3bk9IRzZaUjFVdkd4RzdKY0duaFJFMlFlUFVvcXpVYkVxWnFtc0V4NmpmTHgycTRqd3RNRm9GMDFnaHMKY2RwU0p1WTNEeFlKdFhtbUhKM2h3Q1N1MEZPT1NNMHU2V3JWZ0FFVkRCUno2a0RRcE1lcjlscmQwenBIRTdkUgpJSFNYbWVuVTVFTHVuTGh3a0E5TUhuczAweHhkcEhiR2QrTUFTVEU4LzcrNzR5T3hkQmJZQVhRK04rV2pZQ1NWCm5JT1dVUktWSFpCVDJIKzZxMjFvNkVxK2JlczB2MkhBWmZPTjZhOVVoU2FpZjRQRm9vTXlyS21xSFRkMFdrZlkKY1FZZFVzR0tBUFI3YVZrbmx4cWFxVCtEbXNqM0pBVFdZVjZBV3dJREFRQUJvQUF3RFFZSktvWklodmNOQVFFTApCUUFEZ2dFQkFIOFhoZjJEeVlMRldkQmJtazMwY3FHMkI1Qlh5dEFZWU0vOHQ1Z2UvZjhlMUtEeWQ4aXNQbitXCll1ak5mMzFrNGllRGpZb2NPaDFKZFlTRUkrTmgwdkFiVXVWaU94OU1RY3RUZjBXb0hHTklwRlZ3SFVsNTE2R3MKazRMMEgyVXM5WnlNMUJXSGxVaG13MDJncGs5VjV2RDc3QjN6dnh3aUs4UnJyZlA2YWVScGtnbzdOcFg0bEVMWApRSVY0VzBUWExsTjF2Si9UaGVncHhRZVhPZHZMbjJxd084aEgyVmRSRnQzODNWS0RTNFR0UFdWNGw5WTJGSkxVCk01NlhsdEFhcFd2aDBnV3U4RWVjbzhJd0tSY2xvYzU2RUlob05OVzBHN2lUeDgxVW1LUWlPRXl3RVZVNllPaHAKa25wbXhreWFGUXo4QTdIeG1ON2ZIaTdwdUNCcGZDUT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUgUkVRVUVTVC0tLS0tCg==
signerName: kubernetes.io/kube-apiserver-client
#expirationSeconds: 86400 # one day
usages:
- client auth
EOF
- request 字段是 CSR 文件内容的 base64 编码值。 要得到该值,可以执行命令
cat zmc.csr | base64 | tr -d "\n"
- expirationSeconds 证书过期时间(k8s 1.22版本之前没有这个字段,默认的过期时间是十年左右)
- usage 字段必须是 'client auth',代表密钥的用途
注意:下面的 csr 代表的是 certificatesigningrequests(是k8s里面的一种资源),注意与上面的 certificate 区分开来。
查看CertificateSigningRequest资源状态
4)批准 CertificateSigningRequest
kubectl certificate approve zmc
查看CertificateSigningRequest资源状态
5)从 CertificateSigningRequest 导出颁发的证书
kubectl get csr zmc -o jsonpath='{.status.certificate}'| base64 -d > zmc.crt
当前k8s集群版本是1.21,可以看到默认的过期时间是十年左右。
验证新签发用户证书也是由kuberntests集群ca证书签发的。
6)使用签发的用户证书访问k8s集群资源
配置用户信息到到对应的 kubeconfig 上下文中。
将客户端证书文件 zmc.crt 和客户端密钥文件 zmc.key 设置为名为 zmc 的用户凭证,并将这些证书和密钥嵌入到 kubeconfig 文件中
kubectl config set-credentials zmc --client-certificate=zmc.crt --client-key=zmc.key --embed-certs=true
查看kubeconfig最新配置。
kubectl config view
为当前集群和用户zmc配置kubeconfi上下文。
kubectl config set-context zmc@cluster.local --cluster=cluster.local --user=zmc
切换上下文为当前新创建的用户。
kubectl config use-context zmc@cluster.local
再次查看kubeconfig最新配置。
使用签发的用户证书访问k8s集群资源。
注意: 如果访问资源没有权限就为用户当前k8s集群维护对应资源的RBAC权限,这块详细步骤可以参见《Kubernetes客户端认证(一)------ 基于CA证书的双向认证方式》和《Kubernetes客户端认证(二)------ 基于ServiceAccount的JWTToken认证》这两篇博文,本文不再赘余。
4、总结
本文详细讲解了Kubernetes使用CertificateSigningRequest方式签发客户端证书详细步骤,基于kubectl和k8s集群交互为示例,当然也可以通过client-go与kube-apiserver交互管理CertificateSigningRequest资源签发客户端证书,并且对于可信客户端证书颁发请求还可以通过开发自定义控制器自动进行证书审批工作。
参考:K8S集群安全机制
参考:证书和证书签名请求 (详细步骤参加k8s官方文档)