k8s cert-manager cloudflare 配置

2025-12-26 7 0

cert-manager 是 Kubernetes 上的证书管理工具,支持基于 ACME 协议与 Let's Encrypt 签发免费证书并为证书自动续期。

cert-manager 工作原理

cert-manager 部署到 Kubernetes 集群后,它会 watch 它所支持的 CRD 资源,我们通过创建 CRD 资源来指示 cert-manager 为我们签发证书并自动续期

image.png

上图显示,cert-manager 拥有 Issuer 和 Certificate 等自定义资源,我们稍后会创建这些资源。

解释下几个关键的资源:

  • Issuer/ClusterIssuer: 用于指示 cert-manager 用什么方式签发证书,本文主要讲解签发免费证书的 ACME 方式。ClusterIssuer 与 Issuer 的唯一区别就是 Issuer 只能用来签发自己所在 namespace 下的证书,ClusterIssuer 可以签发任意 namespace 下的证书。
  • Certificate: 用于告诉 cert-manager 我们想要什么域名的证书以及签发证书所需要的一些配置,包括对 Issuer/ClusterIssuer 的引用。

免费证书签发原理

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://<YOUR_DOMAIN>/.well-known/acme-challenge/<TOKEN>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.<YOUR_DOMAIN>。 然后 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

选择哪种方式呢?条件允许的话,建议是尽量用 DNS-01 的方式,限制更少,功能更全。

安装 cert-manager

添加 cert-manager 仓库

helm repo add jetstack https://charts.jetstack.io
helm repo update

生成 values.yaml

helm show values jetstack/cert-manager > values.yaml

修改 values.yaml

installCRDs: true

prometheus:
  enabled: false

webhook:
  timeoutSeconds: 10

如果想查看生成的清单,可以使用

helm template cert-manager jetstack/cert-manager -n cert-manager -f values.yaml > cert-manager.yaml

安装 cert-manager

helm install cert-manager jetstack/cert-manager -n cert-manager --create-namespace -f values.yaml

等待

kubectl wait --for=condition=Ready pods --all -n cert-manager

# pod/cert-manager-74cb9c54dd-rs446 condition met
# pod/cert-manager-cainjector-5b99cf9569-c6bpt condition met
# pod/cert-manager-webhook-b9999597-xtfl6 condition met

配置DNS

添加 DNS 记录
whoami.sundayhk.com 下面测试使用
image.png

前往个人资料 -> API 令牌, 使用编辑区域 DNS 模版, 创建一个 token。

image.png

测试令牌是否能正常工作:

curl -X GET "https://api.cloudflare.com/client/v4/user/tokens/verify" \
     -H "Authorization: Bearer <yout api token>" \
     -H "Content-Type:application/json"

创建 Issuer

apiVersion: v1
kind: Secret
metadata:
  name: cloudflare-api-token-secret
  namespace: cert-manager
type: Opaque
stringData:
  api-token: <API Token> # 粘贴 cloudflare API Token 到这里,不需要 base64 加密。

这里创建 ClusterIssuer,Secret 需要创建在 cert-manager 所在命名空间中,如果是 Issuer,那就创建在 Issuer 所在命名空间中。

# letsencrypt-issuer.yaml
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: my-cloudflare-acc@example.com # 替换成你的 cloudflare邮箱,API Token方式认证非必需,API Keys 认证是必需
          apiTokenSecretRef:
            key: api-token
            name: cloudflare-api-token-secret # 引用上面cloudflare Secret

查看 Let't Encrypt 注册状态

kubectl describe clusterissuer letsencrypt-dns01

如下表示注册成功

root@kr-master:~# kubectl describe clusterissuer letsencrypt-dns01
...
Status:
  Acme:
    Last Private Key Hash:  MjEcqc52vL8XCp+yKXJeqKnO6yVtJVx/S4zeU4CzYFd=
    Uri:                    https://acme-v02.api.letsencrypt.org/acme/acct/2653488531
  Conditions:
    Last Transition Time:  2025-09-11T05:32:16Z
    Message:               The ACME account was registered with the ACME server
    Observed Generation:   1
    Reason:                ACMEAccountRegistered
    Status:                True
    Type:                  Ready
Events:                    <none>

创建 Certificate

编写 certificate.yaml

# certificate.yaml 
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: wildcard-sundayhk-com
  namespace: default
spec:
  dnsNames:
    - "*.sundayhk.com" # 替换成自己要签发证书的域名
  issuerRef:
    kind: ClusterIssuer
    name: letsencrypt-dns01 # 引用 ClusterIssuer,指示采用 dns01 方式进行校验
  secretName: wildcard-sundayhk-com-tls # 最终签发出来的证书会保存在这个 Secret 里面
kubectl apply -f certificate.yaml

查看证书是否签发成功

root@kr-master:~/ks# kubectl get certificate -w
NAME                    READY   SECRET                     AGE
wildcard-sundayhk-com   False   wildcard-letsencrypt-tls   13s
wildcard-sundayhk-com   False   wildcard-letsencrypt-tls   26s
wildcard-sundayhk-com   True    wildcard-letsencrypt-tls   26s
wildcard-sundayhk-com   True    wildcard-letsencrypt-tls   26s

注意:Let's Encrypt 一个星期内只为同一个域名颁发 5 次证书,sundayhk.com 和 whoami.sundayhk.com 被视为不同的域名。

如果 READY 为 False 表示失败,可以通过 describe 查看 event 来排查失败原因:

$ kubectl describe certificate wildcard-letsencrypt-tls -n prod

测试

编写 whoami.yaml

# whoami.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: whoami
  labels:
    app: containous
    name: whoami
spec:
  replicas: 2
  selector:
    matchLabels:
      app: containous
      task: whoami
  template:
    metadata:
      labels:
        app: containous
        task: whoami
    spec:
      containers:
        - name: containouswhoami
          image: containous/whoami
          resources:
          ports:
            - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: whoami
spec:
  ports:
    - name: http
      port: 80
  selector:
    app: containous
    task: whoami
  type: ClusterIP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: whoami-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - "whoami.sundayhk.com"
      secretName: wildcard-sundayhk-com-tls
  rules:
    - host: whoami.sundayhk.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: whoami
                port:
                  number: 80
kubectl apply -f whoami.yaml

image.png

https://cert-manager.io/docs/installation/helm/
https://cert-manager.io/docs/configuration/acme/dns01/cloudflare/
https://imroc.cc/kubernetes/certs/sign-free-certs-with-cert-manager

相关文章

k8s cert-manager zerossl cloudflare
Kubernetes Metrics Server v0.8.0
kubespray 镜像加速配置
kubespray 离线安装自建配置
kubespray 安装kubernetes集群
kubernetes 部署argocd 自动同步项目代码

发布评论