From 4204f529e63dfb6fbc87c2f2285d9886eb78af89 Mon Sep 17 00:00:00 2001 From: Vadim Rutkovsky Date: Tue, 26 Nov 2024 08:50:40 +0100 Subject: [PATCH] nodekubeconfig: set not-before/not-after annotations --- .../nodekubeconfigcontroller.go | 22 ++++++ .../nodekubeconfigcontroller_test.go | 70 ++++++++++++++----- 2 files changed, 73 insertions(+), 19 deletions(-) diff --git a/pkg/operator/nodekubeconfigcontroller/nodekubeconfigcontroller.go b/pkg/operator/nodekubeconfigcontroller/nodekubeconfigcontroller.go index a98106ff5..82f36b254 100644 --- a/pkg/operator/nodekubeconfigcontroller/nodekubeconfigcontroller.go +++ b/pkg/operator/nodekubeconfigcontroller/nodekubeconfigcontroller.go @@ -2,7 +2,10 @@ package nodekubeconfigcontroller import ( "context" + "crypto/tls" + "crypto/x509" "encoding/base64" + "encoding/pem" "fmt" "strings" "time" @@ -14,6 +17,7 @@ import ( "github.com/openshift/cluster-kube-apiserver-operator/bindata" "github.com/openshift/cluster-kube-apiserver-operator/pkg/operator/operatorclient" "github.com/openshift/library-go/pkg/controller/factory" + "github.com/openshift/library-go/pkg/operator/certrotation" "github.com/openshift/library-go/pkg/operator/events" "github.com/openshift/library-go/pkg/operator/resource/resourceapply" "github.com/openshift/library-go/pkg/operator/resource/resourceread" @@ -112,6 +116,22 @@ func ensureNodeKubeconfigs(ctx context.Context, client coreclientv1.CoreV1Interf return fmt.Errorf("system:admin client private key missing from secret %s/node-system-admin-client", operatorclient.OperatorNamespace) } + // Ensure secret key matches the certificate + _, err = tls.X509KeyPair(systemAdminClientCert, systemAdminClientKey) + if err != nil { + return fmt.Errorf("system:admin client private key doesn't match the certificate from secret %s/node-system-admin-client", operatorclient.OperatorNamespace) + } + // extract not-before/not-after timestamps valid x509 certificate + var block *pem.Block + block, _ = pem.Decode(systemAdminClientCert) + if block == nil || block.Type != "CERTIFICATE" || len(block.Headers) != 0 { + return fmt.Errorf("invalid first block found for certificate from secret %s/node-system-admin-client", operatorclient.OperatorNamespace) + } + parsedCert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + return fmt.Errorf("failed to parse the certificate from secret %s/node-system-admin-client", operatorclient.OperatorNamespace) + } + servingCABundleCM, err := configmapLister.ConfigMaps(operatorclient.TargetNamespace).Get("kube-apiserver-server-ca") if err != nil { return err @@ -152,6 +172,8 @@ func ensureNodeKubeconfigs(ctx context.Context, client coreclientv1.CoreV1Interf requiredSecret.Annotations = map[string]string{} } requiredSecret.Annotations[annotations.OpenShiftComponent] = "kube-apiserver" + requiredSecret.Annotations[certrotation.CertificateNotBeforeAnnotation] = parsedCert.NotBefore.Format(time.RFC3339) + requiredSecret.Annotations[certrotation.CertificateNotAfterAnnotation] = parsedCert.NotAfter.Format(time.RFC3339) _, _, err = resourceapply.ApplySecret(ctx, client, recorder, requiredSecret) if err != nil { diff --git a/pkg/operator/nodekubeconfigcontroller/nodekubeconfigcontroller_test.go b/pkg/operator/nodekubeconfigcontroller/nodekubeconfigcontroller_test.go index cdca5acbd..97a91b086 100644 --- a/pkg/operator/nodekubeconfigcontroller/nodekubeconfigcontroller_test.go +++ b/pkg/operator/nodekubeconfigcontroller/nodekubeconfigcontroller_test.go @@ -2,12 +2,15 @@ package nodekubeconfigcontroller import ( "context" + "encoding/base64" + "fmt" "testing" "github.com/google/go-cmp/cmp" "github.com/openshift/api/annotations" configv1 "github.com/openshift/api/config/v1" configlistersv1 "github.com/openshift/client-go/config/listers/config/v1" + "github.com/openshift/library-go/pkg/operator/certrotation" "github.com/openshift/library-go/pkg/operator/events" corev1 "k8s.io/api/core/v1" apiequality "k8s.io/apimachinery/pkg/api/equality" @@ -85,7 +88,34 @@ func (l *secretLister) Get(name string) (*corev1.Secret, error) { return l.client.CoreV1().Secrets(l.namespace).Get(context.Background(), name, metav1.GetOptions{}) } +const privateKey = ` +-----BEGIN PRIVATE KEY----- +MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEArvkpSCWaStPfbYr4 +cCJyv8pXWnJ4K22emSrYDNcp7Dm6qjtN/lsVNuGDyWyR4cUaJYXkaD2OrZiXDzzk +BZlS3QIDAQABAkA9BZhoGPUec5XQVk8ejGUIjkC4woM2YhyVvmNq1v8/6q6V+uPw +yDEfBMapuLVY+QhyVELXFOCHA5iKxrlFHZThAiEA1XA5mlbHtrJqEZ7yI5m6+Szj +7YVzSkdSgfDZ//heAh8CIQDR3VbN9QmJRIM1yhIkP9BoWSxvXdH6QMXdC2X7Tkwj +gwIgcpbSxjLK/CIjYhx0oXpacIaSRCX+dKV//XVChPNh/T8CIQCSFscXZez2fhfs +eLb6PuXfzbuN5ryFvVM/VXDvaIi96wIgcHjUpONghaoA51XejMAxWanDiwAgRV5H +XNdFkBi4q7o= +-----END PRIVATE KEY-----` +const publicKey = `-----BEGIN CERTIFICATE----- +MIIBfzCCASmgAwIBAgIUEEUHu1PzqJCGQ63vxVokwBxGPYwwDQYJKoZIhvcNAQEL +BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI0MTEyNjA4NTA0NloXDTM0MTEy +NDA4NTA0NlowFDESMBAGA1UEAwwJbG9jYWxob3N0MFwwDQYJKoZIhvcNAQEBBQAD +SwAwSAJBAK75KUglmkrT322K+HAicr/KV1pyeCttnpkq2AzXKew5uqo7Tf5bFTbh +g8lskeHFGiWF5Gg9jq2Ylw885AWZUt0CAwEAAaNTMFEwHQYDVR0OBBYEFJna5Io+ +idLKO73zypGl2itp92JUMB8GA1UdIwQYMBaAFJna5Io+idLKO73zypGl2itp92JU +MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADQQB71tlkWNFDvMRxtz+a +NYMU1thAVfVFciNXPS07tUduFSwVvYORUxx2w+5JfUdKu69hLpBFVPqvHQjPoQgc +vUBI +-----END CERTIFICATE-----` +const certNotBefore = "2024-11-26T08:50:46Z" +const certNotAfter = "2034-11-24T08:50:46Z" + func TestEnsureNodeKubeconfigs(t *testing.T) { + publicKeyBase64 := base64.StdEncoding.EncodeToString([]byte(publicKey)) + privateKeyBase64 := base64.StdEncoding.EncodeToString([]byte(privateKey)) tt := []struct { name string existingObjects []runtime.Object @@ -111,8 +141,8 @@ func TestEnsureNodeKubeconfigs(t *testing.T) { Name: "node-system-admin-client", }, Data: map[string][]byte{ - "tls.crt": []byte("system:admin certificate"), - "tls.key": []byte("system:admin key"), + "tls.crt": []byte(publicKey), + "tls.key": []byte(privateKey), }, }, }, @@ -143,11 +173,13 @@ func TestEnsureNodeKubeconfigs(t *testing.T) { Namespace: "openshift-kube-apiserver", Name: "node-kubeconfigs", Annotations: map[string]string{ - annotations.OpenShiftComponent: "kube-apiserver", + annotations.OpenShiftComponent: "kube-apiserver", + certrotation.CertificateNotBeforeAnnotation: certNotBefore, + certrotation.CertificateNotAfterAnnotation: certNotAfter, }, }, Data: map[string][]byte{ - "localhost.kubeconfig": []byte(`apiVersion: v1 + "localhost.kubeconfig": []byte(fmt.Sprintf(`apiVersion: v1 kind: Config clusters: - cluster: @@ -163,10 +195,10 @@ current-context: system:admin users: - name: system:admin user: - client-certificate-data: c3lzdGVtOmFkbWluIGNlcnRpZmljYXRl - client-key-data: c3lzdGVtOmFkbWluIGtleQ== -`), - "localhost-recovery.kubeconfig": []byte(`apiVersion: v1 + client-certificate-data: %s + client-key-data: %s +`, publicKeyBase64, privateKeyBase64)), + "localhost-recovery.kubeconfig": []byte(fmt.Sprintf(`apiVersion: v1 kind: Config clusters: - cluster: @@ -183,10 +215,10 @@ current-context: system:admin users: - name: system:admin user: - client-certificate-data: c3lzdGVtOmFkbWluIGNlcnRpZmljYXRl - client-key-data: c3lzdGVtOmFkbWluIGtleQ== -`), - "lb-ext.kubeconfig": []byte(`apiVersion: v1 + client-certificate-data: %s + client-key-data: %s +`, publicKeyBase64, privateKeyBase64)), + "lb-ext.kubeconfig": []byte(fmt.Sprintf(`apiVersion: v1 kind: Config clusters: - cluster: @@ -202,10 +234,10 @@ current-context: system:admin users: - name: system:admin user: - client-certificate-data: c3lzdGVtOmFkbWluIGNlcnRpZmljYXRl - client-key-data: c3lzdGVtOmFkbWluIGtleQ== -`), - "lb-int.kubeconfig": []byte(`apiVersion: v1 + client-certificate-data: %s + client-key-data: %s +`, publicKeyBase64, privateKeyBase64)), + "lb-int.kubeconfig": []byte(fmt.Sprintf(`apiVersion: v1 kind: Config clusters: - cluster: @@ -221,9 +253,9 @@ current-context: system:admin users: - name: system:admin user: - client-certificate-data: c3lzdGVtOmFkbWluIGNlcnRpZmljYXRl - client-key-data: c3lzdGVtOmFkbWluIGtleQ== -`), + client-certificate-data: %s + client-key-data: %s +`, publicKeyBase64, privateKeyBase64)), }, }, },