diff --git a/go.mod b/go.mod index c2e694c3a..c3ab5ea24 100644 --- a/go.mod +++ b/go.mod @@ -121,3 +121,5 @@ require ( sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect sigs.k8s.io/yaml v1.4.0 // indirect ) + +replace github.com/openshift/library-go => github.com/vrutkovs/library-go v0.0.0-20241125144145-91216c7c764f diff --git a/go.sum b/go.sum index 344bf46c1..7b95f9eb7 100644 --- a/go.sum +++ b/go.sum @@ -154,8 +154,6 @@ github.com/openshift/build-machinery-go v0.0.0-20241031155326-6ae126a9cb72 h1:kM github.com/openshift/build-machinery-go v0.0.0-20241031155326-6ae126a9cb72/go.mod h1:8jcm8UPtg2mCAsxfqKil1xrmRMI3a+XU2TZ9fF8A7TE= github.com/openshift/client-go v0.0.0-20241001162912-da6d55e4611f h1:FRc0bVNWprihWS0GqQWzb3dY4dkCwpOP3mDw5NwSoR4= github.com/openshift/client-go v0.0.0-20241001162912-da6d55e4611f/go.mod h1:KiZi2mJRH1TOJ3FtBDYS6YvUL30s/iIXaGSUrSa36mo= -github.com/openshift/library-go v0.0.0-20241120135057-fc703a7407c9 h1:bwIqO3LDkumwfDKTMRzixNHKUqU7yaKTTAKwENi6JOY= -github.com/openshift/library-go v0.0.0-20241120135057-fc703a7407c9/go.mod h1:9B1MYPoLtP9tqjWxcbUNVpwxy68zOH/3EIP6c31dAM0= github.com/openshift/multi-operator-manager v0.0.0-20241119235446-3c965870ef94 h1:9Z5HQo1KSQogIpQ2tDzrCk4+sDjL/xF+YXBrDP4R36k= github.com/openshift/multi-operator-manager v0.0.0-20241119235446-3c965870ef94/go.mod h1:Fn/rmcwj4bCuS11UT5TZvzONt7qTjzcd9BCSQkIwQOI= github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0= @@ -206,6 +204,8 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 h1:6fotK7otjonDflCTK0BCfls4SPy3NcCVb5dqqmbRknE= github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75/go.mod h1:KO6IkyS8Y3j8OdNO85qEYBsRPuteD+YciPomcXdrMnk= +github.com/vrutkovs/library-go v0.0.0-20241125144145-91216c7c764f h1:YmW4hFg+lOVaEAtMbSOcRerZ8TIE/cdnWNBoPD/wTfg= +github.com/vrutkovs/library-go v0.0.0-20241125144145-91216c7c764f/go.mod h1:9B1MYPoLtP9tqjWxcbUNVpwxy68zOH/3EIP6c31dAM0= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= diff --git a/pkg/controllers/webhookauthenticator/webhookauthenticator_controller.go b/pkg/controllers/webhookauthenticator/webhookauthenticator_controller.go index 938247238..eac1a955a 100644 --- a/pkg/controllers/webhookauthenticator/webhookauthenticator_controller.go +++ b/pkg/controllers/webhookauthenticator/webhookauthenticator_controller.go @@ -2,7 +2,10 @@ package webhookauthenticator import ( "context" + "crypto/tls" + "crypto/x509" "encoding/base64" + "encoding/pem" "fmt" "io/ioutil" "net" @@ -28,6 +31,7 @@ import ( configinformers "github.com/openshift/client-go/config/informers/externalversions" applyoperatorv1 "github.com/openshift/client-go/operator/applyconfigurations/operator/v1" "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/status" @@ -173,12 +177,29 @@ func (c *webhookAuthenticatorController) ensureKubeConfigSecret(ctx context.Cont kubeconfigComplete := replacer.Replace(string(kubeconfigBytes)) + _, err = tls.X509KeyPair(cert, key) + if err != nil { + return nil, fmt.Errorf("private key doesn't match the certificate of authenticator secret") + } + // extract not-before/not-after timestamps valid x509 certificate + var block *pem.Block + block, _ = pem.Decode(cert) + if block == nil || block.Type != "CERTIFICATE" || len(block.Headers) != 0 { + return nil, fmt.Errorf("invalid first block found in the certificate of authenticator secret") + } + parsedCert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + return nil, fmt.Errorf("failed to parse the certificate of authenticator secret") + } + requiredSecret := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: "webhook-authentication-integrated-oauth", Namespace: "openshift-config", Annotations: map[string]string{ - annotations.OpenShiftComponent: "apiserver-auth", + annotations.OpenShiftComponent: "apiserver-auth", + certrotation.CertificateNotBeforeAnnotation: parsedCert.NotBefore.Format(time.RFC3339), + certrotation.CertificateNotAfterAnnotation: parsedCert.NotAfter.Format(time.RFC3339), }, }, Data: map[string][]byte{ diff --git a/vendor/github.com/openshift/library-go/pkg/operator/certrotation/annotations.go b/vendor/github.com/openshift/library-go/pkg/operator/certrotation/annotations.go index 5ce9fa293..c4c81b53e 100644 --- a/vendor/github.com/openshift/library-go/pkg/operator/certrotation/annotations.go +++ b/vendor/github.com/openshift/library-go/pkg/operator/certrotation/annotations.go @@ -6,6 +6,16 @@ import ( ) const ( + // CertificateNotBeforeAnnotation contains the certificate expiration date in RFC3339 format. + CertificateNotBeforeAnnotation = "auth.openshift.io/certificate-not-before" + // CertificateNotAfterAnnotation contains the certificate expiration date in RFC3339 format. + CertificateNotAfterAnnotation = "auth.openshift.io/certificate-not-after" + // CertificateIssuer contains the common name of the certificate that signed another certificate. + CertificateIssuer = "auth.openshift.io/certificate-issuer" + // CertificateHostnames contains the hostnames used by a signer. + CertificateHostnames = "auth.openshift.io/certificate-hostnames" + // AutoRegenerateAfterOfflineExpiryAnnotation contains a link to PR and an e2e test name which verifies + // that TLS artifact is correctly regenerated after it has expired AutoRegenerateAfterOfflineExpiryAnnotation string = "certificates.openshift.io/auto-regenerate-after-offline-expiry" ) @@ -17,6 +27,10 @@ type AdditionalAnnotations struct { // AutoRegenerateAfterOfflineExpiry contains a link to PR and an e2e test name which verifies // that TLS artifact is correctly regenerated after it has expired AutoRegenerateAfterOfflineExpiry string + // NotBefore contains certificate the certificate creation date in RFC3339 format. + NotBefore string + // NotAfter contains certificate the certificate validity date in RFC3339 format. + NotAfter string } func (a AdditionalAnnotations) EnsureTLSMetadataUpdate(meta *metav1.ObjectMeta) bool { @@ -36,6 +50,14 @@ func (a AdditionalAnnotations) EnsureTLSMetadataUpdate(meta *metav1.ObjectMeta) meta.Annotations[AutoRegenerateAfterOfflineExpiryAnnotation] = a.AutoRegenerateAfterOfflineExpiry modified = true } + if len(a.NotBefore) > 0 && meta.Annotations[CertificateNotBeforeAnnotation] != a.NotBefore { + meta.Annotations[CertificateNotBeforeAnnotation] = a.NotBefore + modified = true + } + if len(a.NotAfter) > 0 && meta.Annotations[CertificateNotAfterAnnotation] != a.NotAfter { + meta.Annotations[CertificateNotAfterAnnotation] = a.NotAfter + modified = true + } return modified } diff --git a/vendor/github.com/openshift/library-go/pkg/operator/certrotation/client_cert_rotation_controller.go b/vendor/github.com/openshift/library-go/pkg/operator/certrotation/client_cert_rotation_controller.go index d8569f2c8..4b7fb1fda 100644 --- a/vendor/github.com/openshift/library-go/pkg/operator/certrotation/client_cert_rotation_controller.go +++ b/vendor/github.com/openshift/library-go/pkg/operator/certrotation/client_cert_rotation_controller.go @@ -15,14 +15,6 @@ import ( ) const ( - // CertificateNotBeforeAnnotation contains the certificate expiration date in RFC3339 format. - CertificateNotBeforeAnnotation = "auth.openshift.io/certificate-not-before" - // CertificateNotAfterAnnotation contains the certificate expiration date in RFC3339 format. - CertificateNotAfterAnnotation = "auth.openshift.io/certificate-not-after" - // CertificateIssuer contains the common name of the certificate that signed another certificate. - CertificateIssuer = "auth.openshift.io/certificate-issuer" - // CertificateHostnames contains the hostnames used by a signer. - CertificateHostnames = "auth.openshift.io/certificate-hostnames" // RunOnceContextKey is a context value key that can be used to call the controller Sync() and make it only run the syncWorker once and report error. RunOnceContextKey = "cert-rotation-controller.openshift.io/run-once" ) diff --git a/vendor/github.com/openshift/library-go/pkg/operator/certrotation/signer.go b/vendor/github.com/openshift/library-go/pkg/operator/certrotation/signer.go index 2eb761bbb..540114949 100644 --- a/vendor/github.com/openshift/library-go/pkg/operator/certrotation/signer.go +++ b/vendor/github.com/openshift/library-go/pkg/operator/certrotation/signer.go @@ -90,7 +90,7 @@ func (c RotatedSigningCASecret) EnsureSigningCertKeyPair(ctx context.Context) (* reason = "secret doesn't exist" } c.EventRecorder.Eventf("SignerUpdateRequired", "%q in %q requires a new signing cert/key pair: %v", c.Name, c.Namespace, reason) - if err := setSigningCertKeyPairSecret(signingCertKeyPairSecret, c.Validity); err != nil { + if err := setSigningCertKeyPairSecret(signingCertKeyPairSecret, c.Validity, c.AdditionalAnnotations); err != nil { return nil, false, err } @@ -194,7 +194,7 @@ func getValidityFromAnnotations(annotations map[string]string) (notBefore time.T } // setSigningCertKeyPairSecret creates a new signing cert/key pair and sets them in the secret -func setSigningCertKeyPairSecret(signingCertKeyPairSecret *corev1.Secret, validity time.Duration) error { +func setSigningCertKeyPairSecret(signingCertKeyPairSecret *corev1.Secret, validity time.Duration, annotations AdditionalAnnotations) error { signerName := fmt.Sprintf("%s_%s@%d", signingCertKeyPairSecret.Namespace, signingCertKeyPairSecret.Name, time.Now().Unix()) ca, err := crypto.MakeSelfSignedCAConfigForDuration(signerName, validity) if err != nil { @@ -215,9 +215,11 @@ func setSigningCertKeyPairSecret(signingCertKeyPairSecret *corev1.Secret, validi } signingCertKeyPairSecret.Data["tls.crt"] = certBytes.Bytes() signingCertKeyPairSecret.Data["tls.key"] = keyBytes.Bytes() - signingCertKeyPairSecret.Annotations[CertificateNotAfterAnnotation] = ca.Certs[0].NotAfter.Format(time.RFC3339) - signingCertKeyPairSecret.Annotations[CertificateNotBeforeAnnotation] = ca.Certs[0].NotBefore.Format(time.RFC3339) + annotations.NotBefore = ca.Certs[0].NotBefore.Format(time.RFC3339) + annotations.NotAfter = ca.Certs[0].NotAfter.Format(time.RFC3339) signingCertKeyPairSecret.Annotations[CertificateIssuer] = ca.Certs[0].Issuer.CommonName + _ = annotations.EnsureTLSMetadataUpdate(&signingCertKeyPairSecret.ObjectMeta) + return nil } diff --git a/vendor/github.com/openshift/library-go/pkg/operator/certrotation/target.go b/vendor/github.com/openshift/library-go/pkg/operator/certrotation/target.go index b68aea163..42b755955 100644 --- a/vendor/github.com/openshift/library-go/pkg/operator/certrotation/target.go +++ b/vendor/github.com/openshift/library-go/pkg/operator/certrotation/target.go @@ -251,8 +251,8 @@ func setTargetCertKeyPairSecret(targetCertKeyPairSecret *corev1.Secret, validity if err != nil { return err } - targetCertKeyPairSecret.Annotations[CertificateNotAfterAnnotation] = certKeyPair.Certs[0].NotAfter.Format(time.RFC3339) - targetCertKeyPairSecret.Annotations[CertificateNotBeforeAnnotation] = certKeyPair.Certs[0].NotBefore.Format(time.RFC3339) + annotations.NotBefore = certKeyPair.Certs[0].NotBefore.Format(time.RFC3339) + annotations.NotAfter = certKeyPair.Certs[0].NotAfter.Format(time.RFC3339) targetCertKeyPairSecret.Annotations[CertificateIssuer] = certKeyPair.Certs[0].Issuer.CommonName _ = annotations.EnsureTLSMetadataUpdate(&targetCertKeyPairSecret.ObjectMeta) diff --git a/vendor/github.com/openshift/library-go/pkg/operator/csr/cert_controller.go b/vendor/github.com/openshift/library-go/pkg/operator/csr/cert_controller.go index d56c88df5..ba4f1b358 100644 --- a/vendor/github.com/openshift/library-go/pkg/operator/csr/cert_controller.go +++ b/vendor/github.com/openshift/library-go/pkg/operator/csr/cert_controller.go @@ -3,7 +3,9 @@ package csr import ( "context" "crypto/tls" + "crypto/x509" "crypto/x509/pkix" + "encoding/pem" "fmt" "math/rand" "time" @@ -166,7 +168,7 @@ func (c *clientCertificateController) sync(ctx context.Context, syncCtx factory. // reconcile pending csr if exists if len(c.csrName) > 0 { - newSecretConfig, err := c.syncCSR(secret) + newSecretConfig, leaf, err := c.syncCSR(secret) if err != nil { c.reset() return err @@ -179,6 +181,12 @@ func (c *clientCertificateController) sync(ctx context.Context, syncCtx factory. newSecretConfig[k] = v } secret.Data = newSecretConfig + + // Update not-before/not-after annotations + c.AdditionalAnnotations.NotBefore = leaf.NotBefore.Format(time.RFC3339) + c.AdditionalAnnotations.NotAfter = leaf.NotAfter.Format(time.RFC3339) + _ = c.AdditionalAnnotations.EnsureTLSMetadataUpdate(&secret.ObjectMeta) + // save the changes into secret if err := c.saveSecret(secret); err != nil { return err @@ -231,10 +239,10 @@ func (c *clientCertificateController) sync(ctx context.Context, syncCtx factory. return nil } -func (c *clientCertificateController) syncCSR(secret *corev1.Secret) (map[string][]byte, error) { +func (c *clientCertificateController) syncCSR(secret *corev1.Secret) (map[string][]byte, *x509.Certificate, error) { // skip if there is no ongoing csr if len(c.csrName) == 0 { - return nil, fmt.Errorf("no ongoing csr") + return nil, nil, fmt.Errorf("no ongoing csr") } // skip if csr no longer exists @@ -244,38 +252,48 @@ func (c *clientCertificateController) syncCSR(secret *corev1.Secret) (map[string // fallback to fetching csr from hub apiserver in case it is not cached by informer yet csr, err = c.hubCSRClient.Get(context.Background(), c.csrName, metav1.GetOptions{}) if errors.IsNotFound(err) { - return nil, fmt.Errorf("unable to get csr %q. It might have already been deleted.", c.csrName) + return nil, nil, fmt.Errorf("unable to get csr %q. It might have already been deleted.", c.csrName) } case err != nil: - return nil, err + return nil, nil, err } // skip if csr is not approved yet if !isCSRApproved(csr) { - return nil, nil + return nil, nil, nil } // skip if csr has no certificate in its status yet if len(csr.Status.Certificate) == 0 { - return nil, nil + return nil, nil, nil } klog.V(4).Infof("Sync csr %v", c.csrName) // check if cert in csr status matches with the corresponding private key if c.keyData == nil { - return nil, fmt.Errorf("No private key found for certificate in csr: %s", c.csrName) + return nil, nil, fmt.Errorf("No private key found for certificate in csr: %s", c.csrName) } _, err = tls.X509KeyPair(csr.Status.Certificate, c.keyData) if err != nil { - return nil, fmt.Errorf("Private key does not match with the certificate in csr: %s", c.csrName) + return nil, nil, fmt.Errorf("Private key does not match with the certificate in csr: %s", c.csrName) + } + // verify that the recieved data is a valid x509 certificate + var block *pem.Block + block, _ = pem.Decode(csr.Status.Certificate) + if block == nil || block.Type != "CERTIFICATE" || len(block.Headers) != 0 { + return nil, nil, fmt.Errorf("invalid first block found for certificate in csr: %s", c.csrName) } + certBytes := block.Bytes + parsedCert, err := x509.ParseCertificate(certBytes) + if err != nil { + return nil, nil, fmt.Errorf("failed to parse the certificate in csr %s: %v", c.csrName, err) + } data := map[string][]byte{ TLSCertFile: csr.Status.Certificate, TLSKeyFile: c.keyData, } - - return data, nil + return data, parsedCert, nil } func (c *clientCertificateController) createCSR(ctx context.Context) (string, error) { diff --git a/vendor/modules.txt b/vendor/modules.txt index 7c97b75fa..9a2d9da4f 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -315,7 +315,7 @@ github.com/openshift/client-go/user/applyconfigurations/internal github.com/openshift/client-go/user/applyconfigurations/user/v1 github.com/openshift/client-go/user/clientset/versioned/scheme github.com/openshift/client-go/user/clientset/versioned/typed/user/v1 -# github.com/openshift/library-go v0.0.0-20241120135057-fc703a7407c9 +# github.com/openshift/library-go v0.0.0-20241120135057-fc703a7407c9 => github.com/vrutkovs/library-go v0.0.0-20241125144145-91216c7c764f ## explicit; go 1.22.0 github.com/openshift/library-go/pkg/apiserver/jsonpatch github.com/openshift/library-go/pkg/apps/deployment @@ -1482,3 +1482,4 @@ sigs.k8s.io/structured-merge-diff/v4/value ## explicit; go 1.12 sigs.k8s.io/yaml sigs.k8s.io/yaml/goyaml.v2 +# github.com/openshift/library-go => github.com/vrutkovs/library-go v0.0.0-20241125144145-91216c7c764f