文章目录
自签 Root CA实践
SelfSigned 三个主要的使用场景:
- 自签证书;
- 自签来构建Root CA,然后由此CA 签发业务证书;
- 合法的CA颁发的根证书,然后由此根证书签发业务证书;
集群级 Root CA
该步骤主要用 SelfSigned 引导生成"集群级 Root CA",并创建 ClusterIssuer(全集群可签)。
shell
[root@master01 cert-manager]# vim cluster-ca-bootstrap.yaml
# 1) 一个 SelfSigned 的 ClusterIssuer(只用来签 Root CA)
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: selfsigned-bootstrap
spec:
selfSigned: {}
---
# 2) 生成 Root CA 证书(isCA: true),把 CA 私钥/证书落到 cert-manager namespace 的 Secret 里
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: cluster-root-ca
namespace: cert-manager
spec:
isCA: true
commonName: cluster-root-ca
subject:
organizations:
- linuxsb
secretName: cluster-root-ca-secret
duration: 87600h # 设置10年
privateKey:
algorithm: ECDSA
size: 256
rotationPolicy: Always
issuerRef:
name: selfsigned-bootstrap
kind: ClusterIssuer
group: cert-manager.io
---
# 3) 用上面的 Root CA Secret,创建一个真正用来"签业务证书"的 ClusterIssuer
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: cluster-ca
spec:
ca:
secretName: cluster-root-ca-secret
[root@master01 cert-manager]# kubectl apply -f cluster-ca-bootstrap.yaml
提示:Root CA 和业务证书都会过期(默认都是90天),一般生产环境中Root CA 很少变,如图 Root CA 过期了,所有基于此办法的业务证书都会不受信任,因此在如上实验中,直接将 Root CA 设置为 10 年。
确认验证:
shell
[root@master01 cert-manager]# kubectl get clusterissuer selfsigned-bootstrap cluster-ca
NAME READY AGE
selfsigned-bootstrap True 2m29s
cluster-ca True 2m29s
[root@master01 cert-manager]# kubectl -n cert-manager get certificate cluster-root-ca
NAME READY SECRET AGE
cluster-root-ca True cluster-root-ca-secret 2m32s
[root@master01 cert-manager]# kubectl -n cert-manager get secret cluster-root-ca-secret
NAME TYPE DATA AGE
cluster-root-ca-secret kubernetes.io/tls 3 2m33s
签名证书
该步骤主要用"cluster-ca"给所需的域名签一张证书(落到 test namespace 的 TLS Secret)。
shell
[root@master01 cert-manager]# kubectl create namespace test
[root@master01 cert-manager]# vim test-flask-cert.yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: flask-linuxsb-com
namespace: test
spec:
secretName: flask-linuxsb-com-cert
duration: 8760h # 设置 1 年时间
dnsNames:
- flask.linuxsb.com
issuerRef:
name: cluster-ca
kind: ClusterIssuer
privateKey:
rotationPolicy: Always
[root@master01 cert-manager]# kubectl apply -f test-flask-cert.yaml
[root@master01 cert-manager]# kubectl -n test get certificate flask-linuxsb-com
NAME READY SECRET AGE
flask-linuxsb-com True flask-linuxsb-com-cert 16s
[root@master01 cert-manager]# kubectl -n test get secret flask-linuxsb-com-cert
NAME TYPE DATA AGE
flask-linuxsb-com-cert kubernetes.io/tls 3 16s
提示:Root CA 和业务证书都会过期(默认都是90天),一般生产环境中Root CA 很少变,业务证书 会基于 cert-manager 自动续期:只要 Certificate 资源还在、Issuer 可用,它会在到期前(默认在证书有效期的约 2/3 处开始尝试,具体由版本/策略决定)自动重新签发并更新 Secret。但实验环境也给与 1 年有效期。
使用证书
该步骤主要是让 Ingress 使用这个自签 TLS Secret(对外暴露 HTTPS)。
创建一个测试的的demo应用。
shell
[root@master01 cert-manager]# vim test-flask-demo.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-flask-dp
namespace: test
spec:
selector:
matchLabels:
name: hello-flask
template:
metadata:
name: hello-flask
labels:
name: hello-flask
spec:
containers:
- name: hello-flask
image: registry.cn-hangzhou.aliyuncs.com/xhyimages/hello-flask:v1.0.0
ports:
- containerPort: 5000
---
apiVersion: v1
kind: Service
metadata:
name: hello-flask-svc
namespace: test
spec:
selector:
name: hello-flask
ports:
- port: 80
targetPort: 5000
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: hello-flask-ingress
namespace: test
spec:
ingressClassName: "nginx"
tls:
- hosts:
- flask.linuxsb.com
secretName: flask-linuxsb-com-cert
rules:
- host: flask.linuxsb.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: hello-flask-svc
port:
number: 80
[root@master01 cert-manager]# kubectl apply -f test-flask-demo.yaml
确认验证
- 访问验证
客户端进行访问,或者浏览器进行访问 https://flask.linuxsb.com/ 。
shell
[root@master01 cert-manager]# kubectl -n test get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
hello-flask-dp-cd7fc68b-v6g6c 1/1 Running 0 30s 10.10.30.119 worker02 <none> <none>
[root@master01 cert-manager]# kubectl -n test get svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
hello-flask-svc ClusterIP 10.20.19.56 <none> 80/TCP 33s name=hello-flask
[root@master01 cert-manager]# kubectl -n test get ingress -o wide
NAME CLASS HOSTS ADDRESS PORTS AGE
hello-flask-ingress nginx flask.linuxsb.com 10.20.60.120 80, 443 36s
[root@master01 cert-manager]# curl -k https://flask.linuxsb.com/
<p>Hello, Flask, Deployment Succuss!</p>

