1 2 3 4 5 6 7 作者:李晓辉 联系方式: 1. 微信:Lxh_Chat 2. 邮箱:939958092@qq.com
零信任环境概念 零信任环境就是每次交互都先当它是不受信任的,这样更安全。用户只能访问那些明确被允许的文件或对象,通信还得加密,客户端应用也得验证服务器是不是真的靠谱。而且,零信任环境要求用受信任的证书颁发机构(CA)签名的证书来加密流量,通过引用 CA 证书,应用就能用已签名的证书加密验证另一个应用是不是真的。
service-ca控制器 OpenShift 有个 service-ca
控制器,挺有意思的。它能为内部流量生成并签名服务证书,还会创建一个机密,里面装着已签名的证书和密钥。Deployment 可以把这个机密挂载成卷,然后就能用上已签名的证书啦。不过,客户端应用也得信任 service-ca
控制器的 CA 哦。
service-ca
证书有效期默认是 26 个月,13 个月后会自动轮转。轮转完还有 13 个月的宽限期,原来的 CA 证书在这期间还能用。要是有 pod 是信任老的 CA 证书的,那在宽限期内得找个机会重启一下,重启后服务会自动把新的 CA 捆绑包加进去。
要是你想手动来轮转证书,操作也挺简单。要是想轮转生成的服务证书,就把现有的机密删掉,service-ca 控制器会自动给你生成一个新的。
1 oc delete secret certificate-secret
要是想手动轮转服务 CA 证书,那就把 openshift-service-ca 命名空间里的 signing-key 机密删掉。不过要注意,这个操作会让之前的服务 CA 证书马上失效,你得赶紧把所有用到它的 pod 都重启一下,不然 TLS 就没法正常工作了。
1 oc delete secret/signing-key -n openshift-service-ca
零信任案例 要是想生成证书和密钥对,只要给服务加上 service.beta.openshift.io/serving-cert-secret-name=your-secret
这个注释就行啦。service-ca
控制器会瞅瞅同一命名空间里有没有 your-secret
这个机密,要是没有,就直接创建一个,然后把服务的已签名证书和密钥对都塞进去。
创建后端服务 比如说,我们来为服务生成一个包含证书对的机密:lxh-secret
第一步,先把服务创建出来
1 2 3 4 5 6 oc project default oc new-app --name hello --image registry.ocp4.example.com:8443/redhattraining/hello-world-nginx:latest oc get service NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE hello ClusterIP 172.30.141.226 <none> 8080/TCP 13s
这个8080端口不方便访问,我们直接给它删了,改成443,下面的8888端口,是我们后期打算让pod工作在这个端口
1 2 oc delete service hello oc expose deployment/hello --port 443 --target-port 8888
1 2 3 [student@workstation ~]$ oc get service hello NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE hello ClusterIP 172.30.83.38 <none> 443/TCP 105s
为服务创建tls机密 第二步,为服务启用证书填充
1 2 [root@workstation ~]# oc annotate service hello service.beta.openshift.io/serving-cert-secret-name=lxh-secret service/hello annotate
看看服务是否添加了这个注解,看上去多了3个cert相关的注解
1 2 3 4 5 6 7 8 9 10 [root@workstation ~]# oc describe service hello Name: hello Namespace: default Labels: app=hello app.kubernetes.io/component=hello app.kubernetes.io/instance=hello Annotations: openshift.io/generated-by: OpenShiftNewApp service.alpha.openshift.io/serving-cert-signed-by: openshift-service-serving-signer@1706011744 service.beta.openshift.io/serving-cert-secret-name: lxh-secret service.beta.openshift.io/serving-cert-signed-by: openshift-service-serving-signer@1706011744
我们来研究一下,这个自动生成的机密到底是什么
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 [root@workstation ~]# oc describe secrets lxh-secret Name: lxh-secret Namespace: default Labels: <none> Annotations: service.alpha.openshift.io/expiry: 2026-12-20T06:39:38Z service.beta.openshift.io/expiry: 2026-12-20T06:39:38Z service.beta.openshift.io/originating-service-name: hello service.beta.openshift.io/originating-service-uid: fc4aafa0-fa9f-4b7b-9d44-e52507b893a4 Type: kubernetes.io/tls Data ==== tls.crt: 2575 bytes tls.key: 1675 bytes
证书是啥内容看看去
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 [root@workstation ~]# oc get secrets lxh-secret -o yaml apiVersion: v1 data: tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUR3VENDQXFtZ0F3SUJBZ0lJQVdmMURucWtFSmN3RFFZSktvWklodmNOQVFFTEJRQXdOakUwTURJR0ExVUUKQXd3cmIzQmxibk5vYVdaMExYTmtcmdkb25aMn tls.key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb3dJQkFBS0NBUUVBb011NGhDU2JvYmE3ZVIreC80MXUxd1Izd0I0aTdnTW82cGU4MTZnR2pGcklFbXJnCmRvbloyenZ1VlVLaG54Rm kind: Secret metadata: annotations: service.alpha.openshift.io/expiry: "2026-12-20T06:39:38Z" service.beta.openshift.io/expiry: "2026-12-20T06:39:38Z" service.beta.openshift.io/originating-service-name: hello service.beta.openshift.io/originating-service-uid: fc4aafa0-fa9f-4b7b-9d44-e52507b893a4 creationTimestamp: "2024-12-20T06:39:38Z" name: lxh-secret namespace: default ownerReferences: - apiVersion: v1 kind: Service name: hello uid: fc4aafa0-fa9f-4b7b-9d44-e52507b893a4 resourceVersion: "170877" uid: 51726655-b1cb-407b-8c76-0f2551635b1e type : kubernetes.io/tls
可以看到这个自动生成的机密中,的确包括了两个证书,我们只需要把证书挂载到我们的deployment中就可以了,我们先更新一下配置文件来包含tls部分
为后端服务准备tls配置文件 我们要读取的证书位于:/etc/pki/nginx/,让pod工作在8888端口
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 cat > nginx.conf <<-'EOF' server { listen 8888 ssl http2 default_server; listen [::]:8888 ssl http2 default_server; server_name _; root /usr/share/nginx/html; ssl_certificate "/etc/pki/nginx/server.crt" ; ssl_certificate_key "/etc/pki/nginx/private/server.key" ; ssl_session_cache shared:SSL:1m; ssl_session_timeout 10m; ssl_ciphers PROFILE=SYSTEM; ssl_prefer_server_ciphers on; include /etc/nginx/default.d/*.conf; location / { } error_page 404 /404.html; location = /40x.html { } error_page 500 502 503 504 /50x.html; location = /50x.html { } } EOF
把这个配置文件存为configmap,一会儿让deployment来挂载
1 2 [student@workstation ~]$ oc create configmap lxh-nginx-tls --from-file nginx.conf configmap/lxh-nginx-tls created
用补丁文件的方法修改,比手工改更精准
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 cat > patch.yml <<-'EOF' spec: template: spec: containers: - name: hello ports: - containerPort: 8888 volumeMounts: - name: tls-config mountPath: /etc/nginx/conf.d/ - name: server-secret mountPath: /etc/pki/nginx/ volumes: - name: tls-config configMap: defaultMode: 420 name: lxh-nginx-tls - name: server-secret secret: defaultMode: 420 secretName: lxh-secret items: - key: tls.crt path: server.crt - key: tls.key path: private/server.key EOF
来,打一下补丁,发现我们的pod age很新,是刚创建的
1 2 3 4 [student@workstation ~]$ oc patch deployment hello --patch-file patch.yml [student@workstation ~]$ oc get pod NAME READY STATUS RESTARTS AGE hello-74fbfc9fd-ppzpq 1/1 Running 0 28s
测试陌生pod访问 我们随便找个调试pod,看看能不能访问service
1 2 3 [student@workstation ~]$ oc get service hello NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE hello ClusterIP 172.30.83.38 <none> 443/TCP 105s
发现,随便找个pod是无法建立安全连接的,但是用-k跳过证书验证可以访问
以下hello.default.svc的格式是k8s自带的
hello是服务名称
default是命名空间
svc表示这是一个服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 [student@workstation ~]$ oc debug -t sh-4.4# curl https://hello.default.svc curl: (60) SSL certificate problem: self signed certificate in certificate chain More details here: https://curl.haxx.se/docs/sslcerts.html curl failed to verify the legitimacy of the server and therefore could not establish a secure connection to it. To learn more about this situation and how to fix it, please visit the web page mentioned above. sh-4.4# curl https://hello.default.svc -k <html> <body> <h1>Hello, world from nginx!</h1> </body> </html>
为客户端准备CA证书 生成包含服务 CA 捆绑包的configmap,并使用它来创建 client
1 2 [student@workstation ~]$ oc create configmap ca-file configmap/ca-file created
给这个configmap填充ca证书
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 [student@workstation ~]$ oc annotate configmap ca-file service.beta.openshift.io/inject-cabundle=true configmap/ca-file annotate [student@workstation ~]$ oc describe configmaps ca-file Name: ca-file Namespace: default Labels: <none> Annotations: service.beta.openshift.io/inject-cabundle: true Data ==== service-ca.crt: ---- -----BEGIN CERTIFICATE----- MIIDUTCCAjmgAwIBAgIIFAxCe1I9d3swDQYJKoZIhvcNAQELBQAwNjE0MDIGA1UE Awwrb3BlbnNoaWZ0LXNlcnZpY2Utc2VydmluZy1zaWduZXJAMTcwNjAxMTc0NDAe Fw0yNDAxMjMxMjA5MDNaFw0yNjAzMjMxMjA5MDRaMDYxNDAyBgNVBAMMK29wZW5z aGlmdC1zZXJ2aWNlLXNlcnZpbmctc2lnbmVyQDE3MDYwMTE3NDQwggEiMA0GCSqG SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDBmQBMDkBshSKSBnnPSDOJFx5lN05uj1ij QkdMkvZ77UF6+grNK7J2XjMUL7XGV1AB2dylr+/Ze8bv+zgaKVPuz33v5Qkq1Xq3 sGTCcvEOKFFQpNi2xvvz+SRxE3ZepSn466d8Yl8KAMOwUs41SV1hRWhMjDnQpJFY 1o6zBSF3NUHrjwpgdaoqxvpAZq0F12ZdjmP6kY64CvYujUxjpZ+WTwkqQHi/RXfL F3JkbhX/dmMsMG4lRegMwzcUvrNHV89pqg82urLAXKpEdeaqiDq1rz5ImomTJUyY nDFDQWApBS+ds++M364pKGktIIJT4S9bp1+HJWBMlVR3yRcglgD1AgMBAAGjYzBh MA4GA1UdDwEB/wQEAwICpDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQbVoak nlEWPiZHAj6Dv7RltXKfyDAfBgNVHSMEGDAWgBQbVoaknlEWPiZHAj6Dv7RltXKf yDANBgkqhkiG9w0BAQsFAAOCAQEAS3XqN/+utRKusxadlawdR61lDh4CB8mMrhc6 AVTXqdAaE2j+T4xAXMQWkCAs82UDKuCUK2O+PTf9HrePiKLp3YWi+VoqFUoPiI++ KFl24z+kcOuTatJJdutZ3UN8bk4T24GINBlQNyN2P3HDGQ1FL2NLNc59xC5qxFGC I1j4RwsxmDz2SIlPeselEMS/unqpWTAW4M9ZkMmvg7BeHsUnNtHJPtQwqYkaWlXn df5REDU7IkMKuNdlxD5PehUjujGB29PaBubfxBxFlhFyKLWJ72nECEZdJAwku1sf L+TYgUQf39c9HNo9tBmDg/XdOXGM0BUjDQ/rTo0mcbSmp6b/dg== -----END CERTIFICATE----- BinaryData ==== Events: <none>
验证零信任效果 我们来启动一个带有此ca的客户端访问一下看看
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 cat > pod-with-ca.yml <<-'EOF' apiVersion: v1 kind: Pod metadata: name: client spec: containers: - name: client image: registry.ocp4.example.com:8443/redhattraining/hello-world-nginx volumeMounts: - mountPath: /etc/pki/ca-trust/extracted/pem name: trusted-ca volumes: - configMap: defaultMode: 420 name: ca-file items: - key: service-ca.crt path: tls-ca-bundle.pem name: trusted-ca EOF
1 oc create -f pod-with-ca.yml
可以发现,直接访问成功,不需要-k跳过证书验证了
1 2 3 4 5 6 sh-4.4$ curl https://hello.default.svc <html> <body> <h1>Hello, world from nginx!</h1> </body> </html>
研究一下证书交互过程
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 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 sh-4.4$ curl https://hello.default.svc -vv -I * Rebuilt URL to: https://hello.default.svc/ * Trying 172.30.246.118... * TCP_NODELAY set * Connected to hello.default.svc (172.30.246.118) port 443 (#0) * ALPN, offering h2 * ALPN, offering http/1.1 * successfully set certificate verify locations: * CAfile: /etc/pki/tls/certs/ca-bundle.crt CApath: none * TLSv1.3 (OUT), TLS handshake, Client hello (1): * TLSv1.3 (IN), TLS handshake, Server hello (2): * TLSv1.3 (IN), TLS handshake, [no content] (0): * TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8): * TLSv1.3 (IN), TLS handshake, [no content] (0): * TLSv1.3 (IN), TLS handshake, Certificate (11): * TLSv1.3 (IN), TLS handshake, [no content] (0): * TLSv1.3 (IN), TLS handshake, CERT verify (15): * TLSv1.3 (IN), TLS handshake, [no content] (0): * TLSv1.3 (IN), TLS handshake, Finished (20): * TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1): * TLSv1.3 (OUT), TLS handshake, [no content] (0): * TLSv1.3 (OUT), TLS handshake, Finished (20): * SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 * ALPN, server accepted to use h2 * Server certificate: * subject: CN=hello.default.svc * start date: Dec 20 08:36:30 2024 GMT * expire date: Dec 20 08:36:31 2026 GMT * subjectAltName: host "hello.default.svc" matched cert's "hello.default.svc" * issuer: CN=openshift-service-serving-signer@1706011744 * SSL certificate verify ok. * Using HTTP2, server supports multi-use * Connection state changed (HTTP/2 confirmed) * Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0 * TLSv1.3 (OUT), TLS app data, [no content] (0): * TLSv1.3 (OUT), TLS app data, [no content] (0): * TLSv1.3 (OUT), TLS app data, [no content] (0): * Using Stream ID: 1 (easy handle 0x561a9eab7a00) * TLSv1.3 (OUT), TLS app data, [no content] (0): > HEAD / HTTP/2 > Host: hello.default.svc > User-Agent: curl/7.61.1 > Accept: */* > * TLSv1.3 (IN), TLS handshake, [no content] (0): * TLSv1.3 (IN), TLS handshake, Newsession Ticket (4): * TLSv1.3 (IN), TLS handshake, [no content] (0): * TLSv1.3 (IN), TLS handshake, Newsession Ticket (4): * TLSv1.3 (IN), TLS app data, [no content] (0): * Connection state changed (MAX_CONCURRENT_STREAMS == 128)! * TLSv1.3 (OUT), TLS app data, [no content] (0): * TLSv1.3 (IN), TLS app data, [no content] (0): * TLSv1.3 (IN), TLS app data, [no content] (0): < HTTP/2 200 HTTP/2 200 < server: nginx/1.14.1 server: nginx/1.14.1 < date: Fri, 20 Dec 2024 08:52:26 GMT date: Fri, 20 Dec 2024 08:52:26 GMT < content-type: text/html content-type: text/html < content-length: 72 content-length: 72 < last-modified: Wed, 26 Jun 2019 22:19:37 GMT last-modified: Wed, 26 Jun 2019 22:19:37 GMT < etag: "5d13ef79-48" etag: "5d13ef79-48" < accept-ranges: bytes accept-ranges: bytes < * Connection #0 to host hello.default.svc left intact
也可以用下面的方法验证过程
1 openssl s_client -connect hello.default.svc:443