管理集群中的 TLS 证书
Kubernetes 提供了一个 certificates.k8s.io
API,它允许你配置由你控制的证书颁发机构 (CA) 签署的 TLS 证书。你的工作负载可以使用这些 CA 和证书来建立信任。
certificates.k8s.io
API 使用的协议类似于 ACME 草案。
准备开始
你需要有一个 Kubernetes 集群,并且必须将 kubectl 命令行工具配置为与你的集群通信。建议在至少有两个节点不充当控制平面主机的集群上运行本教程。如果你还没有集群,可以使用 minikube 创建一个集群,或者可以使用以下 Kubernetes 游乐场之一
你需要 cfssl
工具。你可以从 https://github.com/cloudflare/cfssl/releases 下载 cfssl
。
此页面中的某些步骤使用 jq
工具。如果你没有 jq
,可以通过操作系统的软件源安装它,或者从 https://jqlang.github.io/jq/ 获取它。
信任集群中的 TLS
要信任来自作为 Pod 运行的应用程序的自定义 CA,通常需要一些额外的应用程序配置。你需要将 CA 证书包添加到 TLS 客户端或服务器信任的 CA 证书列表中。例如,你可以通过解析证书链并将解析的证书添加到 tls.Config
结构中的 RootCAs
字段来使用 golang TLS 配置执行此操作。
注意
即使自定义 CA 证书可能包含在文件系统中(在 ConfigMap kube-root-ca.crt
中),也不应将该证书颁发机构用于验证内部 Kubernetes 端点以外的任何目的。内部 Kubernetes 端点的一个示例是默认命名空间中名为 kubernetes
的 Service。
如果要为工作负载使用自定义证书颁发机构,则应单独生成该 CA,并使用 Pod 有权读取的 ConfigMap 分发其 CA 证书。
请求证书
以下部分演示如何为通过 DNS 访问的 Kubernetes Service 创建 TLS 证书。
注意
本教程使用 CFSSL:Cloudflare 的 PKI 和 TLS 工具包,点击此处 了解更多信息。创建证书签名请求
运行以下命令生成私钥和证书签名请求(或 CSR)
cat <<EOF | cfssl genkey - | cfssljson -bare server
{
"hosts": [
"my-svc.my-namespace.svc.cluster.local",
"my-pod.my-namespace.pod.cluster.local",
"192.0.2.24",
"10.0.34.2"
],
"CN": "my-pod.my-namespace.pod.cluster.local",
"key": {
"algo": "ecdsa",
"size": 256
}
}
EOF
其中 192.0.2.24
是 Service 的集群 IP,my-svc.my-namespace.svc.cluster.local
是 Service 的 DNS 名称,10.0.34.2
是 Pod 的 IP,my-pod.my-namespace.pod.cluster.local
是 Pod 的 DNS 名称。你应该会看到类似于以下内容的输出
2022/02/01 11:45:32 [INFO] generate received request
2022/02/01 11:45:32 [INFO] received CSR
2022/02/01 11:45:32 [INFO] generating key: ecdsa-256
2022/02/01 11:45:32 [INFO] encoded CSR
此命令生成两个文件;它生成包含 PEM 编码的 PKCS#10 证书请求的 server.csr
,以及包含 PEM 编码的密钥的 server-key.pem
,该密钥用于尚未创建的证书。
创建要发送到 Kubernetes API 的 CertificateSigningRequest 对象
生成 CSR 清单(YAML 格式),并将其发送到 API 服务器。你可以通过运行以下命令来完成此操作
cat <<EOF | kubectl apply -f -
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
name: my-svc.my-namespace
spec:
request: $(cat server.csr | base64 | tr -d '\n')
signerName: example.com/serving
usages:
- digital signature
- key encipherment
- server auth
EOF
请注意,在步骤 1 中创建的 server.csr
文件经过 base64 编码并存储在 .spec.request
字段中。你还请求了一个具有“数字签名”、“密钥加密”和“服务器身份验证”密钥用途的证书,该证书由示例 example.com/serving
签名者签名。必须请求特定的 signerName
。有关更多信息,请查看支持的签名者名称 的文档。
现在,应该可以在 API 中以“待处理”状态查看 CSR。你可以通过运行以下命令来查看它
kubectl describe csr my-svc.my-namespace
Name: my-svc.my-namespace
Labels: <none>
Annotations: <none>
CreationTimestamp: Tue, 01 Feb 2022 11:49:15 -0500
Requesting User: [email protected]
Signer: example.com/serving
Status: Pending
Subject:
Common Name: my-pod.my-namespace.pod.cluster.local
Serial Number:
Subject Alternative Names:
DNS Names: my-pod.my-namespace.pod.cluster.local
my-svc.my-namespace.svc.cluster.local
IP Addresses: 192.0.2.24
10.0.34.2
Events: <none>
批准 CertificateSigningRequest
批准 证书签名请求 可以通过自动批准流程完成,也可以由集群管理员一次性完成。如果你有权批准证书请求,则可以使用 kubectl
手动执行此操作;例如
kubectl certificate approve my-svc.my-namespace
certificatesigningrequest.certificates.k8s.io/my-svc.my-namespace approved
你现在应该会看到以下内容
kubectl get csr
NAME AGE SIGNERNAME REQUESTOR REQUESTEDDURATION CONDITION
my-svc.my-namespace 10m example.com/serving [email protected] <none> Approved
这意味着证书请求已获批准,并且正在等待请求的签名者对其进行签名。
签署 CertificateSigningRequest
接下来,你将扮演证书签名者的角色,颁发证书,并将其上传到 API。
签名者通常会监视 CertificateSigningRequest API 中是否有带有其 signerName
的对象,检查它们是否已获批准,为这些请求签署证书,并使用颁发的证书更新 API 对象状态。
创建证书颁发机构
你需要一个颁发机构来为新证书提供数字签名。
首先,通过运行以下命令创建签名证书
cat <<EOF | cfssl gencert -initca - | cfssljson -bare ca
{
"CN": "My Example Signer",
"key": {
"algo": "rsa",
"size": 2048
}
}
EOF
你应该会看到类似于以下内容的输出
2022/02/01 11:50:39 [INFO] generating a new CA key and certificate from CSR
2022/02/01 11:50:39 [INFO] generate received request
2022/02/01 11:50:39 [INFO] received CSR
2022/02/01 11:50:39 [INFO] generating key: rsa-2048
2022/02/01 11:50:39 [INFO] encoded CSR
2022/02/01 11:50:39 [INFO] signed certificate with serial number 263983151013686720899716354349605500797834580472
这将生成证书颁发机构密钥文件 (ca-key.pem
) 和证书 (ca.pem
)。
颁发证书
{
"signing": {
"default": {
"usages": [
"digital signature",
"key encipherment",
"server auth"
],
"expiry": "876000h",
"ca_constraint": {
"is_ca": false
}
}
}
}
使用 server-signing-config.json
签名配置以及证书颁发机构密钥文件和证书对证书请求进行签名
kubectl get csr my-svc.my-namespace -o jsonpath='{.spec.request}' | \
base64 --decode | \
cfssl sign -ca ca.pem -ca-key ca-key.pem -config server-signing-config.json - | \
cfssljson -bare ca-signed-server
你应该会看到类似于以下内容的输出
2022/02/01 11:52:26 [INFO] signed certificate with serial number 576048928624926584381415936700914530534472870337
这将生成一个已签名的服务证书文件 ca-signed-server.pem
。
上传已签名的证书
最后,在 API 对象的状态中填充已签名的证书
kubectl get csr my-svc.my-namespace -o json | \
jq '.status.certificate = "'$(base64 ca-signed-server.pem | tr -d '\n')'"' | \
kubectl replace --raw /apis/certificates.k8s.io/v1/certificatesigningrequests/my-svc.my-namespace/status -f -
注意
这使用命令行工具jq
在 .status.certificate
字段中填充 base64 编码的内容。如果你没有 jq
,也可以将 JSON 输出保存到文件中,手动填充此字段,然后上传生成的文件。批准 CSR 并上传已签名的证书后,运行
kubectl get csr
输出类似于以下内容
NAME AGE SIGNERNAME REQUESTOR REQUESTEDDURATION CONDITION
my-svc.my-namespace 20m example.com/serving [email protected] <none> Approved,Issued
下载证书并使用它
现在,作为请求用户,你可以下载颁发的证书并将其保存到 server.crt
文件中,方法是运行以下命令
kubectl get csr my-svc.my-namespace -o jsonpath='{.status.certificate}' \
| base64 --decode > server.crt
现在,你可以将 server.crt
和 server-key.pem
填充到 Secret 中,稍后可以将其挂载到 Pod 中(例如,与提供 HTTPS 的 Web 服务器一起使用)。
kubectl create secret tls server --cert server.crt --key server-key.pem
secret/server created
最后,你可以将 ca.pem
填充到 ConfigMap 中,并将其用作信任根来验证服务证书
kubectl create configmap example-serving-ca --from-file ca.crt=ca.pem
configmap/example-serving-ca created
批准 CertificateSigningRequests
Kubernetes 管理员(具有适当权限)可以使用 kubectl certificate approve
和 kubectl certificate deny
命令手动批准(或拒绝)CertificateSigningRequests。但是,如果你打算大量使用此 API,则可以考虑编写自动证书控制器。
警告
批准 CSR 的能力决定了在你的环境中谁信任谁。批准 CSR 的能力不应广泛或轻易地授予。
在授予 approve
权限之前,你应该确保自己完全了解审批人承担的验证要求**以及**颁发特定证书的后果。
无论是使用 kubectl 的机器还是人类(如上所述),*审批人* 的作用都是验证 CSR 是否满足以下两个要求
- CSR 的使用者控制用于签署 CSR 的私钥。这解决了第三方冒充授权使用者的威胁。在上面的示例中,此步骤将验证 Pod 是否控制用于生成 CSR 的私钥。
- CSR 的标的被授权在请求的上下文中执行操作。这解决了不希望的标的加入集群的威胁。在上面的例子中,这一步将验证 Pod 是否被允许参与请求的服务。
当且仅当满足这两个要求时,批准者才应批准 CSR,否则应拒绝 CSR。
有关证书批准和访问控制的更多信息,请阅读证书签名请求参考页面。
配置您的集群以提供签名
本页假设已设置签名者以服务于证书 API。Kubernetes 控制器管理器提供了一个签名者的默认实现。要启用它,请将 --cluster-signing-cert-file
和 --cluster-signing-key-file
参数传递给控制器管理器,并使用证书颁发机构密钥对的路径。