J.V.'s Blog

k8s使用cert-manager签发免费SSL证书

介绍如何在Kubernetes集群中使用cert-manager自动签发和管理免费的SSL证书

概述

在Kubernetes环境中部署应用时,为服务配置HTTPS加密访问是一个常见需求。手动管理SSL证书不仅繁琐,而且容易出错和过期。cert-manager是一个Kubernetes原生证书管理工具,可以自动化证书的申请、签发和续期过程,特别是与Let's Encrypt等免费证书颁发机构(CA)集成,为Kubernetes服务提供免费的SSL证书。

本文将介绍cert-manager的基本原理、安装配置方法,以及如何使用它来自动签发和管理SSL证书。

cert-manager介绍

cert-manager是一个开源的Kubernetes证书管理控制器,它可以在Kubernetes集群中自动化证书的颁发和管理过程。主要特性包括:

原理

cert-manager的工作原理基于Kubernetes的控制器模式和自定义资源定义(CRD):

核心组件

  1. 控制器(Controller):监听Certificate资源的变化,处理证书申请和续期
  2. 自定义资源定义(CRD)
    • Certificate:定义需要申请的证书
    • Issuer:命名空间级别的证书颁发者配置
    • ClusterIssuer:集群级别的证书颁发者配置
  3. Webhook:验证和转换自定义资源

工作流程

  1. 用户创建Certificate资源,定义所需证书的域名、有效期等信息
  2. cert-manager控制器检测到新的Certificate资源
  3. 根据Certificate中引用的Issuer/ClusterIssuer配置,选择合适的证书颁发机构
  4. 使用ACME协议向CA申请证书
  5. 完成域名所有权验证(HTTP-01或DNS-01)
  6. 获取证书并存储在Kubernetes Secret中
  7. 将证书应用到Ingress或其他资源
  8. 定期检查证书有效期,自动续期即将过期的证书

ACME验证方式

操作步骤

1. 安装cert-manager

helm install \
  cert-manager oci://quay.io/jetstack/charts/cert-manager \
  --version v1.19.0 \
  --namespace cert-manager \
  --create-namespace \
  --set crds.enabled=true \
  --set prometheus.enabled=false

2. 配置证书颁发者(ClusterIssuer)

以下配置说明:

对于需要通配符证书或内网服务的场景,可以使用DNS-01验证方式。首先需要创建DNS提供商的API令牌Secret:

cloudflare-api-token-secret.yaml

apiVersion: v1
kind: Secret
metadata:
  namespace: cert-manager # cluster-issuer使用的secret需要在cert-manager命名空间下
  name: cloudflare-api-token-secret
type: Opaque
stringData:
  api-token: xxx # 请替换为您的实际Cloudflare API令牌

创建支持DNS-01验证的ClusterIssuer:

cluster-issuer.yaml

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod-dns-cluster-issuer
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: example@example.com
    privateKeySecretRef:
      name: letsencrypt-prod-dns-cluster-issuer-secret
    solvers:
      - dns01:
          cloudflare:
            apiTokenSecretRef:
              name: cloudflare-api-token-secret
              key: api-token
        selector:
          dnsNames:
            - "*.example.com"
            - "example.com"
            - "app.example.com"
      - dns01:
          cloudflare:
            apiTokenSecretRef:
              name: cloudflare-api-token-secret
              key: api-token
        selector:
          dnsZones:
            - "example.com"
      - dns01:
          # 兜底
          cloudflare:
            apiTokenSecretRef:
              name: cloudflare-api-token-secret
              key: api-token

3. 创建证书资源

方式一:通过Ingress自动创建特定域名证书

