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

概述

在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):

核心组件

  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验证方式

  • 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 # cluster-issuer使用的secret需要在cert-manager命名空间下
name: cloudflare-api-token-secret
type: Opaque
stringData:
api-token: xxx # 请替换为您的实际Cloudflare API令牌

创建支持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 自动创建证书
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

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 自动创建证书
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

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 # 保存ssl证书的secret,cert-manager自动创建的证书将保存到此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:
# 注意手动创建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. 查看资源类型

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配置的复杂性。在实际应用中,建议:

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

参考资料