有效的 SSL/TLS 证书是现代应用环境的核心要求。 不幸的是,在部署应用时,管理证书(或证书)更新通常是事后才想到的。 证书的有效期有限, DigiCert 证书的有效期约为 13 个月, Let's Encrypt 证书的有效期为 90 天。 为了保持安全访问,这些证书需要在到期之前进行更新/重新颁发。 鉴于大多数运营团队的工作量巨大,证书更新有时会出现疏漏,导致证书接近(或更糟的是超过)到期日期时出现混乱。
没必要这样。 通过一些规划和准备,证书管理可以实现自动化和简化。 在这里,我们将研究使用三种技术的 Kubernetes 解决方案:
在这篇博客中,您将学习如何通过为您的端点提供唯一的、自动更新和更新的证书来简化证书管理。
在讨论技术细节之前,我们需要定义一些术语。 术语“TLS 证书”是指在我们的 Ingress 控制器上启用 HTTPS 连接所需的两个组件:
证书和私钥均由Let's Encrypt颁发。 有关 TLS 证书如何工作的完整说明,请参阅 DigiCert 的文章《TLS/SSL 证书如何工作》 。
在 Kubernetes 中,这两个组件都存储为Secrets 。 Kubernetes 工作负载(例如NGINX Ingress Controller和cert-manager )可以写入和读取这些 Secret,有权访问 Kubernetes 安装的用户也可以管理这些 Secret。
cert-manager项目是一个与 Kubernetes 和 OpenShift 协同工作的证书控制器。 当部署在 Kubernetes 中时,cert-manager 将自动颁发 Ingress 控制器所需的证书,并确保它们有效且最新。 此外,它还将跟踪证书的到期日期并尝试按照配置的时间间隔进行续订。 虽然它与众多公共和私人发行商合作,但我们将展示它与 Let's Encrypt 的集成。
使用 Let’s Encrypt 时,所有证书管理都会自动处理。 虽然这提供了很大的便利,但也带来了一个问题: 该服务如何确保您拥有相关的完全限定域名 (FQDN)?
该问题可以通过质询来解决,它要求您回答只有有权访问特定域的 DNS 记录的人才能提供的验证请求。 挑战有两种形式:
HTTP-01 是生成证书的最简单方法,因为它不需要直接访问 DNS 提供商。 此类挑战始终通过端口 80 (HTTP) 进行。 请注意,当使用 HTTP-01 挑战时,cert-manager 将利用 Ingress 控制器来提供挑战令牌。
Ingress 控制器是 Kubernetes 的一项专门服务,它将流量从集群外部引入,将其负载均衡到内部Pod (一组或多个容器),并管理出口流量。 此外,Ingress 控制器通过 Kubernetes API 进行控制,并将在 Pod 添加、删除或失败时监视和更新负载均衡配置。
要了解有关 Ingress 控制器的更多信息,请阅读以下博客:
在下面的示例中,我们将使用由 F5 NGINX 开发和维护的 NGINX Ingress Controller。
这些示例假设您有一个可以测试的 Kubernetes 安装,并且该安装可以分配一个外部 IP 地址(Kubernetes LoadBalancer 对象)。 此外,它假定您可以在端口 80 和端口 443 上接收流量(如果使用 HTTP-01 质询)或者仅在端口 443 上接收流量(如果使用 DNS-01 质询)。 这些示例使用 Mac OS X 进行说明,但也可以在 Linux 或 WSL 上使用。
您还需要一个可以调整 A 记录的 DNS 提供商和 FQDN。 如果您使用 HTTP-01 质询,则只需要添加 A 记录(或让别人为您添加一条)即可。 如果您使用 DNS-01 质询,则需要对受支持的 DNS 提供商或受支持的 webhook 提供商进行 API 访问。
最简单的方法是通过Helm部署。 此部署允许您同时使用 Kubernetes Ingress 和 NGINX 虚拟服务器 CRD。
$ helm repo add nginx-stable https://helm.nginx.com/stable “nginx-stable”已添加到您的存储库
$ helm repo update 请稍等,我们会从您的图表存储库中获取最新信息...
...已成功从“nginx-stable”图表存储库获取更新
更新完成。⎈祝您 Helming 愉快!⎈
$ helm install nginx-kic nginx-stable/nginx-ingress \ --namespace nginx-ingress --set controller.enableCustomResources=true \
--create-namespace --set controller.enableCertManager=true
名称:nginx-kic
最后部署: 2022 年 9 月 1 日星期四 15:58:15
命名空间:nginx-ingress
状态:已部署
修订: 1
测试套件: 无
备注:
NGINX Ingress Controller 已安装。
$ kubectl get deploys --namespace nginx-ingress 名称 就绪 最新 可用 年龄
nginx-kic-nginx-ingress 1/1 1 1 23s
$ kubectl get services --namespace nginx-ingress 名称 类型 集群 IP 外部 IP 端口 年龄
nginx-kic-nginx-ingress 负载均衡器10.128.60.190 www.xxx.yyy.zzz 80:31526/TCP,443:32058/TCP 30 秒
此处的过程取决于您的 DNS 提供商。 此 DNS 名称需要从 Let's Encrypt 服务器解析,这可能需要您等待记录传播才能起作用。 有关更多信息,请参阅 SiteGround 文章DNS 传播是什么以及为什么它需要这么长时间?
一旦您可以解析所选的 FQDN,您就可以继续下一步。
$ host cert.example.com cert.example.com 有地址 www.xxx.yyy.zzz
下一步是部署最新版本的 cert-manager。 再次,我们将使用 Helm 进行部署。
$ helm repo add jetstack https://charts.jetstack.io “jetstack” 已添加到您的存储库
$ helm repo update 请稍等,我们会从您的图表存储库中获取最新信息...
...已成功从“nginx-stable”图表存储库获取更新
...已成功从“jetstack”图表存储库获取更新
更新完成。⎈祝您掌舵愉快!⎈
$ helm install cert-manager jetstack/cert-manager \ --namespace cert-manager --create-namespace \ --version v1.9.1 --set installCRDs=true 名称:cert-manager 最后部署: 2022 年 9 月 1 日星期四 16:01:52 命名空间:cert-manager 状态:已部署 修订: 1 测试套件: 无 注意:cert-manager v1.9.1 已成功部署!
为了开始颁发证书,您需要设置 ClusterIssuer 或 Issuer 资源(例如,通过创建“letsencrypt-staging”颁发者)。
有关不同类型的发行人及其配置方法的更多信息,请参阅我们的文档:
https://cert-manager.io/docs/configuration/
有关如何配置 cert-manager 以自动为 Ingress 资源提供证书的信息,请查看“ingress-shim”文档:
https://cert-manager.io/docs/usage/ingress/
$ kubectl get deploys --namespace cert-manager 名称 已就绪 最新 可用 年龄
cert-manager 1/1 1 1 4m30s
cert-manager-cainjector 1/1 1 1 4m30s
cert-manager-webhook 1/1 1 1 4m30s
我们将使用 NGINX Cafe 示例来提供我们的后端部署和服务。 这是 NGINX 提供的文档中使用的常见示例。我们不会在此过程中部署 Ingress。
$ git clone https://github.com/nginxinc/kubernetes-ingress.git 克隆到“kubernetes-ingress”...
远程: 枚举对象: 44979,完成。
远程: 计数对象: 100% (172/172),完成。
远程: 压缩对象: 100% (108/108),完成。
远程: 总计 44979(增量 87),重用 120(增量 63),打包重用 44807
接收对象: 100% (44979/44979),60.27 MiB | 27.33 MiB/s,完成。
解决差异: 100%(26508/26508),完成。
$ cd ./kubernetes-ingress/examples/ingress-resources/complete-example
$ kubectl apply -f ./cafe.yaml
deployment.apps/coffee 创建
service/coffee-svc 创建
deployment.apps/tea 创建
service/tea-svc 创建
kubectl
get 命令验证部署和服务。 您要确保 Pod 显示为READY
,并且 Services 显示为running
。 下面的示例显示了您正在寻找的代表性样本。 请注意, kubernetes
服务是在与 NGINX Cafe 示例相同的命名空间(默认)中运行的系统服务。$ kubectl get deploys,services --namespace default NAME READY UP-TO-DATE AVAILABLE AGE
deploy.apps/coffee 2/2 2 2 69s
deploy.apps/tea 3/3 3 3 68s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/coffee-svc ClusterIP 10.128.154.225 <无> 80/TCP 68s
服务/kubernetes ClusterIP 10.128.0.1 <无> 443/TCP 29m
服务/tea-svc ClusterIP 10.128.96.145 <无> 80/TCP 68s
在 cert-manager 中,可以使用ClusterIssuer来颁发证书。 这是一个集群范围的对象,可以被任何命名空间引用,并可以被任何具有定义的证书颁发机构的证书请求使用。 在此示例中,任何针对 Let's Encrypt 证书的证书请求都可以由此 ClusterIssuer 处理。
为您选择的挑战类型部署 ClusterIssuer。 虽然这超出了本文的范围,但有一些高级配置选项允许您在 ClusterIssuer 中指定多个解析器(根据选择器字段选择)。
自动证书管理环境 (ACME) 协议用于确定您是否拥有域名,因此可以颁发 Let’s Encrypt 证书。 对于此挑战,需要传递以下参数:
此示例显示如何设置 ClusterIssuer 以使用 HTTP-01 质询来证明域所有权并接收证书。
$ cat << EOF | kubectl apply -f apiVersion: cert-manager.io/v1
种类: ClusterIssuer
元数据:
名称:prod-issuer
规范:
acme:
电子邮件:example@example.com
服务器:https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
名称:prod-issuer-account-key
求解器:
- http01:
入口:
类:nginx
EOF
clusterissuer.cert-manager.io/prod-issuer 创建
$ kubectl get clusterissuer NAME READY AGE
prod-issuer True 34s
此示例显示如何设置 ClusterIssuer 以使用 DNS-01 质询来验证您的域所有权。 根据您的 DNS 提供商,您可能需要使用 Kubernetes Secret 来存储您的令牌。 此示例使用Cloudflare 。 注意命名空间的使用。 部署到 cert-manager 命名空间的 cert-manager应用需要有权访问 Secret 。
对于此示例,您将需要一个Cloudflare API 令牌,您可以从您的帐户中创建它。 这需要放在下面的 行中。 如果您没有使用 Cloudflare,则需要遵循提供商的文档。
$ cat << EOF | kubectl apply -f apiVersion: v1
种类: 机密
元数据:
名称:cloudflare-api-token-secret
命名空间:cert-manager
类型: 不透明
stringData:
api-token: <API Token>
EOF
$ cat << EOF | kubectl apply -f apiVersion: cert-manager.io/v1
种类: ClusterIssuer
元数据:
名称:prod-issuer
规格:
acme:
电子邮件:example@example.com
服务器:https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
名称:prod-issuer-account-key
求解器:
- dns01:
cloudflare:
apiTokenSecretRef:
名称:cloudflare-api-token-secret
密钥:api-token
EOF
$ kubectl 获取 clusterissuer 名称 READY AGE
prod-issuer True 31 分钟
这就是我们一直在努力实现的目标——为我们的应用部署 Ingress 资源。 这会将流量路由到我们之前部署的 NGINX Cafe应用。
如果您使用标准 Kubernetes Ingress 资源,您将使用以下部署 YAML 来配置 Ingress 并请求证书。
api版本:networking.k8s.io/v1 种类: 入口
元数据:
名称:cafe-ingress
注释:
cert-manager.io/cluster-issuer:prod-issuer
acme.cert-manager.io/http01-edit-in-place:“true”
规格:
ingressClassName:nginx
tls:
- 主机:
- cert.example.com
secretName:cafe-secret
规则:
- 主机:cert.example.com
http:
路径:
- 路径:/tea
路径类型: 前缀
后端:
服务:
名称:tea-svc
端口:
号码: 80
- 路径:/咖啡
路径类型: 前缀
后端:
服务:
名称:coffee-svc
端口:
号码: 80
值得回顾一下清单中的一些关键部分:
metadata.annotations
下,我们将acme.cert-manager.io/http01-edit-in-place
设置为“true”。 此值是必需的,用于调整挑战的服务方式。 有关更多信息,请参阅支持的注释文档。 这也可以通过使用master/minion 设置来处理。spec.ingressClassName
指的是我们已经安装并将要使用的 NGINX Ingress 控制器。spec.tls.secret
Kubernetes Secret 资源存储 Let's Encrypt 颁发证书时返回的证书密钥。 spec.tls.hosts
和spec.rules.host
指定了主机名cert.example.com
。 这是我们的 ClusterIssuer 颁发证书的主机名。spec.rules.http
部分定义路径和将在这些路径上提供服务请求的后端服务。 例如,到/tea 的
流量将被定向到tea-svc
上的端口 80。spec.rules.host
和spec.tls.hosts
值,但您应该检查配置中的所有参数。 $ kubectl apply -f ./cafe-virtual-server.yaml virtualserver.k8s.nginx.org/cafe 创建
$ kubectl 获取证书 名称 READY SECRET AGE
certificate.cert-manager.io/cafe-secret True cafe-secret 37m
如果您使用 NGINX CRD,则需要使用以下部署 YAML 来配置您的 Ingress。
api版本: k8s.nginx.org/v1
种类: 虚拟服务器
元数据:
名称:cafe
规格:
主机:cert.example.com
tls:
秘密:cafe-secret
证书管理器:
集群发行者:prod-issuer
上游:
- 名称:tea
服务:tea-svc
端口: 80
- 名称:coffee
服务:coffee-svc
端口: 80
路线:
- 路径:/茶
动作:
通行证:茶
- 路径:/咖啡
动作:
通行证:咖啡
再次强调,值得回顾一下清单中的一些关键部分:
spec.tls.secret
Kubernetes Secret 资源存储 Let's Encrypt 颁发证书时返回的证书密钥。 spec.host
指定了主机名cert.example.com
。 这是我们的 ClusterIssuer 颁发证书的主机名。spec.upstreams
值指向我们的后端服务,包括端口。spec.routes
定义了路线以及命中这些路线时要采取的操作。spec.host
值,但您应该检查配置中的所有参数。 $ kubectl apply -f ./cafe-virtual-server.yaml virtualserver.k8s.nginx.org/cafe 创建
$ kubectl 获取虚拟服务器名称 状态 主机 IP 端口 年龄 咖啡馆 有效 cert.example.com www.xxx.yyy.zzz [80,443] 51m
您可以通过 Kubernetes API 查看证书。这将显示有关证书的详细信息,包括其大小和关联的私钥。
$ kubectl describe secret cafe-secret 名称: cafe-secret
命名空间: default
标签: <none>
注释: cert-manager.io/alt-names: cert.example.com
cert-manager.io/证书名称:咖啡馆的秘密
cert-manager.io/通用名称: cert.example.com
cert-manager.io/ip-sans:
cert-manager.io/issuer-group:
cert-manager.io/issuer-kind: ClusterIssuer cert-manager.io/issuer-name:prod-issuer cert-manager.io/uri-sans :类型: kubernetes.io/tls数据 ==== tls.crt: 5607 字节 tls.key: 1675 字节
如果您想查看实际的证书和密钥,可以通过运行以下命令来实现。 (笔记: 这确实说明了 Kubernetes Secrets 的弱点。 也就是说,任何拥有必要访问权限的人都可以阅读它们。)
$ kubectl 获取秘密 cafe-secret -o yaml
测试证书。 您可以在此处使用任何您希望的方法。 下面的示例使用cURL 。 成功通过与之前显示的类似块来表示,其中包括服务器名称、服务器的内部地址、日期、所选的 URI(路由)(咖啡或茶)以及请求 ID。失败将采用 HTTP 错误代码的形式,最有可能是 400 或 301。
$ curl https://cert.example.com/tea
服务器地址: 10.2.0.6:8080
服务器名称:tea-5c457db9-l4pvq
日期: 2022 年 9 月 2 日:15:21:06 +0000
URI:/tea
请求 ID:d736db9f696423c6212ffc70cd7ebecf
$ curl https://cert.example.com/coffee
服务器地址: 10.2.2.6:8080
服务器名称:coffee-7c86d7d67c-kjddk
日期: 2022 年 9 月 2 日:15:21:10 +0000
URI:/coffee
请求 ID: 4ea3aa1c87d2f1d80a706dde91f31d54
一开始,我们承诺这种方法将消除管理证书更新的需要。 然而,我们还没有解释如何做到这一点。 为什么? 因为这是 cert-manager 的核心内置部分。 在这个自动过程中,当 cert-manager 发现证书不存在、已过期、距离过期还有 15 天,或者用户通过 CLI 请求新的证书时,就会自动请求新的证书。 没有比这更容易的事情了。
如果您是 NGINX Plus 订阅者,那么唯一的区别就是安装 NGINX Ingress Controller。 请参阅 NGINX 文档的安装 Helm部分,了解如何修改上面给出的 Helm 命令来实现此目的的说明。
这很大程度上取决于您的使用情况。
HTTP-01 质询方法要求端口 80 对 Internet 开放,并且已为 Ingress 控制器的 IP 地址正确配置 DNS A 记录。 除了创建 A 记录之外,此方法不需要访问 DNS 提供商。
当您无法将端口 80 暴露给 Internet 时,可以使用 DNS-01 质询方法,并且仅要求 cert-manager 具有对 DNS 提供商的出口访问权限。 但是,此方法确实要求您能够访问 DNS 提供商的 API,尽管所需的访问级别因具体提供商而异。
由于 Kubernetes 非常复杂,因此很难提供有针对性的故障排除信息。 如果您确实遇到问题,我们诚邀您在NGINX Community Slack上向我们提问(NGINX Plus 订阅者可以使用他们的正常支持选项)。
首先申请 NGINX Ingress Controller 与 NGINX App Protect WAF 和 DoS 的30 天免费试用版,然后下载始终免费的 NGINX Service Mesh。
“这篇博文可能引用了不再可用和/或不再支持的产品。 有关 F5 NGINX 产品和解决方案的最新信息,请探索我们的NGINX 产品系列。 NGINX 现在是 F5 的一部分。 所有之前的 NGINX.com 链接都将重定向至 F5.com 上的类似 NGINX 内容。”