ingress-test1.yaml

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: cert-manager-ingress-test1
  namespace: default
  annotations:
    # 通过 cert-manager 自动创建证书
    cert-manager.io/cluster-issuer: "letsencrypt-prod-dns-cluster-issuer"
    nginx.ingress.kubernetes.io/use-forwarded-headers: "true"
    nginx.ingress.kubernetes.io/proxy-real-ip-cidr: "0.0.0.0/0"
    nginx.ingress.kubernetes.io/enable-real-ip: "true"
    nginx.ingress.kubernetes.io/compute-full-forwarded-for: "true"
    nginx.ingress.kubernetes.io/proxy-body-size: "100m"
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - app.example.com # 创建特定的域名的证书
      secretName: app.example.com-ingress-tls-secret # 保存ssl证书的secret,cert-manager自动创建的证书将保存到此secret中
  rules:
    - host: app.example.com
      http:
        paths:
          - pathType: Prefix
            backend:
              service:
                name: example-svc
                port:
                  number: 8080
            path: /

方式二:通过Ingress自动创建通配符证书

ingress-test2.yaml

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: cert-manager-ingress-test2
  namespace: default
  annotations:
    # 通过 cert-manager 自动创建证书
    cert-manager.io/cluster-issuer: "letsencrypt-prod-dns-cluster-issuer"
    nginx.ingress.kubernetes.io/use-forwarded-headers: "true"
    nginx.ingress.kubernetes.io/proxy-real-ip-cidr: "0.0.0.0/0"
    nginx.ingress.kubernetes.io/enable-real-ip: "true"
    nginx.ingress.kubernetes.io/compute-full-forwarded-for: "true"
    nginx.ingress.kubernetes.io/proxy-body-size: "100m"
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - "*.example.com" # 创建三级域名通配符证书
        - "example.com" # 创建二级域名证书
      secretName: wildcard-example.com-manager-tls-secret # 保存ssl证书的secret,cert-manager自动创建的证书将保存到此secret中
  rules:
    - host: app2.example.com
      http:
        paths:
          - pathType: Prefix
            backend:
              service:
                name: example-svc
                port:
                  number: 8080
            path: /

方式三:手动创建Certificate,并手动绑定到Ingress

先手动创建通配符证书 manual-create-certificate.yaml

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: wildcard-example.com-certificate
  namespace: default
spec:
  secretName: wildcard-example.com-tls-secret # 保存ssl证书的secret,cert-manager自动创建的证书将保存到此secret中
  issuerRef:
    name: letsencrypt-prod-dns-cluster-issuer
    kind: ClusterIssuer
  dnsNames:
    - "*.example.com"
    - "example.com"

然后创建Ingress时绑定此证书

ingress-test3.yaml

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: cert-manager-ingress-test3
  namespace: default
  annotations:
    # 注意手动创建Certificate时,这里不需要使用注解cert-manager.io/cluster-issuer
    nginx.ingress.kubernetes.io/use-forwarded-headers: "true"
    nginx.ingress.kubernetes.io/proxy-real-ip-cidr: "0.0.0.0/0"
    nginx.ingress.kubernetes.io/enable-real-ip: "true"
    nginx.ingress.kubernetes.io/compute-full-forwarded-for: "true"
    nginx.ingress.kubernetes.io/proxy-body-size: "100m"
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - app3.example.com
      secretName: wildcard-example.com-tls-secret # 引用上面manual-create-certificate.yaml预先已创建的通配符证书
  rules:
    - host: app3.example.com
      http:
        paths:
          - pathType: Prefix
            backend:
              service:
                name: example-svc
                port:
                  number: 8080
            path: /

4. 查看资源类型

# 查看核心资源
kubectl get certificate          # 查看证书
kubectl get issuer               # 查看命名空间级签发者
kubectl get clusterissuer        # 查看集群级签发者

kubectl get challenge            # 查看 ACME 验证挑战
kubectl get order                # 查看 ACME 订单
kubectl get certificaterequest   # 查看 ACME 证书请求

kubectl get secret

总结

cert-manager为Kubernetes环境提供了强大的证书自动化管理能力,通过与Let's Encrypt等免费CA集成,可以大大简化HTTPS配置的复杂性。在实际应用中,建议:

  1. 先使用Let's Encrypt测试环境进行验证
  2. 根据实际需求选择ClusterIssuer或Issuer
  3. 根据实际需求选择合适的验证方式(HTTP-01或DNS-01)
  4. 设置适当的监控和告警机制
  5. 定期检查证书状态和cert-controller日志

参考资料

#k8s #开发