有效的 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" has been added to your repositories
$ helm repo update Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "nginx-stable" chart repository
Update Complete. ⎈Happy Helming!⎈
$ helm install nginx-kic nginx-stable/nginx-ingress \ --namespace nginx-ingress --set controller.enableCustomResources=true \
--create-namespace --set controller.enableCertManager=true
NAME: nginx-kic
LAST DEPLOYED: Thu Sep 1 15:58:15 2022
NAMESPACE: nginx-ingress
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
The NGINX Ingress Controller has been installed.
$ kubectl get deployments --namespace nginx-ingress NAME READY UP-TO-DATE AVAILABLE AGE
nginx-kic-nginx-ingress 1/1 1 1 23s
$ kubectl get services --namespace nginx-ingress
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-kic-nginx-ingress LoadBalancer 10.128.60.190 www.xxx.yyy.zzz 80:31526/TCP,443:32058/TCP 30s
此处的过程取决于您的 DNS 提供商。 此 DNS 名称需要从 Let's Encrypt 服务器解析,这可能需要您等待记录传播才能起作用。 有关更多信息,请参阅 SiteGround 文章DNS 传播是什么以及为什么它需要这么长时间?
一旦您可以解析所选的 FQDN,您就可以继续下一步。
$ host cert.example.com cert.example.com has address www.xxx.yyy.zzz
下一步是部署最新版本的 cert-manager。 再次,我们将使用 Helm 进行部署。
$ helm repo add jetstack https://charts.jetstack.io "jetstack" has been added to your repositories
$ helm repo update Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "nginx-stable" chart repository
...Successfully got an update from the "jetstack" chart repository
Update Complete. ⎈Happy Helming!⎈
$ helm install cert-manager jetstack/cert-manager \ --namespace cert-manager --create-namespace \
--version v1.9.1 --set installCRDs=true
NAME: cert-manager
LAST DEPLOYED: Thu Sep 1 16:01:52 2022
NAMESPACE: cert-manager
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
cert-manager v1.9.1 has been deployed successfully!
In order to begin issuing certificates, you will need to set up a ClusterIssuer
or Issuer resource (for example, by creating a 'letsencrypt-staging' issuer).
More information on the different types of issuers and how to configure them
can be found in our documentation:
https://cert-manager.io/docs/configuration/
For information on how to configure cert-manager to automatically provision
Certificates for Ingress resources, take a look at the `ingress-shim`
documentation:
https://cert-manager.io/docs/usage/ingress/
$ kubectl get deployments --namespace cert-manager NAME READY UP-TO-DATE AVAILABLE AGE
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 Cloning into 'kubernetes-ingress'...
remote: Enumerating objects: 44979, done.
remote: Counting objects: 100% (172/172), done.
remote: Compressing objects: 100% (108/108), done.
remote: Total 44979 (delta 87), reused 120 (delta 63), pack-reused 44807
Receiving objects: 100% (44979/44979), 60.27 MiB | 27.33 MiB/s, done.
Resolving deltas: 100% (26508/26508), done.
$ cd ./kubernetes-ingress/examples/ingress-resources/complete-example
$ kubectl apply -f ./cafe.yaml
deployment.apps/coffee created
service/coffee-svc created
deployment.apps/tea created
service/tea-svc created
kubectl
get 命令验证部署和服务。 您要确保 Pod 显示为READY
,并且 Services 显示为running
。 下面的示例显示了您正在寻找的代表性样本。 请注意, kubernetes
服务是在与 NGINX Cafe 示例相同的命名空间(默认)中运行的系统服务。$ kubectl get deployments,services --namespace default NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/coffee 2/2 2 2 69s
deployment.apps/tea 3/3 3 3 68s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/coffee-svc ClusterIP 10.128.154.225 <none> 80/TCP 68s
service/kubernetes ClusterIP 10.128.0.1 <none> 443/TCP 29m
service/tea-svc ClusterIP 10.128.96.145 <none> 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
kind: ClusterIssuer
metadata:
name: prod-issuer
spec:
acme:
email: example@example.com
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: prod-issuer-account-key
solvers:
- http01:
ingress:
class: nginx
EOF
clusterissuer.cert-manager.io/prod-issuer created
$ 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
kind: Secret
metadata:
name: cloudflare-api-token-secret
namespace: cert-manager
type: Opaque
stringData:
api-token: <API Token>
EOF
$ cat << EOF | kubectl apply -f apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: prod-issuer
spec:
acme:
email: example@example.com
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: prod-issuer-account-key
solvers:
- dns01:
cloudflare:
apiTokenSecretRef:
name: cloudflare-api-token-secret
key: api-token
EOF
$ kubectl get clusterissuer NAME READY AGE
prod-issuer True 31m
这就是我们一直在努力实现的目标——为我们的应用部署 Ingress 资源。 这会将流量路由到我们之前部署的 NGINX Cafe应用。
如果您使用标准 Kubernetes Ingress 资源,您将使用以下部署 YAML 来配置 Ingress 并请求证书。
apiVersion: networking.k8s.io/v1 kind: Ingress
metadata:
name: cafe-ingress
annotations:
cert-manager.io/cluster-issuer: prod-issuer
acme.cert-manager.io/http01-edit-in-place: "true"
spec:
ingressClassName: nginx
tls:
- hosts:
- cert.example.com
secretName: cafe-secret
rules:
- host: cert.example.com
http:
paths:
- path: /tea
pathType: Prefix
backend:
service:
name: tea-svc
port:
number: 80
- path: /coffee
pathType: Prefix
backend:
service:
name: coffee-svc
port:
number: 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 created
$ kubectl get certificates NAME READY SECRET AGE
certificate.cert-manager.io/cafe-secret True cafe-secret 37m
如果您使用 NGINX CRD,则需要使用以下部署 YAML 来配置您的 Ingress。
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
metadata:
name: cafe
spec:
host: cert.example.com
tls:
secret: cafe-secret
cert-manager:
cluster-issuer: prod-issuer
upstreams:
- name: tea
service: tea-svc
port: 80
- name: coffee
service: coffee-svc
port: 80
routes:
- path: /tea
action:
pass: tea
- path: /coffee
action:
pass: coffee
再次强调,值得回顾一下清单中的一些关键部分:
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 created
$ kubectl get VirtualServers NAME STATE HOST IP PORTS AGE
cafe Valid cert.example.com www.xxx.yyy.zzz [80,443] 51m
您可以通过 Kubernetes API 查看证书。这将显示有关证书的详细信息,包括其大小和关联的私钥。
$ kubectl describe secret cafe-secret Name: cafe-secret
Namespace: default
Labels: <none>
Annotations: cert-manager.io/alt-names: cert.example.com
cert-manager.io/certificate-name: cafe-secret
cert-manager.io/common-name: 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:Type: kubernetes.io/tlsData
====
tls.crt: 5607 bytes
tls.key: 1675 bytes
如果您想查看实际的证书和密钥,可以通过运行以下命令来实现。 (笔记: 这确实说明了 Kubernetes Secrets 的弱点。 也就是说,任何拥有必要访问权限的人都可以阅读它们。)
$ kubectl get secret cafe-secret -o yaml
测试证书。 您可以在此处使用任何您希望的方法。 下面的示例使用cURL 。 成功通过与之前显示的类似块来表示,其中包括服务器名称、服务器的内部地址、日期、所选的 URI(路由)(咖啡或茶)以及请求 ID。失败将采用 HTTP 错误代码的形式,最有可能是 400 或 301。
$ curl https://cert.example.com/tea
Server address: 10.2.0.6:8080
Server name: tea-5c457db9-l4pvq
Date: 02/Sep/2022:15:21:06 +0000
URI: /tea
Request ID: d736db9f696423c6212ffc70cd7ebecf
$ curl https://cert.example.com/coffee
Server address: 10.2.2.6:8080
Server name: coffee-7c86d7d67c-kjddk
Date: 02/Sep/2022:15:21:10 +0000
URI: /coffee
Request 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 内容。”