cert-manager 是一个云原生证书管理开源项目,用于在 Kubernetes 集群中自动管理和颁发来自各种颁发源的 TLS 证书,它可以从各种受支持的来源颁发证书,包括 Let’s Encrypt、HashiCorp Vault和Venafi以及私有 PKI,它将确保证书定期有效和更新,并在到期前的适当时间尝试更新证书。
架构
![](/2024/067bd705c5/image-20250122154920890.png)
Issuers/ClusterIssuers:定义使用 什么证书颁发机构 (CA) 来去颁发证书,Issuers和ClusterIssuers区别是issuers是一个名称空间级别的资源, 只能用来签发自己所在 namespace 下的证书,ClusterIssuer是个集群级别的资源 可以签发任意 namespace 下的证书
Certificate:定义所需的 X.509 证书,该证书将更新并保持最新。Certificate是一个命名空间资源,当Certificate被创建时,它会去创建相应的CertificateRequest资源来去申请证书。
安装
安装cert-manager相对比较简单,这里安装的cert-manager版本为v1.4,注意该版本要求kubectl版本>= v1.19.0-rc.1
所有资源(CustomResourceDefinitions和 cert-manager、cainjector 和 webhook 组件)都包含在一个 YAML 清单文件中:
1 | wget https://github.com/jetstack/cert-manager/releases/download/v1.4.0/cert-manager.yaml |
默认情况下,cert-manager 将安装到cert-manager
命名空间中,我们可以使用如下命令验证安装
1 | root@i-tsfhx8p6:~/qke-k8s/cert-manager# kubectl get pod -n cert-manager |
配置Issuer/ClusterIssuer
cert-manager支持以下几种证书颁发者
- SelfSigned
- CA
- Vault
- Venafi
- External
- ACME
这里只介绍使用ACME作为证书颁发者的方式。
HTTP-01 校验原理
HTTP-01 的校验原理是给你域名指向的 HTTP 服务增加一个临时 location ,Let’s Encrypt 会发送 http 请求到 http:///.well-known/acme-challenge/,YOUR_DOMAIN 就是被校验的域名,TOKEN 是 ACME 协议的客户端负责放置的文件,在这里 ACME 客户端就是 cert-manager,它通过修改或创建 Ingress 规则来增加这个临时校验路径并指向提供 TOKEN 的服务。Let’s Encrypt 会对比 TOKEN 是否符合预期,校验成功后就会颁发证书。此方法仅适用于给使用 Ingress 暴露流量的服务颁发证书,并且不支持泛域名证书。
DNS-01 校验原理
DNS-01 的校验原理是利用 DNS 提供商的 API Key 拿到你的 DNS 控制权限, 在 Let’s Encrypt 为 ACME 客户端提供令牌后,ACME 客户端 (cert-manager) 将创建从该令牌和您的帐户密钥派生的 TXT 记录,并将该记录放在 _acme-challenge.。 然后 Let’s Encrypt 将向 DNS 系统查询该记录,如果找到匹配项,就可以颁发证书。此方法不需要你的服务使用 Ingress,并且支持泛域名证书。
校验方式对比
HTTP-01 的校验方式的优点是: 配置简单通用,不管使用哪个 DNS 提供商都可以使用相同的配置方法;缺点是:需要依赖 Ingress,如果你的服务不是用 Ingress 暴露流量的就不适用,而且不支持泛域名证书。
DNS-01 的校验方式的优点是没有 HTTP-01 校验方式缺点,不依赖 Ingress,也支持泛域名;缺点就是不同 DNS 提供商的配置方式不一样,而且 DNS 提供商有很多,cert-manager 的 Issuer 不可能每个都去支持,支持如下的dns提供商:
Akamai
AzureDNS
CloudFlare
Google
Route53
DigitalOcean
RFC2136
Cert-manager 还支持使用外部 webhook 的接入 DNS 提供商,支持如下webhook
cert-manager-webhook-oci
(Oracle 云基础设施)
HTTP01配置示例
1 | apiVersion: cert-manager.io/v1 |
说明:
- metadata.name: 是我们创建的签发机构的名称,后面我们创建证书的时候会引用它
- spec.acme.email: 是你自己的邮箱,证书快过期的时候会有邮件提醒,不过 cert-manager 会利用 acme 协议自动给我们重新颁发证书来续期
- spec.acme.server: 是 acme 协议的服务端,我们这里用 Let’s Encrypt,这个地址就写死成这样就行
- spec.acme.privateKeySecretRef: 指示此签发机构的私钥将要存储到哪个 Secret 对象中,名称不重要
- spec.acme.solvers: 这里指示签发机构校验方式,有http01和dns01两种,该字段下配置的class和name只能同时存在一个,class指定使用的ingress class 名称,name比较少用,通常用于云上自带的ingress
我们部署上述示例,并部署一个ingress 代理集群内的grafana服务验证签发证书
1 | root@i-tsfhx8p6:~/qke-k8s/cert-manager# kubectl apply -f clusterissuer.yaml |
我们可以查看生成的certificate和签发的证书(安全原因删除了部分证书中的字符)
1 | root@i-tsfhx8p6:~ cert-manager |
此时我们访问我们ingress文件里定义的域名可以看到此时证书已经签发
![](/2024/067bd705c5/image-20250122155625590.png)
DNS01配置示例
这里使用的dns服务商为阿里云,需要使用AliDNS-Webhook
安装alidns-webhook
1 | wget https://raw.githubusercontent.com/pragkent/alidns-webhook/master/deploy/bundle.yaml |
建议修改文件中的acme.yourcompany.com
1 | sed -i s/'acme.yourcompany.com'/'acme.lishuai.fun'/g bundle.yaml |
安装alidns-webhook
1 | kubectl apply -f bundle.yaml |
创建一个包含阿里dns凭据的secert
1 | apiVersion : v1 |
或者使用如下命令行方式创建
1 | kubectl -n cert-manager create secret generic alidns-secret --from-literal=access-key='YOUR_ACCESS_KEY' --from-literal=secret-key='YOUR_SECRET_KEY' |
编写ClusterIssuer yaml文件
1 | apiVersion: cert-manager.io/v1 |
创建ClusterIssuer
1 | kubectl apply -f ClusterIssuer.yaml |
我们创建一个 手动去申请证书
1 | root@i-tsfhx8p6:~/qke-k8s/cert-manager/alidns-webhook# cat certificate.yaml |
刚创建后我们查看certificate会发现ready状态为false,此时稍等会自动在我们的dns解析里创建txt 记录,然后去签发证书,该字段就会变为true
![](/2024/067bd705c5/image-20250122160225810.png)
此时查看发现secert和certificate都发生变化
1 | root@i-tsfhx8p6:~/qke-k8s/cert-manager/alidns-webhook# kubectl get certificate |
查看证书详情
1 | root@i-tsfhx8p6:~/qke-k8s/cert-manager/ingress# kubectl get secrets lishuai-fun-tls -o json |jq --raw-output '.data["tls.crt"]'|base64 -d #从secert中解析证书 |
我们也可以使用一些网站提供的在线证书查看
![](/2024/067bd705c5/image-20250122160310979.png)
我们可以看到这个证书是个泛域名证书,此时我们ingress 可以直接使用该secert,不需要再去添加注解执行使用的issuer/cluseteissuer了,例如
1 | root@i-tsfhx8p6:~/qke-k8s/cert-manager/ingress# cat alertmanager-ingress.yaml |
![](/2024/067bd705c5/image-20250122160335156.png)
创建Certificate
这里拿我们上面创建的那个Certificate来讲解
1 | apiVersion: cert-manager.io/v1 |
- spec.secretName 指示证书最终存到哪个 Secret 中
- spec.issuerRef.kind 值为 ClusterIssuer 说明签发机构不在本 namespace 下,而是在全局
- spec.issuerRef.name 我们创建的签发机构的名称 (ClusterIssuer.metadata.name)
- spec.dnsNames 指示该证书的可以用于哪些域名
下面是一个官方的包含完整字段的Certificate
1 | apiVersion: cert-manager.io/v1 |