1、介绍
证书管理器
cert-manager 将证书和证书颁发者作为资源类型添加到 Kubernetes 集群中,并简化了获取、更新和使用这些证书的过程。
它可以从各种受支持的来源颁发证书,包括 Let’s Encrypt、HashiCorp Vault和Venafi以及私有 PKI。
它将确保证书有效且是最新的,并在到期前的配置时间尝试更新证书。
它松散地基于 kube-lego的工作,并从其他类似项目(如 kube-cert-manager )中借鉴了一些智慧。
2、架构图
解释下几个关键的资源:
- Issuer/ClusterIssuer: 用于指示 cert-manager 用什么方式签发证书,ClusterIssuer 与 Issuer 的唯一区别就是 Issuer 只能用来签发自己所在 namespace 下的证书,ClusterIssuer 可以签发任意 namespace 下的证书。
- Certificate: 用于告诉 cert-manager 我们想要什么域名的证书以及签发证书所需要的一些配置,包括对 Issuer/ClusterIssuer 的引用。
3、证书签发原理
免费证书签发原理
Let’s Encrypt 利用 ACME 协议来校验域名是否真的属于你,校验成功后就可以自动颁发免费证书,证书有效期只有 90 天,在到期前需要再校验一次来实现续期,幸运的是 cert-manager 可以自动续期,这样就可以使用永久免费的证书了。如何校验这个域名是否属于你呢?主流的两种校验方式是 HTTP-01 和 DNS-01,详细校验原理可参考 Let’s Encrypt 的运作方式,下面将简单描述下。
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 不可能每个都去支持,不过有一些可以通过部署实现了 cert-manager 的 Webhook 的服务来扩展 Issuer 进行支持,比如 DNSPod 和 阿里 DNS,详细 Webhook 列表请参考: https://cert-manager.io/docs/configuration/acme/dns01/#webhook
4、部署
helm repo add jetstack https://charts.jetstack.io
helm repo update jetstack
helm install \
cert-manager jetstack/cert-manager \
--namespace cert-manager \
--create-namespace \
--version v1.9.1 \
--set installCRDs=true
配置 CloudFlare DNS
DNS-01 校验方式签发证书
如果使用 DNS-01 的校验方式,就需要看你使用的哪个 DNS 提供商了,cert-manager 内置了一些 DNS 提供商的支持,详细列表和用法请参考 Supported DNS01 providers,不过 cert-manager 不可能去支持所有的 DNS 提供商,如果没有你所使用的 DNS 提供商怎么办呢?有两种方案:
- 方案一:设置 Custom Nameserver。在你的 DNS 提供商后台设置 custom nameserver,指向像 cloudflare 这种可以管理其它 DNS 提供商域名的 nameserver 地址,具体地址可登录 cloudflare 后台查看:
- 方案二:使用 Webhook。使用 cert-manager 的 Webhook 来扩展 cert-manager 的 DNS-01 验证所支持的 DNS 提供商,已经有许多第三方实现,包括国内常用的 DNSPod 与阿里 DNS,详细列表参考: Webhook。
下面以 cloudflare 为例来签发证书:
登录 cloudflare,点到 My Profile > API Tokens > Create Token 来创建 Token:
将 Token 保存到 Secret 中:
apiVersion: v1
kind: Secret
metadata:
name: cloudflare-api-token-secret
namespace: cert-manager
type: Opaque
stringData:
api-token: <API Token> # 粘贴 Token 到这里,不需要 base64 加密。
说明:如果是要创建 ClusterIssuer,Secret 需要创建在 cert-manager 所在命名空间中,如果是 Issuer,那就创建在 Issuer 所在命名空间中。
创建 ClusterIssuer:
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-dns01
spec:
acme:
privateKeySecretRef:
name: letsencrypt-dns01
server: https://acme-v02.api.letsencrypt.org/directory
solvers:
- dns01:
cloudflare:
email: 990052792@qq.com # 替换成你的 cloudflare 邮箱账号,API Token 方式认证非必需,API Keys 认证是必需
apiTokenSecretRef:
key: api-token
name: cloudflare-api-token-secret # 引用保存 cloudflare 认证信息的 Secret
创建 Certificate:
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: bgxwz-com-tls
namespace: default
spec:
dnsNames:
- bgxwz.com # 要签发证书的域名
- "*.bgxwz.com"
issuerRef:
kind: ClusterIssuer
name: letsencrypt-dns01 # 引用 ClusterIssuer,指示采用 dns01 方式进行校验
secretName: bgxwz-com-tls # 最终签发出来的证书会保存在这个 Secret 里面
获取和使用证书
创建好 Certificate 后,等一小会儿,我们可以 kubectl 查看是否签发成功:
kubectl get certificate
NAME READY SECRET AGE
bgxwz-com-tls True bgxwz-com-tls 34s
如果 READY 为 False 表示失败,可以通过 describe 查看 event 来排查失败原因:
kubectl describe certificate bgxwz-com-tls
如果为 True 表示签发成功,证书就保存在我们所指定的 Secret 中 (上面的例子是 default/bgxwz-com-tls),可以通过 kubectl 查看:
kubectl get secret bgxwz-com-tls
...
data:
tls.crt: <cert>
tls.key: <private key>
其中 tls.crt 就是证书,tls.key 是密钥。
你可以将它们挂载到你需要证书的应用中,或者使用自建的 Ingress,可以直接在 Ingress 中引用 secret,示例:
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
name: nginx-ingress
namespace: default
spec:
entryPoints:
- websecure
routes:
- kind: Rule
match: Host(`nginx.bgxwz.com`)
services:
- name: nginx
port: 80
tls:
secretName: bgxwz-com-tls
测试HTTPS证书
5、证书复制
在大多数情况下,我们申请的证书都是需要在多namespace中使用的,所以需要使用到一个证书同步工具,这里使用kubed。
安装kubed
helm repo add appscode https://charts.appscode.com/stable/
helm install kubed appscode/kubed -n cert-manager
修改 Certificated 文件
为 certificated 对象设置 secretTemplate, 设置需要同步到哪些 namespace
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: bgxwz-com-tls
namespace: default
spec:
dnsNames:
- bgxwz.com # 要签发证书的域名
- "*.bgxwz.com"
issuerRef:
kind: ClusterIssuer
name: letsencrypt-dns01 # 引用 ClusterIssuer,指示采用 dns01 方式进行校验
secretName: bgxwz-com-tls # 最终签发出来的证书会保存在这个 Secret 里面
secretTemplate:
annotations:
kubed.appscode.com/sync: "cert-manager-tls=bgxwz-com-tls"
给需要同步的目标 namespace 打 label,这里使用test命名空间作为测试
kubectl create ns test
kubectl label ns default cert-manager-tls=bgxwz-com-tls
查看是否复制成功
kubectl get secret -n test
NAME TYPE DATA AGE
bgxwz-com-tls kubernetes.io/tls 2 2m
评论区