提示:由于自签名不受信任是预期内的。
- 添加信任
可将 Root CA 导出,然后浏览器导入,从而实现证书受信。
shell
[root@master01 cert-manager]# kubectl -n cert-manager get secret cluster-root-ca-secret -o jsonpath='{.data.tls\.crt}' | base64 -d > cluster-root-ca.crt
提示 :很多 CA Secret 不一定有 ca.crt 这个键;对"CA 证书"来说,通常 tls.crt 就是 CA 证书本体。
如果你的 Secret 里同时存在 ca.crt,也可以把上面的 tls.crt 换成 ca.crt 导出。
证书信息(确认 Subject/有效期)可通过如下查看:
shell
openssl x509 -in cluster-root-ca.crt -noout -subject -issuer -dates -fingerprint -sha256
浏览器导入(导入过程略),然后再次访问:

可信 Root CA实践
准备 Root CA
该实践主要是指假设已有CA证书,或者是合法的CA机构,以及准备构建具备可信的PKI基础设施。
区别于自签名签发CA,然后使用该CA签发业务证书,可信CA是指假定已有用于签发业务证书的Root CA证书,然后用此来构建完整的PKI基础设施,即先使用 Root CA证书来创建secret,然后使用cert-manager基于此secret来签发和自动管理业务证书。
CA issuers 通常用于 cert-manager 演示,或供具备 PKI 运维经验和工具的高级用户使用。
若要安全地在生产环境中使用,CA 发行者需熟悉轮换机制、信任存储分发和灾难恢复制定复杂的规划要求。
CA 颁发机构必须配置存储在 Kubernetes Secret 中的证书和私钥。可选择在外部创建,或使用 SelfSigned 颁发机构引导根证书(参考集群级 Root CA步骤)。
本实验仅演示基于已有 Root CA 情况下,做业务证书签发的整个过程。
提示:证书的 Secret 应位于与 Issuer 相同的命名空间内,若想创建 ClusterIssuer ,则需要 Secret 和 ClusterIssuer 都位于 Cluster Resource Namespace (即默认的 cert-manager 命名空间)中。
- 准备根证书
shell
[root@master01 cert-manager]# kubectl -n cert-manager get secret cluster-root-ca-secret -o jsonpath='{.data.tls\.crt}' | base64 -d > cluster-root-ca.crt
[root@master01 cert-manager]# kubectl -n cert-manager get secret cluster-root-ca-secret -o jsonpath='{.data.tls\.key}' | base64 -d > cluster-root-ca.key
[root@master01 cert-manager]# ll cluster-root-ca.crt cluster-root-ca.key
-rw-r--r-- 1 root root 619 Mar 6 16:52 cluster-root-ca.crt
-rw-r--r-- 1 root root 227 Mar 9 10:52 cluster-root-ca.key
提示:该指南使用已有的Root CA证书进行演示,即假定如上证书为合法的CA根证书。
- 创建secret
CA 签发者代表一个证书颁发机构,其证书和私钥以 Kubernetes Secret 的形式存储在集群内部。
shell
[root@master01 cert-manager]# cat cluster-root-ca.crt | base64 -w0
LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJuRENDQVVLZ0F3SUJBZ0lVRzV5M1hmVTU4MzRNVWxlZmJ2bjV5Qi9scFVFd0NnWUlLb1pJemowRUF3SXcKTERFUU1BNEdBMVVFQ2hNSGJHbHVkWGh6WWpFWU1CWUdBMVVFQXhNUFkyeDFjM1JsY2kxeWIyOTBMV05oTUI0WApEVEkyTURNd05qQTRNVEl3TUZvWERUSTJNRFl3TkRBNE1USXdNRm93TERFUU1BNEdBMVVFQ2hNSGJHbHVkWGh6CllqRVlNQllHQTFVRUF4TVBZMngxYzNSbGNpMXliMjkwTFdOaE1Ga3dFd1lIS29aSXpqMENBUVlJS29aSXpqMEQKQVFjRFFnQUVWVno0RkVkNFMzNFpZSXl1NEUxL1IrMC9FaXBtQU1yYThJQXhtVWp5cFkxZ1JPMlFWb09CVkdMcAp6WjdsM1FBZDhsRFdWbnREQTU1Z2lLR3QvSUFsYmFOQ01FQXdEZ1lEVlIwUEFRSC9CQVFEQWdLa01BOEdBMVVkCkV3RUIvd1FGTUFNQkFmOHdIUVlEVlIwT0JCWUVGRnRRZDcybnZuenFkM2J0LzJwYjhjdEladFQ3TUFvR0NDcUcKU000OUJBTUNBMGdBTUVVQ0lRQ2h4eUFvbmVMbFFCeVVFM2JoeVMzMjcvOHRmZmJCUXZpSnZPdENnSVByb1FJZwpJWklWNWVESEcwUUIwaGFJUkw0a2JxUFdkelZXalp1REhBam0xZVl4SVlBPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
[root@master01 cert-manager]# cat cluster-root-ca.key | base64 -w0
LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUFxZXlUV2hRdjVEdXJ4OENNRVRzK0loNjJzZmZnZS9DL24wUXZsalZhUGxvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFVlZ6NEZFZDRTMzRaWUl5dTRFMS9SKzAvRWlwbUFNcmE4SUF4bVVqeXBZMWdSTzJRVm9PQgpWR0xwelo3bDNRQWQ4bERXVm50REE1NWdpS0d0L0lBbGJRPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo=
[root@master01 cert-manager]# vim cluster-root-secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: ca-key-pair
namespace: cert-manager
data:
tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJuRENDQVVLZ0F3SUJBZ0lVRzV5M1hmVTU4MzRNVWxlZmJ2bjV5Qi9scFVFd0NnWUlLb1pJemowRUF3SXcKTERFUU1BNEdBMVVFQ2hNSGJHbHVkWGh6WWpFWU1CWUdBMVVFQXhNUFkyeDFjM1JsY2kxeWIyOTBMV05oTUI0WApEVEkyTURNd05qQTRNVEl3TUZvWERUSTJNRFl3TkRBNE1USXdNRm93TERFUU1BNEdBMVVFQ2hNSGJHbHVkWGh6CllqRVlNQllHQTFVRUF4TVBZMngxYzNSbGNpMXliMjkwTFdOaE1Ga3dFd1lIS29aSXpqMENBUVlJS29aSXpqMEQKQVFjRFFnQUVWVno0RkVkNFMzNFpZSXl1NEUxL1IrMC9FaXBtQU1yYThJQXhtVWp5cFkxZ1JPMlFWb09CVkdMcAp6WjdsM1FBZDhsRFdWbnREQTU1Z2lLR3QvSUFsYmFOQ01FQXdEZ1lEVlIwUEFRSC9CQVFEQWdLa01BOEdBMVVkCkV3RUIvd1FGTUFNQkFmOHdIUVlEVlIwT0JCWUVGRnRRZDcybnZuenFkM2J0LzJwYjhjdEladFQ3TUFvR0NDcUcKU000OUJBTUNBMGdBTUVVQ0lRQ2h4eUFvbmVMbFFCeVVFM2JoeVMzMjcvOHRmZmJCUXZpSnZPdENnSVByb1FJZwpJWklWNWVESEcwUUIwaGFJUkw0a2JxUFdkelZXalp1REhBam0xZVl4SVlBPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
tls.key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUFxZXlUV2hRdjVEdXJ4OENNRVRzK0loNjJzZmZnZS9DL24wUXZsalZhUGxvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFVlZ6NEZFZDRTMzRaWUl5dTRFMS9SKzAvRWlwbUFNcmE4SUF4bVVqeXBZMWdSTzJRVm9PQgpWR0xwelo3bDNRQWQ4bERXVm50REE1NWdpS0d0L0lBbGJRPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo=
[root@master01 cert-manager]# kubectl apply -f cluster-root-secret.yaml
提示 :若后续使用 Issuer 而不是 ClusterIssuer ,则需要把 secret 创建到 Issuer 同命名空间,即后续真正业务证书所在的命名空间。
准备 Issuer
使用上一步骤的 Root CA Secret,创建一个真正用来"签业务证书"的 ClusterIssuer。
提示:也可创建Issuer,如下为创建Issuer,但创建Issuer必须和签发业务证书在同一个namespace,而创建ClusterIssuer可以在任何命名空间引用,因此建议创建ClusterIssuer。
shell
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: ca-issuer
namespace: test
spec:
ca:
secretName: ca-key-pair
创建ClusterIssuer。
shell
[root@master01 cert-manager]# vim cluster-root-issuer.yaml
---
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: ca-issuer
spec:
ca:
secretName: ca-key-pair
[root@master01 cert-manager]# kubectl apply -f cluster-root-issuer.yaml
确认验证:
shell
[root@master01 cert-manager]# kubectl -n cert-manager get secrets ca-key-pair -o wide
NAME TYPE DATA AGE
ca-key-pair Opaque 2 70s
[root@master01 cert-manager]# kubectl get clusterissuers.cert-manager.io ca-issuer -o wide
NAME READY STATUS AGE
ca-issuer True Signing CA verified 7m44s
签名证书
该步骤主要用 ca-issuer 给所需的域名签一张证书(落到 test namespace 的 TLS Secret)。
shell
[root@master01 cert-manager]# kubectl create namespace test
[root@master01 cert-manager]# vim test-spring-cert.yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: spring-linuxsb-com
namespace: test
spec:
secretName: spring-linuxsb-com-cert
duration: 8760h # 设置 1 年时间
dnsNames:
- spring.linuxsb.com
issuerRef:
name: ca-issuer
kind: ClusterIssuer
privateKey:
rotationPolicy: Always
[root@master01 cert-manager]# kubectl apply -f test-spring-cert.yaml
[root@master01 cert-manager]# kubectl -n test get certificate spring-linuxsb-com
NAME READY SECRET AGE
spring-linuxsb-com True spring-linuxsb-com-cert 45s
[root@master01 cert-manager]# kubectl -n test get secret spring-linuxsb-com-cert
NAME TYPE DATA AGE
spring-linuxsb-com-cert kubernetes.io/tls 3 49s
提示:Root CA 和业务证书都会过期(默认都是90天),一般生产环境中Root CA 很少变,业务证书 会基于 cert-manager 自动续期:只要 Certificate 资源还在、Issuer 可用,它会在到期前(默认在证书有效期的约 2/3 处开始尝试,具体由版本/策略决定)自动重新签发并更新 Secret。但实验环境也给与 1 年有效期。
使用证书
该步骤主要是让 Ingress 使用这个自签 TLS Secret(对外暴露 HTTPS)。
创建一个测试的的demo应用。
shell
[root@master01 cert-manager]# vim test-spring-demo.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-spring-dp
namespace: test
spec:
selector:
matchLabels:
name: hello-spring
template:
metadata:
name: hello-spring
labels:
name: hello-spring
spec:
containers:
- name: hello-spring
image: registry.cn-hangzhou.aliyuncs.com/xhyimages/hello-spring:v1.0.0
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: hello-spring-svc
namespace: test
spec:
selector:
name: hello-spring
ports:
- port: 80
targetPort: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: hello-spring-ingress
namespace: test
spec:
ingressClassName: "nginx"
rules:
- host: spring.linuxsb.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: hello-spring-svc
port:
number: 80
[root@master01 cert-manager]# kubectl apply -f test-spring-demo.yaml
确认验证
- 访问验证
客户端进行访问,或者浏览器进行访问 https://spring.linuxsb.com/ 。
shell
[root@master01 cert-manager]# kubectl -n test get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
hello-spring-dp-856699b7ff-2cxng 1/1 Running 0 3m11s 10.10.30.82 worker02 <none> <none>
[root@master01 cert-manager]# kubectl -n test get svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
hello-spring-svc ClusterIP 10.20.144.170 <none> 80/TCP 3m14s name=hello-spring
[root@master01 cert-manager]# kubectl -n test get ingress -o wide
NAME CLASS HOSTS ADDRESS PORTS AGE
hello-spring-ingress nginx spring.linuxsb.com 10.20.60.120 80 3m16s
[root@master01 cert-manager]# curl -k https://spring.linuxsb.com/
Hello, Spring, Deployment Succuss!

提示:由于Root CA为模拟的,实际上也是自签名的,因此不受信任是预期内的。
- 添加信任
可将 Root CA 在浏览器导入,从而实现证书受信,导入后(导入过程略),再次访问进行验证。