# OpenShift 4.11 Certificate Rotation ## Aggregated API Servers ### CA Secret ~~~sh oc -n openshift-kube-apiserver-operator get secret aggregator-client-signer -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate ~~~ ~~~sh notBefore=Sep 29 12:52:23 2022 GMT notAfter=Oct 29 12:52:24 2022 GMT ~~~ ### Client Cert ~~~sh oc -n openshift-kube-apiserver get secret aggregator-client -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate ~~~ ~~~sh notBefore=Sep 29 13:08:34 2022 GMT notAfter=Oct 29 12:52:24 2022 GMT ~~~ ### CA Rotate ~~~sh oc -n openshift-kube-apiserver-operator delete secret aggregator-client-signer ~~~ We can see the new validity: ~~~sh oc -n openshift-kube-apiserver-operator get secret aggregator-client-signer -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate ~~~ ~~~sh notBefore=Oct 4 16:58:33 2022 GMT notAfter=Nov 3 16:58:34 2022 GMT ~~~ At this point, we need to rotate the certificates issued by the old CA, otherwise the trust chain will be broken: ~~~sh oc -n openshift-kube-apiserver-operator get secret aggregator-client-signer -o jsonpath='{.data.tls\.crt}' | base64 -d > /tmp/aggapica.crt oc -n openshift-kube-apiserver get secret aggregator-client -o jsonpath='{.data.tls\.crt}' | base64 -d > /tmp/aggapiclient.crt ~~~ ~~~sh openssl verify -verbose -CAfile /tmp/aggapica.crt /tmp/aggapiclient.crt ~~~ ~~~sh CN = system:openshift-aggregator error 20 at 0 depth lookup: unable to get local issuer certificate error /tmp/aggapiclient.crt: verification failed ~~~ Even if the trust fails with the new signer cert, the old signer cert CA is still available in the CABundle so services using old certs won't stop working. ~~~sh oc -n openshift-kube-apiserver get cm aggregator-client-ca -o jsonpath='{.data.ca-bundle\.crt}' > /tmp/aggregator-cabundle.crt ~~~ ~~~sh openssl verify -verbose -CAfile /tmp/aggregator-cabundle.crt /tmp/aggapiclient.crt ~~~ ~~~sh /tmp/aggapiclient.crt : OK ~~~ Let's force the rotation of the issued certs: ~~~sh oc -n openshift-kube-apiserver delete secret aggregator-client ~~~ ~~~sh oc -n openshift-kube-apiserver get secret aggregator-client -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate ~~~ ~~~sh notBefore=Oct 4 16:59:36 2022 GMT notAfter=Nov 3 16:58:34 2022 GMT ~~~ ~~~sh oc -n openshift-kube-apiserver get secret aggregator-client -o jsonpath='{.data.tls\.crt}' | base64 -d > /tmp/aggapiclient.crt ~~~ ~~~sh openssl verify -verbose -CAfile /tmp/aggapica.crt /tmp/aggapiclient.crt ~~~ ~~~sh /tmp/aggapiclient.crt: OK ~~~ ## MachineConfig Operator Eventhough it's name Root CA, it's just used for generating the certificate used by the MachineConfigServer (openshift-machine-config-operator/machine-config-server-tls). CA public key is present on disk at `/etc/kubernetes/ca.crt`. The private key never reaches the cluster, is deleted during bootstrap. Secret for the MCS (openshift-machine-config-operator/machine-config-server-tls) won't be recreated if deleted. It cannot be rotated, an [RFE](https://issues.redhat.com/browse/RFE-3287) has been opened. ## Service Serving Certificates The procedure for rotating the certs is already documented [here](https://docs.openshift.com/container-platform/4.10/security/certificates/service-serving-certificate.html#manually-rotate-service-ca_service-serving-certificate) ### CA Secret ~~~sh oc -n openshift-service-ca get secret signing-key -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate ~~~ ~~~sh notBefore=Sep 29 13:55:16 2022 GMT notAfter=Nov 27 13:55:17 2024 GMT ~~~ ### Client Certs Any service in the cluster using the `service.beta.openshift.io/serving-cert-secret-name: <secret-name>` ~~~sh oc -n openshift-kube-apiserver-operator get secret kube-apiserver-operator-serving-cert -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate ~~~ ~~~sh notBefore=Sep 29 13:56:07 2022 GMT notAfter=Sep 28 13:56:08 2024 GMT ~~~ ### CA Rotate ~~~sh oc -n openshift-service-ca delete secret signing-key ~~~ ~~~sh oc -n openshift-service-ca get secret signing-key -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate ~~~ ~~~sh notBefore=Oct 4 17:00:50 2022 GMT notAfter=Dec 2 17:00:51 2024 GMT ~~~ We need to wait for the certificates issued by the old CA to be automatically rotated, we can check the cluster operators: ~~~sh oc get co ~~~ ~~~sh NAME VERSION AVAILABLE PROGRESSING DEGRADED SINCE MESSAGE authentication 4.11.4 True True False 2s OAuthServerDeploymentProgressing: deployment/oauth-openshift.openshift-authentication: 1/3 pods have been updated to the latest generation baremetal 4.11.4 True False False 8d cloud-controller-manager 4.11.4 True False False 8d cloud-credential 4.11.4 True False False 8d cluster-autoscaler 4.11.4 True False False 8d config-operator 4.11.4 True False False 8d console 4.11.4 True False False 8d csi-snapshot-controller 4.11.4 True False False 8d dns 4.11.4 True False False 8d etcd 4.11.4 True False False 8d image-registry 4.11.4 True False False 8d ingress 4.11.4 True False False 8d insights 4.11.4 True False False 8d kube-apiserver 4.11.4 True False False 8d kube-controller-manager 4.11.4 True True False 8d NodeInstallerProgressing: 3 nodes are at revision 6; 0 nodes have achieved new revision 8 kube-scheduler 4.11.4 True True False 8d NodeInstallerProgressing: 3 nodes are at revision 7; 0 nodes have achieved new revision 8 kube-storage-version-migrator 4.11.4 True False False 8d machine-api 4.11.4 True False False 8d machine-approver 4.11.4 True False False 8d machine-config 4.11.4 True False False 8d marketplace 4.11.4 True False False 8d monitoring 4.11.4 True False False 8d network 4.11.4 True False False 8d node-tuning 4.11.4 True False False 8d openshift-apiserver 4.11.4 False False False 1s APIServicesAvailable: "image.openshift.io.v1" is not ready: an attempt failed with statusCode = 503, err = the server is currently unable to handle the request openshift-controller-manager 4.11.4 True True False 8d Progressing: daemonset/controller-manager: updated number scheduled is 0, desired number scheduled is 3 openshift-samples 4.11.4 True False False 8d operator-lifecycle-manager 4.11.4 True False False 8d operator-lifecycle-manager-catalog 4.11.4 True False False 8d operator-lifecycle-manager-packageserver 4.11.4 True False False 8d service-ca 4.11.4 True False False 8d storage 4.11.4 True False False 8d ~~~ After a while: ~~~sh NAME VERSION AVAILABLE PROGRESSING DEGRADED SINCE MESSAGE authentication 4.11.4 True False False 13m baremetal 4.11.4 True False False 8d cloud-controller-manager 4.11.4 True False False 8d cloud-credential 4.11.4 True False False 8d cluster-autoscaler 4.11.4 True False False 8d config-operator 4.11.4 True False False 8d console 4.11.4 True False False 8d csi-snapshot-controller 4.11.4 True False False 8d dns 4.11.4 True False False 8d etcd 4.11.4 True False False 8d image-registry 4.11.4 True False False 8d ingress 4.11.4 True False False 8d insights 4.11.4 True False False 8d kube-apiserver 4.11.4 True False False 8d kube-controller-manager 4.11.4 True False False 8d kube-scheduler 4.11.4 True False False 8d kube-storage-version-migrator 4.11.4 True False False 8d machine-api 4.11.4 True False False 8d machine-approver 4.11.4 True False False 8d machine-config 4.11.4 True False False 8d marketplace 4.11.4 True False False 8d monitoring 4.11.4 True False False 8d network 4.11.4 True False False 8d node-tuning 4.11.4 True False False 8d openshift-apiserver 4.11.4 True False False 13m openshift-controller-manager 4.11.4 True False False 8d openshift-samples 4.11.4 True False False 8d operator-lifecycle-manager 4.11.4 True False False 8d operator-lifecycle-manager-catalog 4.11.4 True False False 8d operator-lifecycle-manager-packageserver 4.11.4 True False False 8d service-ca 4.11.4 True False False 8d storage 4.11.4 True False False 8d ~~~ ~~~sh oc -n openshift-kube-apiserver-operator get secret kube-apiserver-operator-serving-cert -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate ~~~ ~~~sh notBefore=Oct 4 17:01:27 2022 GMT notAfter=Oct 3 17:01:28 2024 GMT ~~~ The certificates have been renewed. Platform pods will be restarted, app pods may need to be restarted automatically to get the latest certificates. ~~~sh oc -n openshift-service-ca get secret signing-key -o jsonpath='{.data.tls\.crt}' | base64 -d > /tmp/serviceca.crt oc -n openshift-kube-apiserver-operator get secret kube-apiserver-operator-serving-cert -o jsonpath='{.data.tls\.crt}' | base64 -d > /tmp/servingcert.crt ~~~ ~~~sh openssl verify -verbose -CAfile /tmp/serviceca.crt /tmp/servingcert.crt ~~~ ~~~sh /tmp/servingcert.crt: OK ~~~ ## ETCD Certificates ### CA Secret ~~~sh oc -n openshift-config get secret etcd-signer -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate ~~~ ~~~sh notBefore=Sep 21 07:50:31 2022 GMT notAfter=Sep 18 07:50:32 2032 GMT ~~~ ### Client Certs ~~~sh for type in peer serving do for master in $(seq 0 2) do echo "" echo etcd-$type-openshift-master-$master oc -n openshift-etcd get secret etcd-$type-openshift-master-$master -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate done done ~~~ ~~~sh etcd-peer-openshift-master-0 notBefore=Sep 21 08:10:38 2022 GMT notAfter=Sep 20 08:10:39 2025 GMT etcd-peer-openshift-master-1 notBefore=Sep 21 08:10:38 2022 GMT notAfter=Sep 20 08:10:39 2025 GMT etcd-peer-openshift-master-2 notBefore=Sep 21 08:12:58 2022 GMT notAfter=Sep 20 08:12:59 2025 GMT etcd-serving-openshift-master-0 notBefore=Sep 21 08:10:38 2022 GMT notAfter=Sep 20 08:10:39 2025 GMT etcd-serving-openshift-master-1 notBefore=Sep 21 08:10:38 2022 GMT notAfter=Sep 20 08:10:39 2025 GMT etcd-serving-openshift-master-2 notBefore=Sep 21 08:12:58 2022 GMT notAfter=Sep 20 08:12:59 2025 GMT ~~~ ~~~sh for namespace in openshift-apiserver openshift-config openshift-etcd openshift-etcd-operator openshift-kube-apiserver openshift-oauth-apiserver do echo "" echo "$namespace/etcd-client" oc -n $namespace get secret etcd-client -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate done ~~~ ### CA Rotate ~~~sh oc -n openshift-config delete secret etcd-signer ~~~ At this point I'd expect the etcd-cluster-operator to recreate the cert but that's not the case. I opened an [RFE](https://issues.redhat.com/browse/RFE-3276) to get this fixed as it seems [it's not implemented](https://coreos.slack.com/archives/C027U68LP/p1660380716360259). ## ETCD Metrics Certificates ### CA Secret ~~~sh oc -n openshift-config get secret etcd-metric-signer -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate ~~~ ~~~sh notBefore=Sep 21 07:50:31 2022 GMT notAfter=Sep 18 07:50:32 2032 GMT ~~~ ### Client Certs ~~~sh for master in $(seq 0 2) do echo "" echo etcd-serving-metrics-openshift-master-$master oc -n openshift-etcd get secret etcd-serving-metrics-openshift-master-$master -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate done ~~~ ~~~sh etcd-serving-metrics-openshift-master-0 notBefore=Sep 21 08:10:38 2022 GMT notAfter=Sep 20 08:10:39 2025 GMT etcd-serving-metrics-openshift-master-1 notBefore=Sep 21 08:10:39 2022 GMT notAfter=Sep 20 08:10:40 2025 GMT etcd-serving-metrics-openshift-master-2 notBefore=Sep 21 08:12:59 2022 GMT notAfter=Sep 20 08:13:00 2025 GMT ~~~ ### CA Rotate ~~~sh oc -n openshift-config delete secret etcd-metric-signer ~~~ At this point I'd expect the etcd-cluster-operator to recreate the cert but that's not the case. I opened an [RFE](https://issues.redhat.com/browse/RFE-3276) to get this fixed as it seems [it's not implemented](https://coreos.slack.com/archives/C027U68LP/p1660380716360259). ## Kube API Server Client Certificates ### CA Secrets ~~~sh echo "openshift-kube-apiserver-operator/kube-apiserver-to-kubelet-signer" && oc -n openshift-kube-apiserver-operator get secret kube-apiserver-to-kubelet-signer -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate echo "openshift-kube-apiserver-operator/kube-control-plane-signer" && oc -n openshift-kube-apiserver-operator get secret kube-control-plane-signer -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate echo "openshift-kube-apiserver-operator/node-system-admin-signer" && oc -n openshift-kube-apiserver-operator get secret node-system-admin-signer -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate echo "openshift-kube-controller-manager-operator/csr-signer-signer" && oc -n openshift-kube-controller-manager-operator get secret csr-signer-signer -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate echo "openshift-kube-controller-manager-operator/csr-signer" && oc -n openshift-kube-controller-manager-operator get secret csr-signer -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate echo "openshift-kube-controller-manager/csr-signer" && oc -n openshift-kube-controller-manager get secret csr-signer -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate ~~~ ~~~sh openshift-kube-apiserver-operator/kube-apiserver-to-kubelet-signer notBefore=Sep 30 09:27:33 2022 GMT notAfter=Sep 30 09:27:34 2023 GMT openshift-kube-apiserver-operator/kube-control-plane-signer notBefore=Sep 30 09:27:33 2022 GMT notAfter=Nov 29 09:27:34 2022 GMT openshift-kube-apiserver-operator/node-system-admin-signer notBefore=Sep 30 09:27:34 2022 GMT notAfter=Sep 30 09:27:35 2023 GMT openshift-kube-controller-manager-operator/csr-signer-signer notBefore=Sep 30 09:27:34 2022 GMT notAfter=Nov 29 09:27:35 2022 GMT openshift-kube-controller-manager-operator/csr-signer notBefore=Sep 30 09:27:34 2022 GMT notAfter=Oct 30 09:27:35 2022 GMT openshift-kube-controller-manager/csr-signer notBefore=Sep 30 09:27:34 2022 GMT notAfter=Oct 30 09:27:35 2022 GMT ~~~ ### Client Certs ~~~sh echo "openshift-kube-apiserver-operator/node-system-admin-client" && oc -n openshift-kube-apiserver-operator get secret node-system-admin-client -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate echo "openshift-kube-apiserver/control-plane-node-admin-client-cert-key" && oc -n openshift-kube-apiserver get secret control-plane-node-admin-client-cert-key -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate echo "openshift-kube-apiserver/kubelet-client" && oc -n openshift-kube-apiserver get secret kubelet-client -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate echo "openshift-kube-apiserver/check-endpoints-client-cert-key" && oc -n openshift-kube-apiserver get secret check-endpoints-client-cert-key -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate echo "openshift-kube-controller-manager/kube-controller-manager-client-cert-key" && oc -n openshift-kube-controller-manager get secret kube-controller-manager-client-cert-key -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate echo "openshift-config-managed/kube-controller-manager-client-cert-key" && oc -n openshift-config-managed get secret kube-controller-manager-client-cert-key -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate echo "openshift-config-managed/kube-scheduler-client-cert-key" && oc -n openshift-config-managed get secret kube-scheduler-client-cert-key -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate echo "openshift-kube-scheduler/kube-scheduler-client-cert-key" && oc -n openshift-kube-scheduler get secret kube-scheduler-client-cert-key -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate echo "openshift-monitoring/metrics-client-certs" && oc -n openshift-monitoring get secret metrics-client-certs -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate echo "openshift-oauth-apiserver/openshift-authenticator-certs" && oc -n openshift-oauth-apiserver get secret openshift-authenticator-certs -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate ~~~ ~~~sh openshift-kube-apiserver-operator/node-system-admin-client notBefore=Sep 30 09:50:33 2022 GMT notAfter=Jan 28 09:50:34 2023 GMT openshift-kube-apiserver/control-plane-node-admin-client-cert-key notBefore=Sep 30 09:50:34 2022 GMT notAfter=Oct 30 09:50:35 2022 GMT openshift-kube-apiserver/kubelet-client notBefore=Sep 30 09:50:34 2022 GMT notAfter=Oct 30 09:50:35 2022 GMT openshift-kube-apiserver/check-endpoints-client-cert-key notBefore=Sep 30 09:50:34 2022 GMT notAfter=Oct 30 09:50:35 2022 GMT openshift-kube-controller-manager/kube-controller-manager-client-cert-key notBefore=Sep 30 09:50:34 2022 GMT notAfter=Oct 30 09:50:35 2022 GMT openshift-config-managed/kube-controller-manager-client-cert-key notBefore=Sep 30 09:50:34 2022 GMT notAfter=Oct 30 09:50:35 2022 GMT openshift-config-managed/kube-scheduler-client-cert-key notBefore=Sep 30 09:50:34 2022 GMT notAfter=Oct 30 09:50:35 2022 GMT notBefore=Sep 30 09:50:34 2022 GMT notAfter=Oct 30 09:50:35 2022 GMT openshift-monitoring/metrics-client-certs notBefore=Sep 30 09:45:35 2022 GMT notAfter=Oct 30 09:27:35 2022 GMT openshift-oauth-apiserver/openshift-authenticator-certs notBefore=Sep 30 09:45:36 2022 GMT notAfter=Oct 30 09:27:35 2022 GMT ~~~ ### CA Rotate ~~~sh oc -n openshift-kube-apiserver-operator delete secret kube-apiserver-to-kubelet-signer oc -n openshift-kube-apiserver-operator delete secret kube-control-plane-signer oc -n openshift-kube-apiserver-operator delete secret node-system-admin-signer oc -n openshift-kube-controller-manager-operator delete secret csr-signer-signer oc -n openshift-kube-controller-manager-operator delete secret csr-signer oc -n openshift-kube-controller-manager delete secret csr-signer ~~~ ~~~sh echo "openshift-kube-apiserver-operator/kube-apiserver-to-kubelet-signer" && oc -n openshift-kube-apiserver-operator get secret kube-apiserver-to-kubelet-signer -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate echo "openshift-kube-apiserver-operator/kube-control-plane-signer" && oc -n openshift-kube-apiserver-operator get secret kube-control-plane-signer -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate echo "openshift-kube-apiserver-operator/node-system-admin-signer" && oc -n openshift-kube-apiserver-operator get secret node-system-admin-signer -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate echo "openshift-kube-controller-manager-operator/csr-signer-signer" && oc -n openshift-kube-controller-manager-operator get secret csr-signer-signer -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate echo "openshift-kube-controller-manager-operator/csr-signer" && oc -n openshift-kube-controller-manager-operator get secret csr-signer -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate echo "openshift-kube-controller-manager/csr-signer" && oc -n openshift-kube-controller-manager get secret csr-signer -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate ~~~ ~~~sh openshift-kube-apiserver-operator/kube-apiserver-to-kubelet-signer notBefore=Oct 4 17:24:59 2022 GMT notAfter=Oct 4 17:25:00 2023 GMT openshift-kube-apiserver-operator/kube-control-plane-signer notBefore=Oct 4 17:25:00 2022 GMT notAfter=Dec 3 17:25:01 2022 GMT openshift-kube-apiserver-operator/node-system-admin-signer notBefore=Oct 4 17:25:00 2022 GMT notAfter=Oct 4 17:25:01 2023 GMT openshift-kube-controller-manager-operator/csr-signer-signer notBefore=Oct 4 17:25:00 2022 GMT notAfter=Dec 3 17:25:01 2022 GMT openshift-kube-controller-manager-operator/csr-signer notBefore=Oct 4 17:25:00 2022 GMT notAfter=Nov 3 17:25:01 2022 GMT openshift-kube-controller-manager/csr-signer notBefore=Oct 4 17:25:00 2022 GMT notAfter=Nov 3 17:25:01 2022 GMT ~~~ Cluster operators will roll out new revisions of the components. We need to wait until all cluster operators have stabilized. At this point, we need to rotate the certificates issued by the old CA, otherwise the trust chain will be broken: ~~~sh oc -n openshift-kube-apiserver-operator get secret kube-apiserver-to-kubelet-signer -o jsonpath='{.data.tls\.crt}' | base64 -d > /tmp/kubeletsigner.crt oc -n openshift-kube-apiserver get secret kubelet-client -o jsonpath='{.data.tls\.crt}' | base64 -d > /tmp/kubeletclient.crt ~~~ ~~~sh openssl verify -verbose -CAfile /tmp/kubeletsigner.crt /tmp/kubeletclient.crt ~~~ ~~~sh O = kube-master, CN = system:kube-apiserver error 20 at 0 depth lookup: unable to get local issuer certificate error /tmp/kubeletclient.crt: verification failed ~~~ As we explained earlier, even if the trust is broken with the new CA, the old CA is still present in the bundle. Let's force the rotation of the issued certs: ~~~sh oc -n openshift-kube-apiserver-operator delete secret node-system-admin-client oc -n openshift-kube-apiserver delete secret control-plane-node-admin-client-cert-key oc -n openshift-kube-apiserver delete secret kubelet-client oc -n openshift-kube-apiserver delete secret check-endpoints-client-cert-key oc -n openshift-kube-controller-manager delete secret kube-controller-manager-client-cert-key oc -n openshift-config-managed delete secret kube-controller-manager-client-cert-key oc -n openshift-config-managed delete secret kube-scheduler-client-cert-key oc -n openshift-kube-scheduler delete secret kube-scheduler-client-cert-key oc -n openshift-monitoring delete secret metrics-client-certs oc -n openshift-oauth-apiserver delete secret openshift-authenticator-certs ~~~ ~~~sh oc -n openshift-kube-apiserver get secret kubelet-client -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate ~~~ ~~~sh notBefore=Oct 4 17:35:08 2022 GMT notAfter=Nov 3 17:35:09 2022 GMT ~~~ ~~~sh oc -n openshift-kube-apiserver get secret kubelet-client -o jsonpath='{.data.tls\.crt}' | base64 -d > /tmp/kubeletclient.crt ~~~ ~~~sh openssl verify -verbose -CAfile /tmp/kubeletsigner.crt /tmp/kubeletclient.crt ~~~ ~~~sh /tmp/kubeletclient.crt: OK ~~~ We can check new validity periods: ~~~sh echo "openshift-kube-apiserver-operator/node-system-admin-client" && oc -n openshift-kube-apiserver-operator get secret node-system-admin-client -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate echo "openshift-kube-apiserver/control-plane-node-admin-client-cert-key" && oc -n openshift-kube-apiserver get secret control-plane-node-admin-client-cert-key -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate echo "openshift-kube-apiserver/kubelet-client" && oc -n openshift-kube-apiserver get secret kubelet-client -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate echo "openshift-kube-apiserver/check-endpoints-client-cert-key" && oc -n openshift-kube-apiserver get secret check-endpoints-client-cert-key -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate echo "openshift-kube-controller-manager/kube-controller-manager-client-cert-key" && oc -n openshift-kube-controller-manager get secret kube-controller-manager-client-cert-key -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate echo "openshift-config-managed/kube-controller-manager-client-cert-key" && oc -n openshift-config-managed get secret kube-controller-manager-client-cert-key -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate echo "openshift-config-managed/kube-scheduler-client-cert-key" && oc -n openshift-config-managed get secret kube-scheduler-client-cert-key -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate echo "openshift-kube-scheduler/kube-scheduler-client-cert-key" && oc -n openshift-kube-scheduler get secret kube-scheduler-client-cert-key -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate echo "openshift-monitoring/metrics-client-certs" && oc -n openshift-monitoring get secret metrics-client-certs -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate echo "openshift-oauth-apiserver/openshift-authenticator-certs" && oc -n openshift-oauth-apiserver get secret openshift-authenticator-certs -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate ~~~ ~~~sh openshift-kube-apiserver-operator/node-system-admin-client notBefore=Oct 4 17:35:07 2022 GMT notAfter=Feb 1 17:35:08 2023 GMT openshift-kube-apiserver/control-plane-node-admin-client-cert-key notBefore=Oct 4 17:35:07 2022 GMT notAfter=Nov 3 17:35:08 2022 GMT openshift-kube-apiserver/kubelet-client notBefore=Oct 4 17:35:08 2022 GMT notAfter=Nov 3 17:35:09 2022 GMT openshift-kube-apiserver/check-endpoints-client-cert-key notBefore=Oct 4 17:35:07 2022 GMT notAfter=Nov 3 17:35:08 2022 GMT openshift-kube-controller-manager/kube-controller-manager-client-cert-key notBefore=Oct 4 17:35:07 2022 GMT notAfter=Nov 3 17:35:08 2022 GMT openshift-config-managed/kube-controller-manager-client-cert-key notBefore=Oct 4 17:35:07 2022 GMT notAfter=Nov 3 17:35:08 2022 GMT openshift-config-managed/kube-scheduler-client-cert-key notBefore=Oct 4 17:35:07 2022 GMT notAfter=Nov 3 17:35:08 2022 GMT openshift-kube-scheduler/kube-scheduler-client-cert-key notBefore=Oct 4 17:35:07 2022 GMT notAfter=Nov 3 17:35:08 2022 GMT openshift-monitoring/metrics-client-certs notBefore=Oct 4 17:30:09 2022 GMT notAfter=Nov 3 17:25:01 2022 GMT openshift-oauth-apiserver/openshift-authenticator-certs notBefore=Oct 4 17:30:09 2022 GMT notAfter=Nov 3 17:25:01 2022 GMT ~~~ Cluster operators will roll out new revisions of the components. We need to wait until all cluster operators have stabilized. ## Kube API Server Serving Certificates ### CA Secrets ~~~sh echo "openshift-kube-apiserver-operator/loadbalancer-serving-signer" && oc -n openshift-kube-apiserver-operator get secret loadbalancer-serving-signer -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate echo "openshift-kube-apiserver-operator/localhost-serving-signer" && oc -n openshift-kube-apiserver-operator get secret localhost-serving-signer -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate echo "openshift-kube-apiserver-operator/service-network-serving-signer" && oc -n openshift-kube-apiserver-operator get secret service-network-serving-signer -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate echo "openshift-kube-apiserver-operator/localhost-recovery-serving-signer" && oc -n openshift-kube-apiserver-operator get secret localhost-recovery-serving-signer -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate echo "openshift-ingress-operator/router-ca" && oc -n openshift-ingress-operator get secret router-ca -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate ~~~ ~~~sh openshift-kube-apiserver-operator/loadbalancer-serving-signer notBefore=Oct 3 07:44:52 2022 GMT notAfter=Sep 30 07:44:53 2032 GMT openshift-kube-apiserver-operator/localhost-serving-signer notBefore=Oct 3 07:44:52 2022 GMT notAfter=Sep 30 07:44:53 2032 GMT openshift-kube-apiserver-operator/service-network-serving-signer notBefore=Oct 3 07:44:53 2022 GMT notAfter=Sep 30 07:44:54 2032 GMT openshift-kube-apiserver-operator/localhost-recovery-serving-signer notBefore=Oct 3 07:44:53 2022 GMT notAfter=Sep 30 07:44:54 2032 GMT openshift-ingress-operator/router-ca notBefore=Oct 3 07:47:26 2022 GMT notAfter=Oct 2 07:47:27 2024 GMT ~~~ ### Client Certs ~~~sh echo "openshift-kube-apiserver/localhost-recovery-serving-certkey" && oc -n openshift-kube-apiserver get secret localhost-recovery-serving-certkey -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate echo "openshift-kube-apiserver/internal-loadbalancer-serving-certkey" && oc -n openshift-kube-apiserver get secret internal-loadbalancer-serving-certkey -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate echo "openshift-kube-apiserver/external-loadbalancer-serving-certkey" && oc -n openshift-kube-apiserver get secret external-loadbalancer-serving-certkey -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate echo "openshift-kube-apiserver/localhost-serving-cert-certkey" && oc -n openshift-kube-apiserver get secret localhost-serving-cert-certkey -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate echo "openshift-kube-apiserver/service-network-serving-certkey" && oc -n openshift-kube-apiserver get secret service-network-serving-certkey -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate echo "openshift-ingress/router-certs-default" && oc -n openshift-ingress get secret router-certs-default -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate ~~~ ~~~sh openshift-kube-apiserver/localhost-recovery-serving-certkey notBefore=Oct 3 08:12:28 2022 GMT notAfter=Sep 30 07:44:54 2032 GMT openshift-kube-apiserver/internal-loadbalancer-serving-certkey notBefore=Oct 3 08:12:28 2022 GMT notAfter=Nov 2 08:12:29 2022 GMT openshift-kube-apiserver/external-loadbalancer-serving-certkey notBefore=Oct 3 08:12:28 2022 GMT notAfter=Nov 2 08:12:29 2022 GMT openshift-kube-apiserver/localhost-serving-cert-certkey notBefore=Oct 3 08:12:28 2022 GMT notAfter=Nov 2 08:12:29 2022 GMT openshift-kube-apiserver/service-network-serving-certkey notBefore=Oct 3 08:12:29 2022 GMT notAfter=Nov 2 08:12:30 2022 GMT openshift-ingress/router-certs-default notBefore=Oct 3 09:50:35 2022 GMT notAfter=Oct 2 09:50:36 2024 GMT ~~~ ### CA Rotate ~~~sh oc -n openshift-kube-apiserver-operator delete secret loadbalancer-serving-signer oc -n openshift-kube-apiserver-operator delete secret localhost-serving-signer oc -n openshift-kube-apiserver-operator delete secret service-network-serving-signer oc -n openshift-kube-apiserver-operator delete secret localhost-recovery-serving-signer oc -n openshift-ingress-operator delete secret router-ca ~~~ Cluster operators will roll out new revisions of the components. We need to wait until all cluster operators have stabilized. > :warning: `openshift-ingress-operator/router-ca` secret may take a few moments to get created. ~~~sh echo "openshift-kube-apiserver-operator/loadbalancer-serving-signer" && oc -n openshift-kube-apiserver-operator get secret loadbalancer-serving-signer -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate echo "openshift-kube-apiserver-operator/localhost-serving-signer" && oc -n openshift-kube-apiserver-operator get secret localhost-serving-signer -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate echo "openshift-kube-apiserver-operator/service-network-serving-signer" && oc -n openshift-kube-apiserver-operator get secret service-network-serving-signer -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate echo "openshift-kube-apiserver-operator/localhost-recovery-serving-signer" && oc -n openshift-kube-apiserver-operator get secret localhost-recovery-serving-signer -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate echo "openshift-ingress-operator/router-ca" && oc -n openshift-ingress-operator get secret router-ca -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate ~~~ ~~~sh openshift-kube-apiserver-operator/loadbalancer-serving-signer notBefore=Oct 4 17:39:59 2022 GMT notAfter=Oct 1 17:40:00 2032 GMT openshift-kube-apiserver-operator/localhost-serving-signer notBefore=Oct 4 17:39:59 2022 GMT notAfter=Oct 1 17:40:00 2032 GMT openshift-kube-apiserver-operator/service-network-serving-signer notBefore=Oct 4 17:39:59 2022 GMT notAfter=Oct 1 17:40:00 2032 GMT openshift-kube-apiserver-operator/localhost-recovery-serving-signer notBefore=Oct 4 17:39:59 2022 GMT notAfter=Oct 1 17:40:00 2032 GMT openshift-ingress-operator/router-ca notBefore=Oct 4 17:40:39 2022 GMT notAfter=Oct 3 17:40:40 2024 GMT ~~~ At this point, we need to rotate the certificates issued by the old CA, otherwise the trust chain will be broken: ~~~sh oc -n openshift-kube-apiserver-operator get secret loadbalancer-serving-signer -o jsonpath='{.data.tls\.crt}' | base64 -d > /tmp/lb-signer.crt oc -n openshift-kube-apiserver get secret internal-loadbalancer-serving-certkey -o jsonpath='{.data.tls\.crt}' | base64 -d > /tmp/internallb.crt ~~~ ~~~sh openssl verify -verbose -CAfile /tmp/lb-signer.crt /tmp/internallb.crt ~~~ ~~~sh CN = api-int.mario-ipi.e2e.bos.redhat.com error 20 at 0 depth lookup: unable to get local issuer certificate error /tmp/internallb.crt: verification failed ~~~ As we explained earlier, even if the trust is broken with the new CA, the old CA is still present in the bundle. Let's force the rotation of the issued certs: > **NOTE**: We need to use `--insecure-skip-tls-verify=true` because once delete the lb certs the cert in the kubeconfig won't be valid anymore. ~~~sh oc -n openshift-kube-apiserver delete secret localhost-recovery-serving-certkey --insecure-skip-tls-verify=true oc -n openshift-kube-apiserver delete secret internal-loadbalancer-serving-certkey --insecure-skip-tls-verify=true oc -n openshift-kube-apiserver delete secret external-loadbalancer-serving-certkey --insecure-skip-tls-verify=true oc -n openshift-kube-apiserver delete secret localhost-serving-cert-certkey --insecure-skip-tls-verify=true oc -n openshift-kube-apiserver delete secret service-network-serving-certkey --insecure-skip-tls-verify=true oc -n openshift-ingress delete secret router-certs-default --insecure-skip-tls-verify=true ~~~ We need to wait for the cluster operators to rollout new revisions. At some point, cluster operators will degraded, it's possible that nodes degrade as well. We need to fix the cluster manually. Our nodes will stop working since the CA Authority certificates in the kubeconfigs used by Kubelet on the nodes won't be valid anymore. ~~~sh oc get nodes --insecure-skip-tls-verify=true ~~~ ~~~sh NAME STATUS ROLES AGE VERSION openshift-master-0 NotReady master,worker 12d v1.24.0+b62823b openshift-master-1 NotReady master,worker 12d v1.24.0+b62823b openshift-master-2 NotReady master,worker 12d v1.24.0+b62823b ~~~ First, we fix the local kubeconfig (in our machine): ~~~sh NEW_CRT=$(oc --insecure-skip-tls-verify=true -n openshift-kube-apiserver get secret external-loadbalancer-serving-certkey -o jsonpath='{.data.tls\.crt}') sed -i.orig "s/certificate-authority-data: .*/certificate-authority-data: ${NEW_CRT}/g" /path/to/your/kubeconfig ~~~ Next, we fix the nodes: ~~~sh NEW_CRT=$(oc --insecure-skip-tls-verify=true -n openshift-kube-apiserver get secret internal-loadbalancer-serving-certkey -o jsonpath='{.data.tls\.crt}') ssh core@openshift-master-0 -- sudo sed -i.orig \"s/certificate-authority-data: .*/certificate-authority-data: ${NEW_CRT}/g\" /etc/kubernetes/kubeconfig ssh core@openshift-master-0 -- sudo sed -i.orig \"s/certificate-authority-data: .*/certificate-authority-data: ${NEW_CRT}/g\" /var/lib/kubelet/kubeconfig ssh core@openshift-master-1 -- sudo sed -i.orig \"s/certificate-authority-data: .*/certificate-authority-data: ${NEW_CRT}/g\" /etc/kubernetes/kubeconfig ssh core@openshift-master-1 -- sudo sed -i.orig \"s/certificate-authority-data: .*/certificate-authority-data: ${NEW_CRT}/g\" /var/lib/kubelet/kubeconfig ssh core@openshift-master-2 -- sudo sed -i.orig \"s/certificate-authority-data: .*/certificate-authority-data: ${NEW_CRT}/g\" /etc/kubernetes/kubeconfig ssh core@openshift-master-2 -- sudo sed -i.orig \"s/certificate-authority-data: .*/certificate-authority-data: ${NEW_CRT}/g\" /var/lib/kubelet/kubeconfig ssh core@openshift-master-0 -- sudo reboot ssh core@openshift-master-1 -- sudo reboot ssh core@openshift-master-2 -- sudo reboot ~~~ We need to wait for the cluster operators to move to a stable state. You may see new CSRs being generated, if you do, approve them: ~~~sh oc get csr ~~~ ~~~ NAME AGE SIGNERNAME REQUESTOR REQUESTEDDURATION CONDITION csr-4h5tj 75s kubernetes.io/kube-apiserver-client-kubelet system:node:openshift-master-2 <none> Pending csr-dtptv 80s kubernetes.io/kube-apiserver-client-kubelet system:node:openshift-master-1 <none> Pending csr-h5d7s 4m34s kubernetes.io/kube-apiserver-client-kubelet system:node:openshift-master-0 <none> Pending ~~~ ~~~sh oc get csr | grep Pending | awk '{print $1}' | xargs oc adm certificate approve ~~~ ~~~sh certificatesigningrequest.certificates.k8s.io/csr-4h5tj approved certificatesigningrequest.certificates.k8s.io/csr-dtptv approved certificatesigningrequest.certificates.k8s.io/csr-h5d7s approved ~~~ Once approved, nodes will get back to `Ready` state: ~~~sh oc get nodes ~~~ ~~~sh NAME STATUS ROLES AGE VERSION openshift-master-0 Ready master,worker 12d v1.24.0+b62823b openshift-master-1 Ready master,worker 12d v1.24.0+b62823b openshift-master-2 Ready master,worker 12d v1.24.0+b62823b ~~~ Now we need to wait for the cluster to stabilize, wait for all cluster operators to be ready again. We can check the new certs expiration dates. ~~~sh oc -n openshift-kube-apiserver get secret internal-loadbalancer-serving-certkey -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate ~~~ ~~~sh notBefore=Oct 4 17:43:08 2022 GMT notAfter=Nov 3 17:43:09 2022 GMT ~~~ ~~~sh oc -n openshift-kube-apiserver get secret internal-loadbalancer-serving-certkey -o jsonpath='{.data.tls\.crt}' | base64 -d > /tmp/internallb.crt ~~~ ~~~sh openssl verify -verbose -CAfile /tmp/lb-signer.crt /tmp/internallb.crt ~~~ ~~~sh /tmp/internallb.crt: OK ~~~ We can check new validity periods: ~~~sh echo "openshift-kube-apiserver/localhost-recovery-serving-certkey" && oc -n openshift-kube-apiserver get secret localhost-recovery-serving-certkey -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate echo "openshift-kube-apiserver/internal-loadbalancer-serving-certkey" && oc -n openshift-kube-apiserver get secret internal-loadbalancer-serving-certkey -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate echo "openshift-kube-apiserver/external-loadbalancer-serving-certkey" && oc -n openshift-kube-apiserver get secret external-loadbalancer-serving-certkey -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate echo "openshift-kube-apiserver/localhost-serving-cert-certkey" && oc -n openshift-kube-apiserver get secret localhost-serving-cert-certkey -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate echo "openshift-kube-apiserver/service-network-serving-certkey" && oc -n openshift-kube-apiserver get secret service-network-serving-certkey -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate echo "openshift-ingress/router-certs-default" && oc -n openshift-ingress get secret router-certs-default -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -startdate -enddate ~~~ ~~~sh openshift-kube-apiserver/localhost-recovery-serving-certkey notBefore=Oct 3 08:12:28 2022 GMT notAfter=Sep 30 07:44:54 2032 GMT openshift-kube-apiserver/internal-loadbalancer-serving-certkey notBefore=Oct 3 08:12:28 2022 GMT notAfter=Nov 2 08:12:29 2022 GMT openshift-kube-apiserver/external-loadbalancer-serving-certkey notBefore=Oct 3 08:12:28 2022 GMT notAfter=Nov 2 08:12:29 2022 GMT openshift-kube-apiserver/localhost-serving-cert-certkey notBefore=Oct 3 08:12:28 2022 GMT notAfter=Nov 2 08:12:29 2022 GMT openshift-kube-apiserver/service-network-serving-certkey notBefore=Oct 3 08:12:29 2022 GMT notAfter=Nov 2 08:12:30 2022 GMT openshift-ingress/router-certs-default notBefore=Oct 3 09:50:35 2022 GMT notAfter=Oct 2 09:50:36 2024 GMT ~~~