在集群中管理 TLS 证书
Kubernetes 提供了一个 certificates.k8s.io
API,它允许您通过您控制的证书颁发机构 (CA) 签发 TLS 证书。这些 CA 和证书可以被您的工作负载用于建立信任。
certificates.k8s.io
API 使用的协议类似于 ACME 草案。
注意
使用certificates.k8s.io
API 创建的证书由 专用 CA 签名。您可以将集群配置为为此目的使用集群根 CA,但您永远不应该依赖此配置。不要假设这些证书将针对集群根 CA 进行验证。开始之前
您需要拥有一个 Kubernetes 集群,并且 kubectl 命令行工具必须配置为与您的集群通信。建议在至少有两个节点的集群上运行本教程,这些节点不充当控制平面主机。如果您还没有集群,您可以使用 minikube 创建一个,或者您可以使用以下 Kubernetes 游乐场之一
您需要 cfssl
工具。您可以从 https://github.com/cloudflare/cfssl/releases 下载 cfssl
。
本页中的一些步骤使用 jq
工具。如果您没有 jq
,您可以通过操作系统的软件源安装它,或者从 https://jqlang.github.io/jq/ 获取它。
信任集群中的 TLS
信任 自定义 CA 来自作为 Pod 运行的应用程序通常需要一些额外的应用程序配置。您需要将 CA 证书捆绑包添加到 TLS 客户端或服务器信任的 CA 证书列表中。例如,您可以通过解析证书链并将解析后的证书添加到 tls.Config
结构中的 RootCAs
字段来使用 golang TLS 配置执行此操作。
注意
即使自定义 CA 证书可能包含在文件系统中(在 ConfigMap kube-root-ca.crt
中),您也不应将该证书颁发机构用于除验证内部 Kubernetes 端点以外的任何目的。内部 Kubernetes 端点的一个示例是在默认命名空间中的名为 kubernetes
的服务。
如果您想将自定义证书颁发机构用于您的工作负载,您应该单独生成该 CA,并使用您的 Pod 可以访问以读取的 ConfigMap 分发其 CA 证书。
请求证书
以下部分演示如何为通过 DNS 访问的 Kubernetes 服务创建 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
是服务的集群 IP,my-svc.my-namespace.svc.cluster.local
是服务的 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
。查看有关 受支持的签名者名称 的文档以了解更多信息。
CSR 现在应该以 Pending 状态从 API 中可见。您可以通过运行以下命令查看它
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
现在,您可以在 Secret 中填充 server.crt
和 server-key.pem
,您稍后可以将其挂载到 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
参数传递给控制器管理器,其中包含指向证书颁发机构密钥对的路径。