是用来做什么的
用来签发证书的。
解决了什么问题
TLS 协议在集群内部组件通信以及对外暴露服务时大量使用,伴随着 TLS 证书管理的复杂性。cert-manager 提供了一个集中式的全自动证书管理服务。
典型应用场景
ingress
ingress 可以对外提供七层的 http 和 https 服务,cert-manager 可以自动为 https 服务配置 TLS 证书。
基础概念
证书存储位置
TLS 证书一般以 secret 的形式存储在集群中,可以通过类型 kubernetes.io/tls
来识别
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
> k get secret -A | grep kubernetes.io/tls
argocd argocd kubernetes.io/tls
cattle-provisioning-capi-system capi-webhook-service-cert kubernetes.io/tls
cattle-system cattle-webhook-ca kubernetes.io/tls
cattle-system cattle-webhook-tls kubernetes.io/tls
cattle-system rancher-tls-letsencrypt kubernetes.io/tls
cattle-system serving-cert kubernetes.io/tls
cattle-system tls-ca kubernetes.io/tls
cattle-system tls-rancher kubernetes.io/tls
cattle-system tls-rancher-ingress kubernetes.io/tls
cattle-system tls-rancher-internal kubernetes.io/tls
cattle-system tls-rancher-internal-ca kubernetes.io/tls
cert-manager argocd-tls kubernetes.io/tls
cert-manager codeme-wildcard kubernetes.io/tls
derper derper-tls kubernetes.io/tls
kube-system k3s-serving kubernetes.io/tls
|
公钥和私钥分别以文本 (pem 格式) 的形式存储在 secret 资源中
1
2
3
4
5
6
7
8
9
|
> k describe secret argocd -n argocd
Name: argocd
Namespace: argocd
Type: kubernetes.io/tls
Data
====
tls.crt: 3582 bytes
tls.key: 1679 bytes
|
怎么获取证书
cert-manager 支持多种方式获取证书,详情可见 官方文档。
下面介绍一下通过 ACME 的方式获取 Let’s encrypt
证书
和 certbot
,acme.sh
等工具的原理是一样的。
简单来说,首先,我们会向 Let’s encrypt 的服务器 (CA) 请求:“我想要 example.com
这个域名的证书。”
接着 CA 变会给我发出 challenge,如果我们能够完成 challenge,便可以证明我们对这个域名的所有权,可以顺利签发证书。
常见的 challenge 有两种:http-01
和 dns-01
。
前者需要我们有一个服务器,并对公网提供 http 服务。CA 会给我们一个特定的文件,让我们放在一个特定的目录下。如果 CA 能够通过我们的域名访问到上述文件,即完成验证。
后者需要我们在域名的 DNS 中添加一条特定的 TXT 记录,CA 检测到此记录,即完成验证。
在当前集群全自动管理的环境下,我推荐使用 http-01
的方式进行校验。因为 cert-manager 本身就支持使用 ingress 全自动完成认证,可以给每一个服务都单独签发一张证书,因此泛域名证书变得没必要了(泛域名证书只支持 dns-01
验证)。同时,不使用 dns 的方式,还避免了管理 dns 提供商的密钥带来的负担。
基础概念
cert-manager 也引入了一系列的 crd,下面选几个常用的进行讲解
Issuers
配置怎么签发证书,也就是 CA。类似于 StorageClass。
ClusterIssuers
和前者类似,但是前者的有效范围仅限于当前 namespace,而这个范围的是整个集群。
CertificateRequests
证书请求,类似于 PVC。声明需要证书的域名和使用的Issuers。
Certificates
证书,类似于 PV。定义了关联的域名和存储的 secret。
安装
参考 官方文档
1
2
3
4
5
6
7
8
|
helm repo add jetstack https://charts.jetstack.io --force-update
helm install \
cert-manager jetstack/cert-manager \
--namespace cert-manager \
--create-namespace \
--version v1.16.1 \
--set crds.enabled=true
|
配置前提醒
一下内容都建立的你已经有了一个域名,并且通过这个域名,可以访问到你的集群的 80 和 443 端口的前提下。
配置 Issuers
可以参考 文档
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: letsencrypt-prod
spec:
acme:
# The ACME server URL
server: https://acme-v02.api.letsencrypt.org/directory
# Email address used for ACME registration
email: user@example.com
# Name of a secret used to store the ACME account private key
privateKeySecretRef:
name: letsencrypt-prod
# Enable the HTTP-01 challenge provider
solvers:
- http01:
ingress:
ingressClassName: traefik
|
一般就用这个就够了,记得最后 ingressClassName
要换成自己用的
部署临时服务
出于测试的目的,我们现在起一个 nginx 服务器
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginix
name: nginix
spec:
replicas: 1
selector:
matchLabels:
app: nginix
strategy: {}
template:
metadata:
labels:
app: nginix
spec:
containers:
- image: nginx:latest
name: nginx
resources: {}
status: {}
---
apiVersion: v1
kind: Service
metadata:
name: nginix-service
spec:
selector:
app: nginix
ports:
- protocol: TCP
port: 80
targetPort: 80
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginix-ingress
spec:
rules:
- host: nginix.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nginix-service
port:
number: 80
|
访问对应的域名,出现 nginx 的欢迎页面
配置自动 TLS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginix-ingress
annotations:
cert-manager.io/issuer: "letsencrypt-prod"
spec:
tls:
- hosts:
- nginix.example.com
secretName: nginix-tls
rules:
- host: nginix.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nginix-service
port:
number: 80
|
接着更新 ingress,加上这一段,注意这些选项一个都不能少,不然 cert-manager 可能不会检测到
1
2
3
4
5
6
7
8
9
|
metadata:
name: nginix-ingress
annotations:
cert-manager.io/issuer: "letsencrypt-prod"
spec:
tls:
- hosts:
- nginix.example.com
secretName: nginix-tls
|
稍等片刻,就可以看到创建的证书了
1
2
3
|
> k get secret
NAME TYPE DATA AGE
nginix-tls kubernetes.io/tls 2 106s
|
此时再打开页面,页面已经自动启用了 https 了
直接创建证书
cert-manager 也支持直接创建证书。
使用方法是直接创建一个证书请求
1
2
3
4
5
6
7
8
9
10
11
12
|
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: example-cert
namespace: default
spec:
secretName: example-tls
issuerRef:
name: letsencrypt-prod
kind: Issuer
dnsNames:
- example.com
|
证书便会直接保存在 example-tls
这个 secret 内
挂载证书到应用
有的时候,我们需要应用直接使用证书,所以还需要把证书挂载到容器内部
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
|
apiVersion: apps/v1
kind: Deployment
metadata:
name: example
spec:
replicas: 1
selector:
template:
spec:
containers:
- image: nginx
name: example
volumeMounts:
- name: example-certs
mountPath: /app/certs
ports:
- containerPort: 80
protocol: TCP
- containerPort: 443
protocol: TCP
restartPolicy: Always
volumes:
- name: example-certs
secret:
secretName: example-tls
items:
- key: tls.crt
path: example.com.crt
- key: tls.key
path: example.com.key
|
可以使用这样的方式挂载
TLS 直通
如果我们的应用需要共享主机的 443 端口,但是又不是提供 http 服务,可以使用 SNI 进行分流,然后使用 TLS Passthrough 直接转发到其他后端,具体可以使用 IngressRouteTCP
实现