管理集群中的 TLS 认证

    certificates.k8s.io API使用的协议类似于 ACME 草案

    说明:

    使用 certificates.k8s.io API 创建的证书由指定 颁发。 将集群配置为使用集群根目录 CA 可以达到这个目的,但是你永远不要依赖这一假定。 不要以为这些证书将针对群根目录 CA 进行验证。

    你必须拥有一个 Kubernetes 的集群,同时你的 Kubernetes 集群必须带有 kubectl 命令行工具。 建议在至少有两个节点的集群上运行本教程,且这些节点不作为控制平面主机。 如果你还没有集群,你可以通过 构建一个你自己的集群,或者你可以使用下面任意一个 Kubernetes 工具构建:

    你需要 cfssl 工具。 你可以从 https://github.com/cloudflare/cfssl/releases 下载 cfssl

    本文中某些步骤使用 jq 工具。如果你没有 jq,你可以通过操作系统的软件源安装, 或者从 获取。

    集群中的 TLS 信任

    信任 Pod 中运行的应用程序所提供的 通常需要一些额外的应用程序配置。 你需要将 CA 证书包添加到 TLS 客户端或服务器信任的 CA 证书列表中。 例如,你可以使用 Golang TLS 配置通过解析证书链并将解析的证书添加到 tls.Config 结构中的 RootCAs 字段中。

    说明:

    即使自定义 CA 证书可能包含在文件系统中(在 ConfigMap kube-root-ca.crt 中), 除了验证内部 Kubernetes 端点之外,你不应将该证书颁发机构用于任何目的。 内部 Kubernetes 端点的一个示例是默认命名空间中名为 kubernetes 的服务。

    如果你想为你的工作负载使用自定义证书颁发机构,你应该单独生成该 CA, 并使用你的 Pod 有读权限的 分发该 CA 证书。

    请求证书

    以下部分演示如何为通过 DNS 访问的 Kubernetes 服务创建 TLS 证书。

    说明: 本教程使用 CFSSL:Cloudflare’s PKI 和 TLS 工具包 了解更多信息。

    通过运行以下命令生成私钥和证书签名请求(或 CSR):

    其中 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 名称。 你能看到的输出类似于:

    1. 2022/02/01 11:45:32 [INFO] generate received request
    2. 2022/02/01 11:45:32 [INFO] received CSR
    3. 2022/02/01 11:45:32 [INFO] generating key: ecdsa-256
    4. 2022/02/01 11:45:32 [INFO] encoded CSR

    此命令生成两个文件;它生成包含 PEM 编码 证书请求的 server.csr, 以及 PEM 编码密钥的 server-key.pem,用于待生成的证书。

    创建证书签名请求(CSR)对象发送到 Kubernetes API

    1. cat <<EOF | kubectl apply -f -
    2. apiVersion: certificates.k8s.io/v1
    3. kind: CertificateSigningRequest
    4. metadata:
    5. name: my-svc.my-namespace
    6. spec:
    7. request: $(cat server.csr | base64 | tr -d '\n')
    8. signerName: example.com/serving
    9. usages:
    10. - digital signature
    11. - key encipherment
    12. - server auth
    13. EOF

    请注意,在步骤 1 中创建的 server.csr 文件是 base64 编码并存储在 .spec.request 字段中的。你还要求提供 “digital signature(数字签名)”, “密钥加密(key encipherment)” 和 “服务器身份验证(server auth)” 密钥用途, 由 example.com/serving 示例签名程序签名的证书。 你也可以要求使用特定的 signerName。更多信息可参阅 。

    在 API server 中可以看到这些 CSR 处于 Pending 状态。执行下面的命令你将可以看到:

      1. Name: my-svc.my-namespace
      2. Annotations: <none>
      3. CreationTimestamp: Tue, 01 Feb 2022 11:49:15 -0500
      4. Requesting User: yourname@example.com
      5. Signer: example.com/serving
      6. Status: Pending
      7. Subject:
      8. Common Name: my-pod.my-namespace.pod.cluster.local
      9. Serial Number:
      10. Subject Alternative Names:
      11. DNS Names: my-pod.my-namespace.pod.cluster.local
      12. my-svc.my-namespace.svc.cluster.local
      13. IP Addresses: 192.0.2.24
      14. 10.0.34.2
      15. Events: <none>

      批准证书签名请求(CSR)

      的批准或者是通过自动批准过程完成的,或由集群管理员一次性完成。 如果你被授权批准证书请求,你可以使用 kubectl 来手动完成此操作;例如:

      1. kubectl certificate approve my-svc.my-namespace
      1. certificatesigningrequest.certificates.k8s.io/my-svc.my-namespace approved

      你现在应该能看到如下输出:

      1. NAME AGE SIGNERNAME REQUESTOR REQUESTEDDURATION CONDITION
      2. my-svc.my-namespace 10m example.com/serving yourname@example.com <none> Approved

      这意味着证书请求已被批准,并正在等待请求的签名者对其签名。

      接下来,你将扮演证书签署者的角色,颁发证书并将其上传到 API 服务器。

      签名者通常会使用其 signerName 查看对象的 CertificateSigningRequest API, 检查它们是否已被批准,为这些请求签署证书,并使用已颁发的证书更新 API 对象状态。

      你需要授权在新证书上提供数字签名。

      首先,通过运行以下命令创建签名证书:

      1. cat <<EOF | cfssl gencert -initca - | cfssljson -bare ca
      2. {
      3. "CN": "My Example Signer",
      4. "key": {
      5. "algo": "rsa",
      6. "size": 2048
      7. }
      8. }
      9. EOF

      你应该看到类似于以下的输出:

      1. 2022/02/01 11:50:39 [INFO] generating a new CA key and certificate from CSR
      2. 2022/02/01 11:50:39 [INFO] generate received request
      3. 2022/02/01 11:50:39 [INFO] received CSR
      4. 2022/02/01 11:50:39 [INFO] encoded CSR
      5. 2022/02/01 11:50:39 [INFO] signed certificate with serial number 263983151013686720899716354349605500797834580472

      这会产生一个证书颁发机构密钥文件(ca-key.pem)和证书(ca.pem)。

      1. {
      2. "default": {
      3. "usages": [
      4. "digital signature",
      5. "key encipherment",
      6. "server auth"
      7. ],
      8. "expiry": "876000h",
      9. "ca_constraint": {
      10. "is_ca": false
      11. }
      12. }
      13. }
      14. }

      使用 server-signing-config.json 签名配置、证书颁发机构密钥文件和证书来签署证书请求:

      1. kubectl get csr my-svc.my-namespace -o jsonpath='{.spec.request}' | \
      2. base64 --decode | \
      3. cfssl sign -ca ca.pem -ca-key ca-key.pem -config server-signing-config.json - | \
      4. cfssljson -bare ca-signed-server

      你应该看到类似于以下的输出:

      1. 2022/02/01 11:52:26 [INFO] signed certificate with serial number 576048928624926584381415936700914530534472870337

      这会生成一个签名的服务证书文件,ca-signed-server.pem

      说明:

      这使用命令行工具 在 .status.certificate 字段中填充 base64 编码的内容。 如果你没有 jq 工具,你还可以将 JSON 输出保存到文件中,手动填充此字段,然后上传结果文件。

      批准 CSR 并上传签名证书后,运行:

      1. kubectl get csr

      输入类似于:

      1. NAME AGE SIGNERNAME REQUESTOR REQUESTEDDURATION CONDITION
      2. my-svc.my-namespace 20m example.com/serving yourname@example.com <none> Approved,Issued

      下载证书并使用它

      现在,作为请求用户,你可以通过运行以下命令下载颁发的证书并将其保存到 server.crt 文件中:

      CSR 被签署并获得批准后,你应该看到以下内容:

      1. kubectl get csr my-svc.my-namespace -o jsonpath='{.status.certificate}' \
      2. | base64 --decode > server.crt

      现在你可以将 server.crtserver-key.pem 填充到 中, 稍后你可以将其挂载到 Pod 中(例如,用于提供 HTTPS 的网络服务器)。

      1. kubectl create secret tls server --cert server.crt --key server-key.pem
      1. secret/server created

      最后,你可以将 ca.pem 填充到 ConfigMap 并将其用作信任根来验证服务证书:

      1. kubectl create configmap example-serving-ca --from-file ca.crt=ca.pem

      批准证书签名请求(CSR)

      Kubernetes 管理员(具有适当权限)可以使用 kubectl certificate approvekubectl certificate deny 命令手动批准(或拒绝)证书签名请求(CSR)。 但是,如果你打算大量使用此 API,则可以考虑编写自动化的证书控制器。

      注意:

      批准证书 CSR 的能力决定了在你的环境中谁信任谁。 不应广泛或轻率地授予批准 CSR 的能力。

      在授予 approve 权限之前,你应该确保自己充分了解批准人的验证要求颁发特定证书的后果。

      无论上述机器或人使用 kubectl,“批准者”的作用是验证 CSR 满足如下两个要求:

      1. CSR 的 subject 控制用于签署 CSR 的私钥。这解决了伪装成授权主体的第三方的威胁。 在上述示例中,此步骤将验证该 Pod 控制了用于生成 CSR 的私钥。
      2. CSR 的 subject 被授权在请求的上下文中执行。 这点用于处理不期望的主体被加入集群的威胁。 在上述示例中,此步骤将是验证该 Pod 是否被允许加入到所请求的服务中。

      当且仅当满足这两个要求时,审批者应该批准 CSR,否则拒绝 CSR。

      有关证书批准和访问控制的更多信息, 请阅读证书签名请求参考页。