Kubernetes — SSL 证书自动更新

Kubernetes — SSL 证书自动更新

介绍

提供一个在 Kubernetes 中使用 cert-manager + Cloudflare 自动签发并自动更新 Let’s Encrypt 证书的完整思路与示例(DNS-01 验证),方便你在集群内自动化 TLS 证书更新。

前置条件

  • Kubernetes 集群:可正常访问外网。不做网络环境配置的教程,具体可以去看其他文章
  • Cloudflare 账号:已将你的域名托管到 Cloudflare。使用 Cloudflare 做 dns-01 挑战
  • kubectl:已连接到集群。最基本的条件,保证k8s能正常访问
  • helm:推荐用 Helm 安装 cert-manager。使用helm安装,方便干净

安装

官方推荐用 Helm,这里我使用 1.18.2 的版本,在我这个时间点这个版本还是比较新的

安装 cert-manager

1# 安装 cert-manager CRDs
2kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.18.2/cert-manager.crds.yaml
1## Add the Jetstack Helm repository
2helm repo add jetstack https://charts.jetstack.io --force-update
1## Install the cert-manager helm chart
2helm install cert-manager --namespace cert-manager --version v1.18.2 jetstack/cert-manager

验证:

1kubectl get pods -n cert-manager

准备 Cloudflare API Token

  1. 登陆 Cloudflare Dashboard
  • My Profile → API Tokens → Create Token。
  1. 模板选:Edit zone DNS

  2. 权限:

  • Zone → DNS → Edit
  • Zone → Zone → Read
  1. Zone Resources: 选择需要签发证书的域名。

  2. 保存 token,例如:CF_API_TOKEN=xxxxxxxxxx

创建 Secret 存放 Token

1kubectl create secret generic cloudflare-api-token-secret \
2  --from-literal=api-token=CF_API_TOKEN \
3  -n cert-manager

配置 ClusterIssuer

cluster-issuer.yaml

 1apiVersion: cert-manager.io/v1
 2kind: ClusterIssuer
 3metadata:
 4  name: letsencrypt-dns
 5spec:
 6  acme:
 7    # 生产环境地址
 8    server: https://acme-v02.api.letsencrypt.org/directory
 9    email: your-email@example.com        # 接收过期提醒
10    privateKeySecretRef:
11      name: letsencrypt-dns-key
12    solvers:
13    - dns01:
14        cloudflare:
15          email: your-email@example.com  # 或留空(若使用 API token 可不填 email)
16          apiTokenSecretRef:
17            name: cloudflare-api-token-secret
18            key: api-token

部署yaml

1kubectl apply -f cluster-issuer.yaml

申请证书

在需要证书的命名空间下创建 Certificate 对象,例如 Nginx Ingress 的域名 example.com
certificate.yaml

 1apiVersion: cert-manager.io/v1
 2kind: Certificate
 3metadata:
 4  name: example-com
 5  namespace: default
 6spec:
 7  secretName: example-com-tls        # 生成的 secret 名称
 8  issuerRef:
 9    name: letsencrypt-dns
10    kind: ClusterIssuer
11  commonName: example.com
12  dnsNames:
13  - example.com
14  - "*.example.com"                   # 可选:通配符

部署yaml

1kubectl apply -f certificate.yaml

在 Ingress 中引用

 1apiVersion: networking.k8s.io/v1
 2kind: Ingress
 3metadata:
 4  name: example-ingress
 5  annotations:
 6    kubernetes.io/ingress.class: nginx
 7    cert-manager.io/cluster-issuer: letsencrypt-dns # 配置证书自动生成
 8spec:
 9  tls:
10  - hosts:
11    - example.com
12    secretName: example-com-tls #修改成自己要使用的tls名称会自动生成
13  rules:
14  - host: example.com
15    http:
16      paths:
17      - path: /
18        pathType: Prefix
19        backend:
20          service:
21            name: web
22            port:
23              number: 80

cert-manager 会在证书到期前约 30 天 自动续期。

查看状态

1kubectl describe certificate example-com -n default

复用证书

因为证书只能部署在制定的 namespace下,因此我写了一个脚本,把default命名空间下的 example-com-tls Secret复制到 argocdlonghorn-systemcattle-system 等多个命名空间。

 1#!/bin/bash
 2# 文件名:copy-example-cert.sh
 3# 作用:将 default 命名空间下的 example-com-tls Secret
 4#       复制到 argocd、longhorn-system、cattle-system
 5
 6SRC_NS="default"
 7SECRET_NAME="example-com-tls"
 8TARGET_NAMESPACES=("argocd" "longhorn-system" "cattle-system")
 9
10for ns in "${TARGET_NAMESPACES[@]}"; do
11  echo ">>> 正在复制到命名空间: $ns"
12  kubectl get secret "$SECRET_NAME" -n "$SRC_NS" -o yaml \
13    | sed "s/namespace: $SRC_NS/namespace: $ns/" \
14    | kubectl apply -f -
15done
16
17echo "✅ 复制完成"

执行

1./copy-example-cert.sh

脚本会依次输出

1>>> 正在复制到命名空间: argocd
2secret/example-com-tls created
3>>> 正在复制到命名空间: longhorn-system
4secret/example-com-tls created
5>>> 正在复制到命名空间: cattle-system
6secret/example-com-tls created
7✅ 复制完成