• 在 Kubernetes 环境中实现证书管理的自动化


    原文作者:Jason Schmidt - F5 NGINX 解决方案架构师

    原文链接:在 Kubernetes 环境中实现证书管理的自动化

    转载来源:NGINX 中文官网


    NGINX 唯一中文官方社区 ,尽在 nginx.org.cn

    有效的 SSL/TLS 证书是现代应用环境的核心要求。但遗憾的是,在部署应用时,证书的更新管理很容易被忽略。证书的有效期有限且时长不等,DigiCert 证书的有效期在 13 个月左右,Let’s Encrypt 证书的有效期为 90 天。为了确保安全访问,这些证书需要在到期前更新/重新颁发。由于大多数运维团队的工作任务繁重,证书更新有时会被搁置,因此在证书临近(或更糟糕)超过到期日时,手忙脚乱地管理证书的现象时常发生。

    其实,情况大可不必如此。只需做好一些规划和准备,就可以简化并实现证书管理的自动化。在这里,我们将介绍一款解决方案,面向使用以下三种技术的 Kubernetes 环境:

    通过本文,您将了解如何通过向端点提供自动更新的证书来简化证书管理。

    Kubernetes 环境中的证书

    在讨论技术细节之前,我们需要先定义一些术语。“TLS 证书”是指在我们的 Ingress controller(Ingress 控制器)上启用 HTTPS 连接所需的两个组件:

    1. 证书

    2. 私钥

    证书和私钥都由 Let’s Encrypt 颁发。关于 TLS 证书如何工作的详细说明,请参阅 DigiCert 文章《TLS/SSL 证书如何工作》

    在 Kubernetes 中,这两个组件被存储为 Secrets。Kubernetes 工作负载,例如 NGINX Ingress Controller cert-manager 可以写入和读取这些 Secret,也可以由有权访问 Kubernetes 安装的用户进行管理。

    cert-manager 简介

    cert-manager 项目是一个可以与 Kubernetes 和 OpenShift 协同工作的证书控制器。当部署在 Kubernetes 中时,cert-manager 将自动颁发 Ingress controller 所需的证书,并确保它们是有效和最新的。此外,它将跟踪证书的到期日,并按照配置的时间间隔尝试更新。虽然它与大量公共和私有证书颁发机构合作,但在这里我们将只介绍它与 Let’s Encrypt 的集成。

    两种 challenge 类型

    当使用 Let’s Encrypt 时,所有证书管理都可以自动进行。虽然这提供了极大的便利,但也带来了一个问题:这项服务如何确保您拥有完全限定域名 (FQDN)?

    这个问题可使用 challenge 来解决,challenge 会要求您回答一个仅有权访问特定域名的 DNS 记录的人员才能发起的验证请求。challenge 采取以下两种形式中的一种:

    1. HTTP-01:可通过为要将证书颁发给的 FQDN 创建 DNS 记录来解决该 challenge。例如,如果您的服务器位于 IP www.xxx.yyy.zzz,并且您的 FQDN 是 cert.example.com,那么该 challenge 机制将在位于 www.xxx.yyy.zzz 的服务器上暴露一个令牌,Let’s Encrypt 服务器将试图通过 cert.example.com 访问该令牌。如果成功,challenge 通过并颁发证书。

    HTTP-01 是生成证书的最简单方法,因为它不需要直接访问 DNS 提供商。这种类型的 challenge 总是通过 HTTP 80 支持的 DNS 端口 进行。注意,当使用 HTTP-01 challenge 时,cert-manager 将利用 Ingress controller 来提供 challenge 令牌。

    1. DNS-01:这个 challenge 使用令牌创建 DNS TXT 记录,然后由颁发机构进行验证。如果令牌被认可,则证明您拥有对该域名的所有权,并且可以立即为其记录颁发证书。与 HTTP-01 challenge 不同的是,使用 DNS-01 challenge 时,FQDN 不需要解析服务器的 IP 地址(甚至不存在)。此外,当 80 端口被屏蔽时,可以使用 DNS-01。不方便的是需要通过访问 cert-manager 安装的 API 令牌提供对 DNS 基础设施的访问权限。

    Ingress Controllers

    Ingress controller 是 Kubernetes 的一种 service,它被专门用于从集群外部引入流量,将流量负载均衡到内部 Pods(一组单个或多个容器),并管理 egress 出向流量。此外,Ingress controller 通过 Kubernetes API 进行控制,当添加、移除 Pods 或 Pods 出现故障时,它将监控并更新负载均衡配置。

    有关 Ingress controller 的更多信息,请参阅以下博客:

    在下面的示例中,我们将使用由 F5 NGINX 开发和维护的 NGINX Ingress Controller。

    证书管理 示例

    这些示例假定您拥有一个可供测试使用的有效 Kubernetes 安装,并且该安装可以分配一个外部 IP 地址(Kubernetes LoadBalancer 对象)。此外,它还假定您可以在 80 和 443 端口(如果使用 HTTP-01 challenge)或只在 443 端口(如果使用 DNS-01 challenge)上接收流量。这些示例都使用 Mac OS X 进行演示,但也可以使用 Linux 或 WSL。

    您还需要一个 DNS 提供商以及允许调整 A 记录的 FQDN。如果您使用 HTTP-01 challenge,则只需要添加一个 A 记录(或让人代您添加一个)。如果您使用 DNS-01 challenge,则需要能够对支持的 DNS 提供商或支持的 webhook 提供商进行 API 访问。

    部署 NGINX Ingress Controller

    最简单的方法是通过 Helm 进行部署。这种部署允许您同时使用 Kubernetes Ingress 和 NGINX Virtual Server CRD。

    1. 添加 NGINX 仓库。

    1. $ helm repo add nginx-stable https://helm.nginx.com/stable
    2. "nginx-stable" has been added to your repositories

    2. 更新仓库。

    1. $ helm repo update
    2. Hang tight while we grab the latest from your chart repositories...
    3. ...Successfully got an update from the "nginx-stable" chart repository
    4. Update Complete. ⎈Happy Helming!

    3. 部署 Ingress controller。

    1. $ helm install nginx-kic nginx-stable/nginx-ingress \
    2. --namespace nginx-ingress  --set controller.enableCustomResources=true \
    3. --create-namespace  --set controller.enableCertManager=true
    4. NAME: nginx-kic
    5. LAST DEPLOYED: Thu Sep  1 15:58:15 2022
    6. NAMESPACE: nginx-ingress
    7. STATUS: deployed
    8. REVISION: 1
    9. TEST SUITE: None
    10. NOTES:
    11. The NGINX Ingress Controller has been installed.

    4. 检查部署,并检索面向 Ingress controller 的 egress 的 IP 地址。注意,如果没有有效的 IP 地址,则无法继续。

    1. $ kubectl get deployments --namespace nginx-ingress
    2. NAME                      READY   UP-TO-DATE   AVAILABLE   AGE
    3. nginx-kic-nginx-ingress   1/1     1            1           23s
    4. $ kubectl get services --namespace nginx-ingress
    5. NAME                      TYPE           CLUSTER-IP      EXTERNAL-IP    PORT(S)                      AGE
    6. nginx-kic-nginx-ingress   LoadBalancer   10.128.60.190   www.xxx.yyy.zzz   80:31526/TCP,443:32058/TCP   30s

    添加 DNS A 记录

    具体流程将视您的 DNS 提供商而定。这个 DNS 名称需要能够从 Let’s Encrypt 服务器进行解析,在此之前,您可能需要等待记录传播。有关这方面的更多信息,请参阅 SiteGround 文章《什么是 DNS 传播,为什么需要这么长时间?》

    如果能够解析所选择的 FQDN,则可以进入下一步。

    1. $ host cert.example.com
    2. cert.example.com has address www.xxx.yyy.zzz

    部署 cert-manager

    下一步是部署最新版本的 cert-manager。同样,我们将使用 Helm 进行部署。

    1. 添加 Helm 仓库。

    1. $ helm repo add jetstack https://charts.jetstack.io
    2. "jetstack" has been added to your repositories

    2. 更新仓库。

    1. $ helm repo update
    2. Hang tight while we grab the latest from your chart repositories...
    3. ...Successfully got an update from the "nginx-stable" chart repository
    4. ...Successfully got an update from the "jetstack" chart repository
    5. Update Complete. ⎈Happy Helming!

    3. 部署 cert-manager。

    1. $ helm install cert-manager jetstack/cert-manager \
    2. --namespace cert-manager --create-namespace \
    3. --version v1.9.1 --set installCRDs=true
    4. NAME: cert-manager
    5. LAST DEPLOYED: Thu Sep 1 16:01:52 2022
    6. NAMESPACE: cert-manager
    7. STATUS: deployed
    8. REVISION: 1
    9. TEST SUITE: None
    10. NOTES:
    11. cert-manager v1.9.1 has been deployed successfully!
    12. In order to begin issuing certificates, you will need to set up a ClusterIssuer
    13. or Issuer resource (for example, by creating a 'letsencrypt-staging' issuer).
    14. More information on the different types of issuers and how to configure them
    15. can be found in our documentation:
    16. Issuer Configuration
    17. For information on how to configure cert-manager to automatically provision
    18. Certificates for Ingress resources, take a look at the `ingress-shim`
    19. documentation:
    20. Securing Ingress Resources

    4. 验证部署。

    1. $ kubectl get deployments --namespace cert-manager
    2. NAME READY UP-TO-DATE AVAILABLE AGE
    3. cert-manager 1/1 1 1 4m30s
    4. cert-manager-cainjector 1/1 1 1 4m30s
    5. cert-manager-webhook 1/1 1 1 4m30s

    部署 NGINX Cafe 示例

    我们将使用 NGINX Cafe 示例来提供后端部署和 services。这个示例在 NGINX 提供的文档中十分常见。在此过程中,我们不会部署 Ingress。

    1. 克隆 NGINX Ingress GitHub 项目。

    1. $ git clone https://github.com/nginxinc/kubernetes-ingress.git
    2. Cloning into 'kubernetes-ingress'...
    3. remote: Enumerating objects: 44979, done.
    4. remote: Counting objects: 100% (172/172), done.
    5. remote: Compressing objects: 100% (108/108), done.
    6. remote: Total 44979 (delta 87), reused 120 (delta 63), pack-reused 44807
    7. Receiving objects: 100% (44979/44979), 60.27 MiB | 27.33 MiB/s, done.
    8. Resolving deltas: 100% (26508/26508), done.

    2. 进入到示例目录。这个目录中包含几个示例,演示了 Ingress controller 的各种配置。我们使用的是 complete-example 目录下提供的示例。

    $ cd ./kubernetes-ingress/examples/ingress-resources/complete-example

    3. 部署 NGINX Cafe 示例。

    1. $ kubectl apply -f ./cafe.yaml
    2. deployment.apps/coffee created
    3. service/coffee-svc created
    4. deployment.apps/tea created
    5. service/tea-svc created

    4. 使用 kubectl get 命令验证部署和 services。您需要确保 Pods 显示为 READY,而 service 显示为 running。下面的示例为您提供了一个代表性样本。注意,kubernetes 服务是系统服务,与 NGINX Cafe 示例在同一个命名空间(默认)中运行。

    1. $ kubectl get deployments,services --namespace default
    2. NAME READY UP-TO-DATE AVAILABLE AGE
    3. deployment.apps/coffee 2/2 2 2 69s
    4. deployment.apps/tea 3/3 3 3 68s
    5. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
    6. service/coffee-svc ClusterIP 10.128.154.225 <none> 80/TCP 68s
    7. service/kubernetes ClusterIP 10.128.0.1 <none> 443/TCP 29m
    8. service/tea-svc ClusterIP 10.128.96.145 <none> 80/TCP 68s

    部署 ClusterIssuer

    在 cert-manager 中,ClusterIssuer 可被用来颁发证书。这是一个集群范围对象,可以被任何命名空间引用,也可以被向指定证书颁发机构发起的任何证书请求所使用。在这个示例中,任何对 Let’s Encrypt 证书的请求都可以由这个 ClusterIssuer 来处理。

    为选择的 challenge 类型部署 ClusterIssuer。虽然这不在本文的讨论范围内,但简单提一下,您可以通过高级配置选项在 ClusterIssuer 中指定多个解析器(根据选择器字段选择)。

    ACME challenge 基础知识

    可使用自动证书管理环境 (ACME) 协议来确定您是否拥有某个域名,进而确定能否被颁发 Let’s Encrypt 证书。对于这个 challenge,需要传递以下参数:

    • metadata.name:ClusterIssuer 名称,在 Kubernetes 安装中需要是唯一的。这个名称将在后面的证书颁发示例中用到。

    • spec.acme.email:这是您为生成证书而使用 Let’s Encrypt 注册的电子邮件地址。这应该是您的电子邮件。

    • spec.acme.privateKeySecretRef:这是您将用来存储私钥的 Kubernetes secret 的名称。spec.acme.solvers:这应保持不变——它指出了您所使用的 challenge 类型(或者 ACME 所说的解析程序)(HTTP-01 或 DNS-01),以及它应该应用于哪种 Ingress 类型(在这种情况下,将是 nginx)。

    使用 HTTP-01

    这个示例显示了如何设置 ClusterIssuer 以使用 HTTP-01 challenge 来证明域名所有权和接收证书。

    1. 创建 ClusterIssuer,使用 HTTP-01 challenge。

    1. $ cat << EOF | kubectl apply -f
    2. apiVersion: cert-manager.io/v1
    3. kind: ClusterIssuer
    4. metadata:
    5. name: prod-issuer
    6. spec:
    7. acme:
    8. email: example@example.com
    9. server: https://acme-v02.api.letsencrypt.org/directory
    10. privateKeySecretRef:
    11. name: prod-issuer-account-key
    12. solvers:
    13. - http01:
    14. ingress:
    15. class: nginx
    16. EOF
    17. clusterissuer.cert-manager.io/prod-issuer created

    2. 验证该 ClusterIssuer(应该显示为 Ready)。

    1. $ kubectl get clusterissuer
    2. NAME READY AGE
    3. prod-issuer True 34s

    使用 DNS-01

    这个示例显示了如何设置 ClusterIssuer 以使用 DNS-01 challenge 来验证域名所有权。根据具体的 DNS 提供商,您可能需要使用 Kubernetes Secret 来存储令牌。这个示例使用的是 Cloudflare。注意命名空间的使用。部署到 cert-manager 命名空间中的 cert-manager 应用需要访问 Secret。

    在这个示例中,您需要一个 Cloudflare API 令牌,您可以从账户中创建该令牌。这将需要放在下面的 行中。如果您使用的不是 Cloudflare,则需要遵循提供商的文档

    1. 为 API 令牌创建 Secret。

    1. $ cat << EOF | kubectl apply -f
    2. apiVersion: v1
    3. kind: Secret
    4. metadata:
    5. name: cloudflare-api-token-secret
    6. namespace: cert-manager
    7. type: Opaque
    8. stringData:
    9. api-token: <API Token>
    10. EOF

    2. 创建颁发机构,使用 DNS-01 challenge。

    1. $ cat << EOF | kubectl apply -f
    2. apiVersion: cert-manager.io/v1
    3. kind: ClusterIssuer
    4. metadata:
    5. name: prod-issuer
    6. spec:
    7. acme:
    8. email: example@example.com
    9. server: https://acme-v02.api.letsencrypt.org/directory
    10. privateKeySecretRef:
    11. name: prod-issuer-account-key
    12. solvers:
    13. - dns01:
    14. cloudflare:
    15. apiTokenSecretRef:
    16. name: cloudflare-api-token-secret
    17. key: api-token
    18. EOF

    3. 验证颁发机构(它应显示为 ready)。

    1. $ kubectl get clusterissuer
    2. NAME READY AGE
    3. prod-issuer True 31m

    部署 Ingress

    完成上述步骤后,即可为应用部署 Ingress 资源。这将把流量路由到我们先前部署的 NGINX Cafe 应用中。

    使用 Kubernetes Ingress

    如果您使用标准的 Kubernetes Ingress 资源,则要使用以下部署 YAML 来配置 Ingress 并请求证书。

    1. apiVersion: networking.k8s.io/v1
    2. kind: Ingress
    3. metadata:
    4. name: cafe-ingress
    5. annotations:
    6. cert-manager.io/cluster-issuer: prod-issuer
    7. acme.cert-manager.io/http01-edit-in-place: "true"
    8. spec:
    9. ingressClassName: nginx
    10. tls:
    11. - hosts:
    12. - cert.example.com
    13. secretName: cafe-secret
    14. rules:
    15. - host: cert.example.com
    16. http:
    17. paths:
    18. - path: /tea
    19. pathType: Prefix
    20. backend:
    21. service:
    22. name: tea-svc
    23. port:
    24. number: 80
    25. - path: /coffee
    26. pathType: Prefix
    27. backend:
    28. service:
    29. name: coffee-svc
    30. port:
    31. number: 80

    需要检查清单的一些关键部分:

    • 被调用的 API 是标准 Kubernetes Ingress。

    • 该配置的一个关键部分位于 metadata.annotations 下,在这里我们可以把 acme.cert-manager.io/http01-edit-in-place 设置为 “true”。这个值是必填项,可以调整 challenge 的解决方式。如欲了解更多信息,请参阅支持的注释文档。这也可以通过使用 master/minion setup 来处理。

    • spec.ingressClassName 是指我们已安装并将使用的 NGINX Ingress Controller。

    • spec.tls.secret Kubernetes Secret 资源存储 Let’s Encrypt 颁发证书时所返回的证书密钥。

    • 我们的主机名 cert.example.com 是为 spec.tls.hosts 和 spec.rules.host 指定的。我们的 ClusterIssuer 就为这个主机名颁发证书。

    • spec.rules.http 部分定义了路径以及满足这些路径上的请求的后端 Services。例如,到 /tea 的流量将被引导到 tea-svc 上的 80 端口。

    • 根据您的安装修改上述清单。至少需要更改 spec.rules.host 和 spec.tls.hosts 的值,但建议您审查配置中的所有参数。

    • 应用清单。

    1. $ kubectl apply -f ./cafe-virtual-server.yaml
    2. virtualserver.k8s.nginx.org/cafe created
    • 等待证书颁发,READY 字段的值会变为 “True”。

    1. $ kubectl get certificates
    2. NAME READY SECRET AGE
    3. certificate.cert-manager.io/cafe-secret True cafe-secret 37m

    使用 NGINX 虚拟服务器/虚拟路由

    如果您使用 NGINX CRD,则需要使用以下部署 YAML 来配置 Ingress。

    更多实践代码点击NGINX社区官网查看

    查看证书

    您可以通过 Kubernetes API 查看证书。这将显示有关该证书的细节,包括其大小和关联私钥。

    1. $ kubectl describe secret cafe-secret
    2. Name: cafe-secret
    3. Namespace: default
    4. Labels: <none>
    5. Annotations: cert-manager.io/alt-names: cert.example.com
    6. cert-manager.io/certificate-name: cafe-secret
    7. cert-manager.io/common-name: cert.example.com
    8. cert-manager.io/ip-sans:
    9. cert-manager.io/issuer-group:
    10. cert-manager.io/issuer-kind: ClusterIssuer
    11. cert-manager.io/issuer-name: prod-issuer
    12. cert-manager.io/uri-sans:Type: 404 Page not found
    13. ====
    14. tls.crt: 5607 bytes
    15. tls.key: 1675 bytes

    如要查看实际的证书和密钥,则可以运行以下命令。(注意:这揭露了 Kubernetes Secrets 的一个弱点。也就是说,它们可以被任何具有必要访问权限的人员读取)。

    $ kubectl get secret cafe-secret -o yaml

    测试 Ingress

    测试证书。在这里您可以使用任何想用的方法。下面的示例使用了 cURL。如果成功,则会出现一个类似于前面所示的代码,其中包括服务器名称、服务器的内部地址、日期、选择的 URI(路由)(coffee 或 tea)以及请求 ID。如果失败,则会生成 HTTP 错误代码,最可能是 400 或 301。

    1. $ curl https://cert.example.com/tea
    2. Server address: 10.2.0.6:8080
    3. Server name: tea-5c457db9-l4pvq
    4. Date: 02/Sep/2022:15:21:06 +0000
    5. URI: /tea
    6. Request ID: d736db9f696423c6212ffc70cd7ebecf
    7. $ curl https://cert.example.com/coffee
    8. Server address: 10.2.2.6:8080
    9. Server name: coffee-7c86d7d67c-kjddk
    10. Date: 02/Sep/2022:15:21:10 +0000
    11. URI: /coffee
    12. Request ID: 4ea3aa1c87d2f1d80a706dde91f31d54

    证书更新

    刚开始,我们就提到这种方法将消除管理证书更新的需要。然而,我们尚未解释如何做到这一点。为什么呢?因为这是 cert-manager 的一个核心内置部分。在这个自动化过程中,当 cert-manager 意识到证书不存在、已过期、即将在 15 天内过期时,或者当用户通过 CLI 请求新的证书时,它将自动请求新的证书。简单至极。

    常见问题

    我应该使用哪种 challenge 类型?

    这主要取决于您的用例。

    HTTP-01 challenge 方法要求80 端口对互联网开放,并且 DNS A 记录已根据 Ingress controller 的 IP 地址正确配置。除了创建 A 记录以外,这种方法不要求访问 DNS 提供商。

    当无法将 80 端口暴露在互联网上时,可以使用 DNS-01 challenge 方法,它只要求 cert-manager 对 DNS 提供商具有 egress 访问权。但这种方法要求您能够访问 DNS 提供商的 API,所需的访问级别因具体提供商而异。

    如何进行故障排除?

    由于 Kubernetes 错综复杂,因此很难提供有针对性的故障排除信息。当您遇到问题时,请在 NGINX 社区的官方微信群中向我们提问(NGINX Plus 用户可以使用他们的普通支持选项)。


    NGINX 唯一中文官方社区 ,尽在 nginx.org.cn

    更多 NGINX 相关的技术干货、互动问答、系列课程、活动资源: 开源社区官网 | 微信公众号

  • 相关阅读:
    2N2222简介及用Arduino模拟
    【Oracle】Oracle系列十九--Oracle的体系结构
    69-Java常用API:Object、Objects、StringBuilder、Math、System、BigDecimal
    前端精度问题 (id 返回的和传给后端的不一致问题)
    前端工程化之 Source Map
    四舍五入(c++基础)
    c语言基础知识+OS+数据结构
    oracle 临时表
    大数据可视化Echarts基础快速入门
    Day04 Spring和SpringBoot
  • 原文地址:https://blog.csdn.net/m0_58461993/article/details/133344090