概述
在Kubernetes环境中部署应用时,为服务配置HTTPS加密访问是一个常见需求。手动管理SSL证书不仅繁琐,而且容易出错和过期。cert-manager是一个Kubernetes原生证书管理工具,可以自动化证书的申请、签发和续期过程,特别是与Let’s Encrypt等免费证书颁发机构(CA)集成,为Kubernetes服务提供免费的SSL证书。
本文将介绍cert-manager的基本原理、安装配置方法,以及如何使用它来自动签发和管理SSL证书。
cert-manager介绍
cert-manager是一个开源的Kubernetes证书管理控制器,它可以在Kubernetes集群中自动化证书的颁发和管理过程。主要特性包括:
- 自动化证书管理:自动申请、签发、续期和安装证书
- 多种CA支持:支持Let’s Encrypt、HashiCorp Vault、Venafi等多种证书颁发机构
- ACME协议支持:完整实现ACME协议,支持HTTP-01和DNS-01验证方式
- 证书资源抽象:提供Certificate、Issuer、ClusterIssuer等自定义资源定义(CRD)
- 集成Ingress:与常见Ingress控制器无缝集成,自动为Ingress配置证书
- 监控和告警:提供证书过期监控和告警机制
原理
cert-manager的工作原理基于Kubernetes的控制器模式和自定义资源定义(CRD):
核心组件
- **控制器(Controller)**:监听Certificate资源的变化,处理证书申请和续期
- **自定义资源定义(CRD)**:
Certificate:定义需要申请的证书
Issuer:命名空间级别的证书颁发者配置
ClusterIssuer:集群级别的证书颁发者配置
- Webhook:验证和转换自定义资源
工作流程
- 用户创建Certificate资源,定义所需证书的域名、有效期等信息
- cert-manager控制器检测到新的Certificate资源
- 根据Certificate中引用的Issuer/ClusterIssuer配置,选择合适的证书颁发机构
- 使用ACME协议向CA申请证书
- 完成域名所有权验证(HTTP-01或DNS-01)
- 获取证书并存储在Kubernetes Secret中
- 将证书应用到Ingress或其他资源
- 定期检查证书有效期,自动续期即将过期的证书
ACME验证方式
- HTTP-01验证:通过在域名下放置特定文件来验证域名所有权
- DNS-01验证:通过在DNS记录中添加TXT记录来验证域名所有权
操作步骤
1. 安装cert-manager
1 2 3 4 5 6 7
| 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)
以下配置说明:
- 使用Let’s Encrypt生产环境(ClusterIssuer)
- 类型为ClusterIssuer
- 使用DNS-01验证方式
对于需要通配符证书或内网服务的场景,可以使用DNS-01验证方式。首先需要创建DNS提供商的API令牌Secret:
cloudflare-api-token-secret.yaml
1 2 3 4 5 6 7 8
| apiVersion: v1 kind: Secret metadata: namespace: cert-manager name: cloudflare-api-token-secret type: Opaque stringData: api-token: xxx
|
创建支持DNS-01验证的ClusterIssuer:
cluster-issuer.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: cert-manager-ingress-test1 namespace: default annotations: 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 rules: - host: app.example.com http: paths: - pathType: Prefix backend: service: name: example-svc port: number: 8080 path: /
|
方式二:通过Ingress自动创建通配符证书
ingress-test2.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: cert-manager-ingress-test2 namespace: default annotations: 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 rules: - host: app2.example.com http: paths: - pathType: Prefix backend: service: name: example-svc port: number: 8080 path: /
|
方式三:手动创建Certificate,并手动绑定到Ingress
先手动创建通配符证书
manual-create-certificate.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13
| apiVersion: cert-manager.io/v1 kind: Certificate metadata: name: wildcard-example.com-certificate namespace: default spec: secretName: wildcard-example.com-tls-secret issuerRef: name: letsencrypt-prod-dns-cluster-issuer kind: ClusterIssuer dnsNames: - "*.example.com" - "example.com"
|
然后创建Ingress时绑定此证书
ingress-test3.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: cert-manager-ingress-test3 namespace: default annotations: 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 rules: - host: app3.example.com http: paths: - pathType: Prefix backend: service: name: example-svc port: number: 8080 path: /
|
4. 查看资源类型
1 2 3 4 5 6 7 8 9 10 11
| # 查看核心资源 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配置的复杂性。在实际应用中,建议:
- 先使用Let’s Encrypt测试环境进行验证
- 根据实际需求选择ClusterIssuer或Issuer
- 根据实际需求选择合适的验证方式(HTTP-01或DNS-01)
- 设置适当的监控和告警机制
- 定期检查证书状态和cert-controller日志
参考资料