diff --git a/manifests/03-rbac-role-ns-openshift-ingress-operator.yaml b/manifests/03-rbac-role-ns-openshift-ingress-operator.yaml deleted file mode 100644 index dd6d3f5e9..000000000 --- a/manifests/03-rbac-role-ns-openshift-ingress-operator.yaml +++ /dev/null @@ -1,22 +0,0 @@ -kind: Role -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: console-operator - namespace: openshift-ingress-operator - annotations: - include.release.openshift.io/hypershift: "true" - include.release.openshift.io/ibm-cloud-managed: "true" - include.release.openshift.io/self-managed-high-availability: "true" - include.release.openshift.io/single-node-developer: "true" - capability.openshift.io/name: Console -rules: - - apiGroups: - - "" - resources: - - secrets - resourceNames: - - router-ca - verbs: - - get - - list - - watch diff --git a/manifests/04-rbac-rolebinding.yaml b/manifests/04-rbac-rolebinding.yaml index 1189337d8..5360f279b 100644 --- a/manifests/04-rbac-rolebinding.yaml +++ b/manifests/04-rbac-rolebinding.yaml @@ -79,26 +79,6 @@ subjects: --- kind: RoleBinding apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: console-operator - namespace: openshift-ingress-operator - annotations: - include.release.openshift.io/hypershift: "true" - include.release.openshift.io/ibm-cloud-managed: "true" - include.release.openshift.io/self-managed-high-availability: "true" - include.release.openshift.io/single-node-developer: "true" - capability.openshift.io/name: Console -roleRef: - kind: Role - name: console-operator - apiGroup: rbac.authorization.k8s.io -subjects: - - kind: ServiceAccount - name: console-operator - namespace: openshift-console-operator ---- -kind: RoleBinding -apiVersion: rbac.authorization.k8s.io/v1 metadata: name: console-operator namespace: openshift-config diff --git a/pkg/api/api.go b/pkg/api/api.go index 77a20b14f..5a10033d1 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -9,7 +9,6 @@ const ( ConsoleContainerPort = 443 ConsoleContainerPortName = "https" ConsoleContainerTargetPort = 8443 - ConsoleHTTP2CertSecretName = "console-http2-cert" ConsoleServingCertName = "console-serving-cert" DefaultIngressCertConfigMapName = "default-ingress-cert" DownloadsPort = 8080 @@ -55,7 +54,6 @@ const ( // ingress instance named "default" is the OOTB ingresscontroller // this is an implicit stable API DefaultIngressController = "default" - IngressCASecretName = "router-ca" IngressControllerNamespace = "openshift-ingress-operator" OAuthClientName = OpenShiftConsoleName diff --git a/pkg/console/controllers/route/controller.go b/pkg/console/controllers/route/controller.go index 044628e85..83c2d63e5 100644 --- a/pkg/console/controllers/route/controller.go +++ b/pkg/console/controllers/route/controller.go @@ -13,7 +13,6 @@ import ( "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/util/sets" coreinformersv1 "k8s.io/client-go/informers/core/v1" - corev1client "k8s.io/client-go/kubernetes/typed/core/v1" corev1listers "k8s.io/client-go/listers/core/v1" "k8s.io/klog/v2" @@ -29,7 +28,6 @@ import ( routesinformersv1 "github.com/openshift/client-go/route/informers/externalversions/route/v1" routev1listers "github.com/openshift/client-go/route/listers/route/v1" "github.com/openshift/library-go/pkg/controller/factory" - libcrypto "github.com/openshift/library-go/pkg/crypto" "github.com/openshift/library-go/pkg/operator/events" "github.com/openshift/library-go/pkg/operator/v1helpers" "github.com/openshift/library-go/pkg/route/routeapihelpers" @@ -48,13 +46,10 @@ type RouteSyncController struct { operatorClient v1helpers.OperatorClient routeClient routeclientv1.RoutesGetter routeLister routev1listers.RouteLister - secretClient corev1client.SecretsGetter operatorConfigLister operatorv1listers.ConsoleLister ingressConfigLister configlistersv1.IngressLister ingressControllerLister operatorv1listers.IngressControllerLister secretLister corev1listers.SecretLister - consoleSecretLister corev1listers.SecretLister - ingressCASecretLister corev1listers.SecretLister infrastructureConfigLister configlistersv1.InfrastructureLister clusterVersionLister configlistersv1.ClusterVersionLister } @@ -67,14 +62,11 @@ func NewRouteSyncController( // clients operatorClient v1helpers.OperatorClient, routev1Client routeclientv1.RoutesGetter, - secretClient corev1client.SecretsGetter, // informers operatorConfigInformer v1.ConsoleInformer, ingressControllerInformer v1.IngressControllerInformer, secretInformer coreinformersv1.SecretInformer, - consoleSecretInformer coreinformersv1.SecretInformer, routeInformer routesinformersv1.RouteInformer, - ingressCASecretInformer coreinformersv1.SecretInformer, // events recorder events.Recorder, ) factory.Controller { @@ -87,21 +79,14 @@ func NewRouteSyncController( ingressControllerLister: ingressControllerInformer.Lister(), routeClient: routev1Client, routeLister: routeInformer.Lister(), - secretClient: secretClient, secretLister: secretInformer.Lister(), infrastructureConfigLister: configInformer.Config().V1().Infrastructures().Lister(), clusterVersionLister: configInformer.Config().V1().ClusterVersions().Lister(), } - if consoleSecretInformer != nil { - ctrl.consoleSecretLister = consoleSecretInformer.Lister() - } - if ingressCASecretInformer != nil { - ctrl.ingressCASecretLister = ingressCASecretInformer.Lister() - } configV1Informers := configInformer.Config().V1() - controllerBuilder := factory.New(). + return factory.New(). WithFilteredEventsInformers( // configs util.IncludeNamesFilter(api.ConfigResourceName), configV1Informers.Consoles().Informer(), @@ -114,22 +99,7 @@ func NewRouteSyncController( ingressControllerInformer.Informer(), ).WithInformers( // routes — watch all routes in namespace for additional route discovery routeInformer.Informer(), - ).ResyncEvery(time.Minute).WithSync(ctrl.Sync) - - if consoleSecretInformer != nil { - controllerBuilder = controllerBuilder.WithFilteredEventsInformers( - util.IncludeNamesFilter(api.ConsoleHTTP2CertSecretName), - consoleSecretInformer.Informer(), - ) - } - if ingressCASecretInformer != nil { - controllerBuilder = controllerBuilder.WithFilteredEventsInformers( - util.IncludeNamesFilter(api.IngressCASecretName), - ingressCASecretInformer.Informer(), - ) - } - - return controllerBuilder. + ).ResyncEvery(time.Minute).WithSync(ctrl.Sync). ToController(fmt.Sprintf("%sRouteController", strings.Title(routeName)), recorder.WithComponentSuffix(fmt.Sprintf("%s-route-controller", routeName))) } @@ -151,11 +121,6 @@ func (c *RouteSyncController) Sync(ctx context.Context, controllerContext factor if err = c.removeRoute(ctx, routesub.GetCustomRouteName(c.routeName)); err != nil { return err } - if c.routeName == api.OpenShiftConsoleRouteName { - if err := c.removeHTTP2CertSecret(ctx); err != nil { - return err - } - } return c.removeRoute(ctx, c.routeName) default: return fmt.Errorf("unknown state: %v", updatedOperatorConfig.Spec.ManagementState) @@ -240,17 +205,6 @@ func (c *RouteSyncController) removeRoute(ctx context.Context, routeName string) return err } -func (c *RouteSyncController) removeHTTP2CertSecret(ctx context.Context) error { - if c.secretClient == nil { - return nil - } - err := c.secretClient.Secrets(api.OpenShiftConsoleNamespace).Delete(ctx, api.ConsoleHTTP2CertSecretName, metav1.DeleteOptions{}) - if apierrors.IsNotFound(err) { - return nil - } - return err -} - func (c *RouteSyncController) SyncDefaultRoute(ctx context.Context, routeConfig *routesub.RouteConfig, ingressConfig *configv1.Ingress, controllerContext factory.SyncContext) (*routev1.Route, string, error) { customTLSSecret, configErr := c.GetDefaultRouteTLSSecret(ctx, routeConfig) if configErr != nil { @@ -261,16 +215,6 @@ func (c *RouteSyncController) SyncDefaultRoute(ctx context.Context, routeConfig return nil, "InvalidCustomTLSSecret", secretValidationErr } - if customTLSCert == nil && c.routeName == api.OpenShiftConsoleRouteName && c.secretClient != nil && c.consoleSecretLister != nil { - hostname := routesub.GetDefaultRouteHost(c.routeName, ingressConfig) - ca := c.loadIngressCA() - http2Cert, err := routesub.EnsureHTTP2Cert(ctx, c.secretClient, c.consoleSecretLister, hostname, ca) - if err != nil { - return nil, "FailedHTTP2Cert", err - } - customTLSCert = http2Cert - } - requiredDefaultRoute := routeConfig.DefaultRoute(customTLSCert, ingressConfig) defaultRoute, _, defaultRouteError := routesub.ApplyRoute(c.routeClient, requiredDefaultRoute) @@ -380,31 +324,11 @@ func (c *RouteSyncController) ValidateCustomRouteConfig(ctx context.Context, rou return nil } -// loadIngressCA attempts to load the ingress controller's CA for signing the -// HTTP/2 cert. Returns nil if unavailable (falls back to self-signed). -func (c *RouteSyncController) loadIngressCA() *libcrypto.CA { - if c.ingressCASecretLister == nil { - return nil - } - secret, err := c.ingressCASecretLister.Secrets(api.IngressControllerNamespace).Get(api.IngressCASecretName) - if err != nil { - if apierrors.IsNotFound(err) { - klog.V(4).Infof("ingress CA secret %s/%s not found, falling back to self-signed HTTP/2 cert", api.IngressControllerNamespace, api.IngressCASecretName) - } else { - klog.Warningf("failed to get ingress CA secret %s/%s, falling back to self-signed HTTP/2 cert: %v", api.IngressControllerNamespace, api.IngressCASecretName, err) - } - return nil - } - ca, err := routesub.LoadCAFromSecret(secret) - if err != nil { - klog.Warningf("failed to parse ingress CA secret %s/%s, falling back to self-signed HTTP/2 cert: %v", api.IngressControllerNamespace, api.IngressCASecretName, err) - return nil - } - return ca -} - -// ValidateCustomCertSecret validates the TLS certificate and key in a Secret. -// Returns the parsed cert/key pair, or nil if the secret is nil. +// Validate secret that holds custom TLS certificate and key. +// Secret has to contain `tls.crt` and `tls.key` data keys +// where the certificate and key are stored and both need +// to be in valid format. +// Return the custom TLS certificate and key func ValidateCustomCertSecret(customCertSecret *corev1.Secret) (*routesub.CustomTLSCert, error) { if customCertSecret == nil { return nil, nil diff --git a/pkg/console/controllers/route/controller_test.go b/pkg/console/controllers/route/controller_test.go index c9147466f..7b9bec000 100644 --- a/pkg/console/controllers/route/controller_test.go +++ b/pkg/console/controllers/route/controller_test.go @@ -1,26 +1,18 @@ package route import ( - "context" "crypto/tls" "crypto/x509" "fmt" "testing" - "time" "github.com/go-test/deep" // k8s corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - kubefake "k8s.io/client-go/kubernetes/fake" - corev1listers "k8s.io/client-go/listers/core/v1" - "k8s.io/client-go/tools/cache" // console-operator - "github.com/openshift/console-operator/pkg/api" routesub "github.com/openshift/console-operator/pkg/console/subresource/route" - "github.com/openshift/library-go/pkg/crypto" ) const ( @@ -208,126 +200,3 @@ func TestValidateCustomCertSecret(t *testing.T) { }) } } - -func TestRemoveHTTP2CertSecret(t *testing.T) { - t.Run("secret exists and is deleted", func(t *testing.T) { - existingSecret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: api.ConsoleHTTP2CertSecretName, - Namespace: api.OpenShiftConsoleNamespace, - }, - Type: corev1.SecretTypeTLS, - } - fakeClient := kubefake.NewSimpleClientset(existingSecret) - ctrl := &RouteSyncController{secretClient: fakeClient.CoreV1()} - - err := ctrl.removeHTTP2CertSecret(context.Background()) - if err != nil { - t.Fatalf("expected no error, got: %v", err) - } - - _, getErr := fakeClient.CoreV1().Secrets(api.OpenShiftConsoleNamespace).Get(context.Background(), api.ConsoleHTTP2CertSecretName, metav1.GetOptions{}) - if getErr == nil { - t.Error("expected secret to be deleted") - } - }) - - t.Run("secret does not exist", func(t *testing.T) { - fakeClient := kubefake.NewSimpleClientset() - ctrl := &RouteSyncController{secretClient: fakeClient.CoreV1()} - - err := ctrl.removeHTTP2CertSecret(context.Background()) - if err != nil { - t.Fatalf("expected no error for non-existent secret, got: %v", err) - } - }) - - t.Run("secretClient is nil", func(t *testing.T) { - ctrl := &RouteSyncController{secretClient: nil} - - err := ctrl.removeHTTP2CertSecret(context.Background()) - if err != nil { - t.Fatalf("expected no error when secretClient is nil, got: %v", err) - } - }) -} - -func TestLoadIngressCA(t *testing.T) { - t.Run("ingressCASecretLister is nil", func(t *testing.T) { - ctrl := &RouteSyncController{ingressCASecretLister: nil} - ca := ctrl.loadIngressCA() - if ca != nil { - t.Error("expected nil CA when lister is nil") - } - }) - - t.Run("secret not found", func(t *testing.T) { - lister := newControllerFakeSecretLister(t) - ctrl := &RouteSyncController{ingressCASecretLister: lister} - ca := ctrl.loadIngressCA() - if ca != nil { - t.Error("expected nil CA when secret not found") - } - }) - - t.Run("secret has invalid PEM", func(t *testing.T) { - secret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: api.IngressCASecretName, - Namespace: api.IngressControllerNamespace, - }, - Data: map[string][]byte{ - "tls.crt": []byte("not-valid-pem"), - "tls.key": []byte("not-valid-pem"), - }, - } - lister := newControllerFakeSecretLister(t, secret) - ctrl := &RouteSyncController{ingressCASecretLister: lister} - ca := ctrl.loadIngressCA() - if ca != nil { - t.Error("expected nil CA for invalid PEM") - } - }) - - t.Run("secret has valid CA", func(t *testing.T) { - caConfig, err := crypto.MakeSelfSignedCAConfigForDuration("test-ingress-ca", 24*time.Hour) - if err != nil { - t.Fatalf("failed to create test CA: %v", err) - } - certPEM, keyPEM, err := caConfig.GetPEMBytes() - if err != nil { - t.Fatalf("failed to get PEM bytes: %v", err) - } - - secret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: api.IngressCASecretName, - Namespace: api.IngressControllerNamespace, - }, - Data: map[string][]byte{ - "tls.crt": certPEM, - "tls.key": keyPEM, - }, - } - lister := newControllerFakeSecretLister(t, secret) - ctrl := &RouteSyncController{ingressCASecretLister: lister} - ca := ctrl.loadIngressCA() - if ca == nil { - t.Fatal("expected non-nil CA") - } - if ca.Config.Certs[0].Subject.CommonName != "test-ingress-ca" { - t.Errorf("expected CN=test-ingress-ca, got CN=%s", ca.Config.Certs[0].Subject.CommonName) - } - }) -} - -func newControllerFakeSecretLister(t *testing.T, secrets ...*corev1.Secret) corev1listers.SecretLister { - t.Helper() - indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}) - for _, s := range secrets { - if err := indexer.Add(s.DeepCopy()); err != nil { - t.Fatalf("failed to add secret to indexer: %v", err) - } - } - return corev1listers.NewSecretLister(indexer) -} diff --git a/pkg/console/starter/starter.go b/pkg/console/starter/starter.go index e14ad6cdb..0edd2552f 100644 --- a/pkg/console/starter/starter.go +++ b/pkg/console/starter/starter.go @@ -13,7 +13,6 @@ import ( apiexensionsinformers "k8s.io/apiextensions-apiserver/pkg/client/informers/externalversions" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/dynamic" "k8s.io/client-go/informers" @@ -152,15 +151,6 @@ func RunOperator(ctx context.Context, controllerContext *controllercmd.Controlle informers.WithNamespace(api.OpenShiftConsoleOperatorNamespace), ) - kubeInformersIngressOperatorNamespaced := informers.NewSharedInformerFactoryWithOptions( - kubeClient, - resync, - informers.WithNamespace(api.IngressControllerNamespace), - informers.WithTweakListOptions(func(opts *metav1.ListOptions) { - opts.FieldSelector = fields.OneTermEqualSelector("metadata.name", api.IngressCASecretName).String() - }), - ) - kubeInformersMonitoringNamespaced := informers.NewSharedInformerFactoryWithOptions( kubeClient, resync, @@ -434,14 +424,11 @@ func RunOperator(ctx context.Context, controllerContext *controllercmd.Controlle // clients operatorClient, routesClient.RouteV1(), - kubeClient.CoreV1(), // route operatorConfigInformers.Operator().V1().Consoles(), operatorConfigInformers.Operator().V1().IngressControllers(), kubeInformersConfigNamespaced.Core().V1().Secrets(), // `openshift-config` namespace informers - kubeInformersNamespaced.Core().V1().Secrets(), // `openshift-console` namespace informers (for HTTP/2 cert) routesInformersNamespaced.Route().V1().Routes(), - kubeInformersIngressOperatorNamespaced.Core().V1().Secrets(), // `openshift-ingress-operator` namespace informers (for router-ca) // events recorder, ) @@ -455,14 +442,11 @@ func RunOperator(ctx context.Context, controllerContext *controllercmd.Controlle // clients operatorClient, routesClient.RouteV1(), - nil, // no secret client needed for downloads route // route operatorConfigInformers.Operator().V1().Consoles(), operatorConfigInformers.Operator().V1().IngressControllers(), kubeInformersConfigNamespaced.Core().V1().Secrets(), // `openshift-config` namespace informers - nil, // no console secret lister needed for downloads route routesInformersNamespaced.Route().V1().Routes(), - nil, // no ingress CA needed for downloads route // events recorder, ) @@ -661,7 +645,6 @@ func RunOperator(ctx context.Context, controllerContext *controllercmd.Controlle kubeInformersManagedNamespaced, kubeInformersMonitoringNamespaced, kubeInformersOperatorConfigNamespaced, - kubeInformersIngressOperatorNamespaced, resourceSyncerInformers, operatorConfigInformers, consoleInformers, diff --git a/pkg/console/subresource/route/http2.go b/pkg/console/subresource/route/http2.go deleted file mode 100644 index a2e5fd8db..000000000 --- a/pkg/console/subresource/route/http2.go +++ /dev/null @@ -1,176 +0,0 @@ -package route - -import ( - "bytes" - "context" - "crypto/x509" - "encoding/pem" - "fmt" - "time" - - corev1 "k8s.io/api/core/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/util/sets" - corev1client "k8s.io/client-go/kubernetes/typed/core/v1" - corev1listers "k8s.io/client-go/listers/core/v1" - "k8s.io/klog/v2" - - "github.com/openshift/console-operator/pkg/api" - "github.com/openshift/library-go/pkg/crypto" -) - -const ( - http2CertValidity = 365 * 24 * time.Hour - http2CertRenewalBuffer = 30 * 24 * time.Hour -) - -// GenerateHTTP2Cert creates a TLS certificate for the given hostname to enable -// HTTP/2 on the console route. The cert's only purpose is to be unique in the -// router's cert_config.map so that per-cert ALPN negotiation kicks in. -// -// If ca is provided, the cert is signed by that CA (the ingress controller's -// CA, so the trust chain matches the wildcard cert). Otherwise a self-signed -// CA is created and used. -func GenerateHTTP2Cert(hostname string, ca *crypto.CA) (*CustomTLSCert, error) { - if ca == nil { - caConfig, err := crypto.MakeSelfSignedCAConfigForDuration("console-http2-ca", http2CertValidity) - if err != nil { - return nil, fmt.Errorf("failed to create self-signed CA: %w", err) - } - ca = &crypto.CA{ - SerialGenerator: &crypto.RandomSerialGenerator{}, - Config: caConfig, - } - } - - certConfig, err := ca.MakeServerCertForDuration(sets.New(hostname), http2CertValidity) - if err != nil { - return nil, fmt.Errorf("failed to create server certificate: %w", err) - } - - certPEM, keyPEM, err := certConfig.GetPEMBytes() - if err != nil { - return nil, fmt.Errorf("failed to encode certificate PEM: %w", err) - } - - return &CustomTLSCert{ - Certificate: string(certPEM), - Key: string(keyPEM), - }, nil -} - -// EnsureHTTP2Cert returns a TLS cert for HTTP/2 enablement, creating or -// regenerating one as needed. The cert is persisted in a Secret in the -// openshift-console namespace so it survives operator restarts without -// triggering unnecessary route updates. -func EnsureHTTP2Cert(ctx context.Context, secretClient corev1client.SecretsGetter, secretLister corev1listers.SecretLister, hostname string, ca *crypto.CA) (*CustomTLSCert, error) { - existing, listerErr := secretLister.Secrets(api.OpenShiftConsoleNamespace).Get(api.ConsoleHTTP2CertSecretName) - if listerErr != nil && !apierrors.IsNotFound(listerErr) { - return nil, fmt.Errorf("failed to get HTTP/2 cert secret: %w", listerErr) - } - - if listerErr == nil { - if cert, valid := validHTTP2Cert(existing, hostname, ca); valid { - return cert, nil - } - klog.V(4).Info("HTTP/2 cert needs regeneration") - } - - newCert, err := GenerateHTTP2Cert(hostname, ca) - if err != nil { - return nil, err - } - - secret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: api.ConsoleHTTP2CertSecretName, - Namespace: api.OpenShiftConsoleNamespace, - Labels: map[string]string{ - "app": "console", - }, - }, - Type: corev1.SecretTypeTLS, - Data: map[string][]byte{ - "tls.crt": []byte(newCert.Certificate), - "tls.key": []byte(newCert.Key), - }, - } - - if apierrors.IsNotFound(listerErr) { - klog.V(2).Infof("Creating HTTP/2 cert secret %s/%s", api.OpenShiftConsoleNamespace, api.ConsoleHTTP2CertSecretName) - _, err = secretClient.Secrets(api.OpenShiftConsoleNamespace).Create(ctx, secret, metav1.CreateOptions{}) - } else { - secret.ResourceVersion = existing.ResourceVersion - klog.V(2).Infof("Updating HTTP/2 cert secret %s/%s", api.OpenShiftConsoleNamespace, api.ConsoleHTTP2CertSecretName) - _, err = secretClient.Secrets(api.OpenShiftConsoleNamespace).Update(ctx, secret, metav1.UpdateOptions{}) - } - if err != nil { - return nil, fmt.Errorf("failed to persist HTTP/2 cert secret: %w", err) - } - - return newCert, nil -} - -// LoadCAFromSecret parses a kubernetes.io/tls Secret into a crypto.CA, -// suitable for signing serving certs. -func LoadCAFromSecret(secret *corev1.Secret) (*crypto.CA, error) { - certPEM, ok := secret.Data["tls.crt"] - if !ok { - return nil, fmt.Errorf("secret missing tls.crt") - } - keyPEM, ok := secret.Data["tls.key"] - if !ok { - return nil, fmt.Errorf("secret missing tls.key") - } - return crypto.GetCAFromBytes(certPEM, keyPEM) -} - -// validHTTP2Cert checks whether the cert in the Secret is still usable: -// not expired (with 30-day buffer), hostname matches, and issuer matches -// the current CA (verified cryptographically via SubjectKeyId). -func validHTTP2Cert(secret *corev1.Secret, hostname string, ca *crypto.CA) (*CustomTLSCert, bool) { - certPEM, ok := secret.Data["tls.crt"] - if !ok { - return nil, false - } - keyPEM, ok := secret.Data["tls.key"] - if !ok { - return nil, false - } - - block, _ := pem.Decode(certPEM) - if block == nil { - return nil, false - } - cert, err := x509.ParseCertificate(block.Bytes) - if err != nil { - return nil, false - } - - if time.Now().Add(http2CertRenewalBuffer).After(cert.NotAfter) { - return nil, false - } - if time.Now().Before(cert.NotBefore) { - return nil, false - } - - if err := cert.VerifyHostname(hostname); err != nil { - return nil, false - } - - if ca != nil && len(ca.Config.Certs) > 0 { - if !bytes.Equal(cert.AuthorityKeyId, ca.Config.Certs[0].SubjectKeyId) { - return nil, false - } - } - - if privateKeyVerifier(keyPEM) != nil { - return nil, false - } - - return &CustomTLSCert{ - Certificate: string(certPEM), - Key: string(keyPEM), - }, true -} diff --git a/pkg/console/subresource/route/http2_test.go b/pkg/console/subresource/route/http2_test.go deleted file mode 100644 index 47e15147c..000000000 --- a/pkg/console/subresource/route/http2_test.go +++ /dev/null @@ -1,487 +0,0 @@ -package route - -import ( - "context" - "crypto/x509" - "encoding/pem" - "testing" - "time" - - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/util/sets" - kubefake "k8s.io/client-go/kubernetes/fake" - corev1listers "k8s.io/client-go/listers/core/v1" - "k8s.io/client-go/tools/cache" - - "github.com/openshift/console-operator/pkg/api" - "github.com/openshift/library-go/pkg/crypto" -) - -func TestGenerateHTTP2CertSelfSigned(t *testing.T) { - hostname := "console-openshift-console.apps.example.com" - cert, err := GenerateHTTP2Cert(hostname, nil) - if err != nil { - t.Fatalf("GenerateHTTP2Cert() error: %v", err) - } - if cert.Certificate == "" { - t.Fatal("expected non-empty certificate") - } - if cert.Key == "" { - t.Fatal("expected non-empty key") - } - - parsed := parseCert(t, cert.Certificate) - - if err := parsed.VerifyHostname(hostname); err != nil { - t.Errorf("certificate does not verify for hostname %q: %v", hostname, err) - } - if time.Now().After(parsed.NotAfter) { - t.Error("certificate is already expired") - } - if time.Now().Before(parsed.NotBefore) { - t.Error("certificate is not yet valid") - } - - if privateKeyVerifier([]byte(cert.Key)) != nil { - t.Fatal("key is not a valid private key") - } -} - -func TestGenerateHTTP2CertWithCA(t *testing.T) { - ca := makeTestCA(t) - hostname := "console-openshift-console.apps.example.com" - - cert, err := GenerateHTTP2Cert(hostname, ca) - if err != nil { - t.Fatalf("GenerateHTTP2Cert() error: %v", err) - } - - parsed := parseCert(t, cert.Certificate) - - if err := parsed.VerifyHostname(hostname); err != nil { - t.Errorf("certificate does not verify for hostname %q: %v", hostname, err) - } - - roots := x509.NewCertPool() - roots.AddCert(ca.Config.Certs[0]) - if _, err := parsed.Verify(x509.VerifyOptions{Roots: roots}); err != nil { - t.Errorf("certificate does not chain to CA: %v", err) - } -} - -func TestValidHTTP2Cert(t *testing.T) { - hostname := "console-openshift-console.apps.example.com" - - cert, err := GenerateHTTP2Cert(hostname, nil) - if err != nil { - t.Fatalf("GenerateHTTP2Cert() error: %v", err) - } - secret := makeSecretFromCert(cert) - - tests := []struct { - name string - secret *corev1.Secret - hostname string - ca *crypto.CA - wantValid bool - }{ - { - name: "valid cert", - secret: secret, - hostname: hostname, - wantValid: true, - }, - { - name: "wrong hostname", - secret: secret, - hostname: "other.apps.example.com", - wantValid: false, - }, - { - name: "CA mismatch via SubjectKeyId", - secret: secret, - hostname: hostname, - ca: makeTestCA(t), - wantValid: false, - }, - { - name: "missing tls.crt", - secret: &corev1.Secret{Data: map[string][]byte{"tls.key": []byte("data")}}, - hostname: hostname, - wantValid: false, - }, - { - name: "missing tls.key", - secret: &corev1.Secret{Data: map[string][]byte{"tls.crt": []byte("data")}}, - hostname: hostname, - wantValid: false, - }, - { - name: "invalid PEM", - secret: &corev1.Secret{Data: map[string][]byte{"tls.crt": []byte("not-pem"), "tls.key": []byte("not-pem")}}, - hostname: hostname, - wantValid: false, - }, - { - name: "corrupt key", - secret: &corev1.Secret{Data: map[string][]byte{ - "tls.crt": []byte(cert.Certificate), - "tls.key": []byte("-----BEGIN RSA PRIVATE KEY-----\nbaddata\n-----END RSA PRIVATE KEY-----\n"), - }}, - hostname: hostname, - wantValid: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result, valid := validHTTP2Cert(tt.secret, tt.hostname, tt.ca) - if valid != tt.wantValid { - t.Errorf("validHTTP2Cert() valid = %v, want %v", valid, tt.wantValid) - } - if tt.wantValid && result == nil { - t.Error("expected non-nil cert when valid") - } - }) - } - - t.Run("CA signed cert validates with same CA", func(t *testing.T) { - ca := makeTestCA(t) - caCert, err := GenerateHTTP2Cert(hostname, ca) - if err != nil { - t.Fatalf("GenerateHTTP2Cert() error: %v", err) - } - caSecret := makeSecretFromCert(caCert) - result, valid := validHTTP2Cert(caSecret, hostname, ca) - if !valid { - t.Error("expected CA-signed cert to be valid with same CA") - } - if result == nil { - t.Error("expected non-nil cert") - } - }) -} - -func TestEnsureHTTP2Cert_CreatePath(t *testing.T) { - hostname := "console-openshift-console.apps.example.com" - fakeClient := kubefake.NewSimpleClientset() - lister := newFakeSecretLister(t) - - cert, err := EnsureHTTP2Cert(context.Background(), fakeClient.CoreV1(), lister, hostname, nil) - if err != nil { - t.Fatalf("EnsureHTTP2Cert() error: %v", err) - } - if cert == nil { - t.Fatal("expected non-nil cert") - } - if cert.Certificate == "" || cert.Key == "" { - t.Fatal("expected non-empty cert and key") - } - - created, err := fakeClient.CoreV1().Secrets(api.OpenShiftConsoleNamespace).Get(context.Background(), api.ConsoleHTTP2CertSecretName, metav1.GetOptions{}) - if err != nil { - t.Fatalf("expected secret to be created: %v", err) - } - if created.Type != corev1.SecretTypeTLS { - t.Errorf("expected secret type %s, got %s", corev1.SecretTypeTLS, created.Type) - } - if len(created.Data["tls.crt"]) == 0 || len(created.Data["tls.key"]) == 0 { - t.Error("expected non-empty cert data in secret") - } - if created.Labels["app"] != "console" { - t.Errorf("expected app=console label, got %v", created.Labels) - } -} - -func TestEnsureHTTP2Cert_UpdatePath(t *testing.T) { - hostname := "console-openshift-console.apps.example.com" - - existingSecret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: api.ConsoleHTTP2CertSecretName, - Namespace: api.OpenShiftConsoleNamespace, - ResourceVersion: "123", - }, - Type: corev1.SecretTypeTLS, - Data: map[string][]byte{ - "tls.crt": []byte("old-invalid-cert"), - "tls.key": []byte("old-invalid-key"), - }, - } - - fakeClient := kubefake.NewSimpleClientset(existingSecret) - lister := newFakeSecretLister(t, existingSecret) - - cert, err := EnsureHTTP2Cert(context.Background(), fakeClient.CoreV1(), lister, hostname, nil) - if err != nil { - t.Fatalf("EnsureHTTP2Cert() error: %v", err) - } - if cert == nil { - t.Fatal("expected non-nil cert") - } - - updated, err := fakeClient.CoreV1().Secrets(api.OpenShiftConsoleNamespace).Get(context.Background(), api.ConsoleHTTP2CertSecretName, metav1.GetOptions{}) - if err != nil { - t.Fatalf("expected secret to exist: %v", err) - } - if string(updated.Data["tls.crt"]) == "old-invalid-cert" { - t.Error("expected secret to be updated with new cert") - } - - parseCert(t, string(updated.Data["tls.crt"])) -} - -func TestEnsureHTTP2Cert_ExistingValidCert(t *testing.T) { - hostname := "console-openshift-console.apps.example.com" - - validCert, err := GenerateHTTP2Cert(hostname, nil) - if err != nil { - t.Fatalf("GenerateHTTP2Cert() error: %v", err) - } - - existingSecret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: api.ConsoleHTTP2CertSecretName, - Namespace: api.OpenShiftConsoleNamespace, - ResourceVersion: "123", - }, - Type: corev1.SecretTypeTLS, - Data: map[string][]byte{ - "tls.crt": []byte(validCert.Certificate), - "tls.key": []byte(validCert.Key), - }, - } - - fakeClient := kubefake.NewSimpleClientset(existingSecret) - lister := newFakeSecretLister(t, existingSecret) - - cert, err := EnsureHTTP2Cert(context.Background(), fakeClient.CoreV1(), lister, hostname, nil) - if err != nil { - t.Fatalf("EnsureHTTP2Cert() error: %v", err) - } - if cert.Certificate != validCert.Certificate { - t.Error("expected to return existing valid cert, not generate a new one") - } - if cert.Key != validCert.Key { - t.Error("expected to return existing valid key, not generate a new one") - } -} - -func TestLoadCAFromSecret(t *testing.T) { - t.Run("valid CA secret", func(t *testing.T) { - ca := makeTestCA(t) - certPEM, keyPEM, err := ca.Config.GetPEMBytes() - if err != nil { - t.Fatalf("failed to get CA PEM bytes: %v", err) - } - secret := makeSecretFromCert(&CustomTLSCert{ - Certificate: string(certPEM), - Key: string(keyPEM), - }) - loaded, err := LoadCAFromSecret(secret) - if err != nil { - t.Fatalf("LoadCAFromSecret() error: %v", err) - } - if loaded.Config.Certs[0].Subject.CommonName != "test-ca" { - t.Errorf("expected CN=test-ca, got CN=%s", loaded.Config.Certs[0].Subject.CommonName) - } - }) - - t.Run("missing tls.crt", func(t *testing.T) { - secret := &corev1.Secret{Data: map[string][]byte{"tls.key": []byte("data")}} - _, err := LoadCAFromSecret(secret) - if err == nil { - t.Error("expected error for missing tls.crt") - } - }) - - t.Run("missing tls.key", func(t *testing.T) { - secret := &corev1.Secret{Data: map[string][]byte{"tls.crt": []byte("data")}} - _, err := LoadCAFromSecret(secret) - if err == nil { - t.Error("expected error for missing tls.key") - } - }) - - t.Run("invalid PEM data", func(t *testing.T) { - secret := &corev1.Secret{Data: map[string][]byte{ - "tls.crt": []byte("not-valid-pem"), - "tls.key": []byte("not-valid-pem"), - }} - _, err := LoadCAFromSecret(secret) - if err == nil { - t.Error("expected error for invalid PEM") - } - }) -} - -func TestValidHTTP2Cert_ExpiredCert(t *testing.T) { - hostname := "console-openshift-console.apps.example.com" - ca := makeTestCA(t) - - certConfig, err := ca.MakeServerCertForDuration(sets.New(hostname), 29*24*time.Hour) - if err != nil { - t.Fatalf("MakeServerCertForDuration() error: %v", err) - } - certPEM, keyPEM, err := certConfig.GetPEMBytes() - if err != nil { - t.Fatalf("GetPEMBytes() error: %v", err) - } - secret := &corev1.Secret{ - Data: map[string][]byte{ - "tls.crt": certPEM, - "tls.key": keyPEM, - }, - } - - _, valid := validHTTP2Cert(secret, hostname, nil) - if valid { - t.Error("expected cert within 30-day renewal buffer to be invalid") - } -} - -func TestEnsureHTTP2Cert_CARotation(t *testing.T) { - hostname := "console-openshift-console.apps.example.com" - ca1 := makeTestCA(t) - ca2 := makeTestCA(t) - - certSignedByCA1, err := GenerateHTTP2Cert(hostname, ca1) - if err != nil { - t.Fatalf("GenerateHTTP2Cert() error: %v", err) - } - - existingSecret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: api.ConsoleHTTP2CertSecretName, - Namespace: api.OpenShiftConsoleNamespace, - ResourceVersion: "100", - }, - Type: corev1.SecretTypeTLS, - Data: map[string][]byte{ - "tls.crt": []byte(certSignedByCA1.Certificate), - "tls.key": []byte(certSignedByCA1.Key), - }, - } - - fakeClient := kubefake.NewSimpleClientset(existingSecret) - lister := newFakeSecretLister(t, existingSecret) - - newCert, err := EnsureHTTP2Cert(context.Background(), fakeClient.CoreV1(), lister, hostname, ca2) - if err != nil { - t.Fatalf("EnsureHTTP2Cert() error: %v", err) - } - if newCert.Certificate == certSignedByCA1.Certificate { - t.Error("expected cert to be regenerated when CA changed") - } - - parsed := parseCert(t, newCert.Certificate) - roots := x509.NewCertPool() - roots.AddCert(ca2.Config.Certs[0]) - if _, err := parsed.Verify(x509.VerifyOptions{Roots: roots}); err != nil { - t.Errorf("new cert does not chain to CA-2: %v", err) - } - - updated, err := fakeClient.CoreV1().Secrets(api.OpenShiftConsoleNamespace).Get(context.Background(), api.ConsoleHTTP2CertSecretName, metav1.GetOptions{}) - if err != nil { - t.Fatalf("expected secret to exist: %v", err) - } - if string(updated.Data["tls.crt"]) == certSignedByCA1.Certificate { - t.Error("expected secret to be updated with new cert") - } -} - -func TestEnsureHTTP2Cert_HostnameChange(t *testing.T) { - hostnameA := "console-a.apps.example.com" - hostnameB := "console-b.apps.example.com" - - certForA, err := GenerateHTTP2Cert(hostnameA, nil) - if err != nil { - t.Fatalf("GenerateHTTP2Cert() error: %v", err) - } - - existingSecret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: api.ConsoleHTTP2CertSecretName, - Namespace: api.OpenShiftConsoleNamespace, - ResourceVersion: "100", - }, - Type: corev1.SecretTypeTLS, - Data: map[string][]byte{ - "tls.crt": []byte(certForA.Certificate), - "tls.key": []byte(certForA.Key), - }, - } - - fakeClient := kubefake.NewSimpleClientset(existingSecret) - lister := newFakeSecretLister(t, existingSecret) - - newCert, err := EnsureHTTP2Cert(context.Background(), fakeClient.CoreV1(), lister, hostnameB, nil) - if err != nil { - t.Fatalf("EnsureHTTP2Cert() error: %v", err) - } - if newCert.Certificate == certForA.Certificate { - t.Error("expected cert to be regenerated for new hostname") - } - - parsed := parseCert(t, newCert.Certificate) - if err := parsed.VerifyHostname(hostnameB); err != nil { - t.Errorf("new cert does not verify for hostname %q: %v", hostnameB, err) - } - if err := parsed.VerifyHostname(hostnameA); err == nil { - t.Error("new cert should not verify for old hostname") - } - - updated, err := fakeClient.CoreV1().Secrets(api.OpenShiftConsoleNamespace).Get(context.Background(), api.ConsoleHTTP2CertSecretName, metav1.GetOptions{}) - if err != nil { - t.Fatalf("expected secret to exist: %v", err) - } - if string(updated.Data["tls.crt"]) == certForA.Certificate { - t.Error("expected secret to be updated with new cert") - } -} - -func makeTestCA(t *testing.T) *crypto.CA { - t.Helper() - caConfig, err := crypto.MakeSelfSignedCAConfigForDuration("test-ca", 24*time.Hour) - if err != nil { - t.Fatalf("failed to create test CA: %v", err) - } - return &crypto.CA{ - SerialGenerator: &crypto.RandomSerialGenerator{}, - Config: caConfig, - } -} - -func makeSecretFromCert(cert *CustomTLSCert) *corev1.Secret { - return &corev1.Secret{ - Data: map[string][]byte{ - "tls.crt": []byte(cert.Certificate), - "tls.key": []byte(cert.Key), - }, - } -} - -func newFakeSecretLister(t *testing.T, secrets ...*corev1.Secret) corev1listers.SecretLister { - t.Helper() - indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}) - for _, s := range secrets { - if err := indexer.Add(s.DeepCopy()); err != nil { - t.Fatalf("failed to add secret to indexer: %v", err) - } - } - return corev1listers.NewSecretLister(indexer) -} - -func parseCert(t *testing.T, certPEM string) *x509.Certificate { - t.Helper() - block, _ := pem.Decode([]byte(certPEM)) - if block == nil { - t.Fatal("failed to decode certificate PEM") - } - cert, err := x509.ParseCertificate(block.Bytes) - if err != nil { - t.Fatalf("failed to parse certificate: %v", err) - } - return cert -} diff --git a/test/e2e/custom_url_test.go b/test/e2e/custom_url_test.go index 252dfa4e4..6e330634a 100644 --- a/test/e2e/custom_url_test.go +++ b/test/e2e/custom_url_test.go @@ -435,19 +435,10 @@ func checkCustomTLSWasUnset(t *testing.T, client *framework.ClientSet, routeName if err != nil { return true, err } - if route.Spec.TLS == nil { - return true, nil - } - // For the console route, the operator sets a throwaway HTTP/2 cert - // when no admin cert is configured, so TLS fields will be non-empty. - // Verify the admin cert was removed by checking it changed. - if route.Spec.TLS.Certificate != adminCert { - return true, nil - } - // For routes without HTTP/2 cert (e.g. downloads), empty TLS is expected. if len(route.Spec.TLS.Certificate) == 0 && len(route.Spec.TLS.Key) == 0 { return true, nil } + return false, nil }) if err != nil { diff --git a/test/e2e/framework/framework.go b/test/e2e/framework/framework.go index 6435f1829..966c372d8 100644 --- a/test/e2e/framework/framework.go +++ b/test/e2e/framework/framework.go @@ -50,7 +50,6 @@ func getTestingResources() []TestingResource { {"Deployment", consoleapi.OpenShiftConsoleDeploymentName, consoleapi.OpenShiftConsoleNamespace}, {"Deployment", consoleapi.OpenShiftConsoleDownloadsDeploymentName, consoleapi.OpenShiftConsoleNamespace}, {"Route", consoleapi.OpenShiftConsoleRouteName, consoleapi.OpenShiftConsoleNamespace}, - {"Secret", consoleapi.ConsoleHTTP2CertSecretName, consoleapi.OpenShiftConsoleNamespace}, {"Service", consoleapi.OpenShiftConsoleServiceName, consoleapi.OpenShiftConsoleNamespace}, {"PodDisruptionBudget", consoleapi.OpenShiftConsoleName, consoleapi.OpenShiftConsoleNamespace}, {"PodDisruptionBudget", consoleapi.DownloadsResourceName, consoleapi.OpenShiftConsoleNamespace}, @@ -92,8 +91,6 @@ func GetResource(client *ClientSet, resource TestingResource) (runtime.Object, e switch resource.kind { case "ConfigMap": res, err = client.Core.ConfigMaps(resource.namespace).Get(context.TODO(), resource.name, metav1.GetOptions{}) - case "Secret": - res, err = client.Core.Secrets(resource.namespace).Get(context.TODO(), resource.name, metav1.GetOptions{}) case "Service": res, err = client.Core.Services(resource.namespace).Get(context.TODO(), resource.name, metav1.GetOptions{}) case "Route": @@ -148,8 +145,6 @@ func deleteResource(client *ClientSet, resource TestingResource) error { switch resource.kind { case "ConfigMap": err = client.Core.ConfigMaps(resource.namespace).Delete(context.TODO(), resource.name, metav1.DeleteOptions{}) - case "Secret": - err = client.Core.Secrets(resource.namespace).Delete(context.TODO(), resource.name, metav1.DeleteOptions{}) case "Service": err = client.Core.Services(resource.namespace).Delete(context.TODO(), resource.name, metav1.DeleteOptions{}) case "Route": diff --git a/test/e2e/http2_cert_test.go b/test/e2e/http2_cert_test.go deleted file mode 100644 index 31328bda4..000000000 --- a/test/e2e/http2_cert_test.go +++ /dev/null @@ -1,157 +0,0 @@ -package e2e - -import ( - "context" - "testing" - "time" - - corev1 "k8s.io/api/core/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/util/wait" - - api "github.com/openshift/console-operator/pkg/api" - "github.com/openshift/console-operator/test/e2e/framework" -) - -func TestHTTP2CertAutoGeneration(t *testing.T) { - client, _ := framework.StandardSetup(t) - defer framework.StandardCleanup(t, client) - - var secret *corev1.Secret - err := wait.Poll(1*time.Second, framework.AsyncOperationTimeout, func() (bool, error) { - var getErr error - secret, getErr = client.Core.Secrets(api.OpenShiftConsoleNamespace).Get(context.TODO(), api.ConsoleHTTP2CertSecretName, metav1.GetOptions{}) - if getErr != nil { - if apierrors.IsNotFound(getErr) { - return false, nil - } - return false, getErr - } - return true, nil - }) - if err != nil { - t.Fatalf("Verify that the operator created the HTTP/2 cert secret: %v", err) - } - - if secret.Type != corev1.SecretTypeTLS { - t.Errorf("expected secret type %s, got %s", corev1.SecretTypeTLS, secret.Type) - } - if len(secret.Data["tls.crt"]) == 0 { - t.Error("expected non-empty tls.crt in HTTP/2 cert secret") - } - if len(secret.Data["tls.key"]) == 0 { - t.Error("expected non-empty tls.key in HTTP/2 cert secret") - } - - route, err := client.Routes.Routes(api.OpenShiftConsoleNamespace).Get(context.TODO(), api.OpenShiftConsoleRouteName, metav1.GetOptions{}) - if err != nil { - t.Fatalf("could not get console route: %v", err) - } - if route.Spec.TLS == nil || len(route.Spec.TLS.Certificate) == 0 || len(route.Spec.TLS.Key) == 0 { - t.Error("expected the console route to have the auto-generated HTTP/2 TLS cert and key set") - } -} - -func TestHTTP2CertRegeneration(t *testing.T) { - client, _ := framework.StandardSetup(t) - defer framework.StandardCleanup(t, client) - - err := wait.Poll(1*time.Second, framework.AsyncOperationTimeout, func() (bool, error) { - _, getErr := client.Core.Secrets(api.OpenShiftConsoleNamespace).Get(context.TODO(), api.ConsoleHTTP2CertSecretName, metav1.GetOptions{}) - if getErr != nil { - if apierrors.IsNotFound(getErr) { - return false, nil - } - return false, getErr - } - return true, nil - }) - if err != nil { - t.Fatalf("HTTP/2 cert secret was not created initially: %v", err) - } - - t.Log("Deleting the HTTP/2 cert secret to verify that the operator regenerates it") - err = client.Core.Secrets(api.OpenShiftConsoleNamespace).Delete(context.TODO(), api.ConsoleHTTP2CertSecretName, metav1.DeleteOptions{}) - if err != nil { - t.Fatalf("could not delete HTTP/2 cert secret: %v", err) - } - - err = wait.Poll(1*time.Second, framework.AsyncOperationTimeout, func() (bool, error) { - _, getErr := client.Core.Secrets(api.OpenShiftConsoleNamespace).Get(context.TODO(), api.ConsoleHTTP2CertSecretName, metav1.GetOptions{}) - if getErr != nil { - if apierrors.IsNotFound(getErr) { - return false, nil - } - return false, getErr - } - return true, nil - }) - if err != nil { - t.Fatalf("HTTP/2 cert secret was not regenerated after deletion: %v", err) - } - - route, err := client.Routes.Routes(api.OpenShiftConsoleNamespace).Get(context.TODO(), api.OpenShiftConsoleRouteName, metav1.GetOptions{}) - if err != nil { - t.Fatalf("could not get console route: %v", err) - } - if route.Spec.TLS == nil || len(route.Spec.TLS.Certificate) == 0 { - t.Error("expected the console route to have a TLS cert set after regeneration") - } -} - -func TestHTTP2CertAdminOverride(t *testing.T) { - client, _ := framework.StandardSetup(t) - defer cleanupCustomURLTestCase(t, client) - - tlsSecretName := "http2-test-custom-tls" - componentRouteSpec := getComponentRouteSpec(t, client, api.OpenShiftConsoleRouteName, tlsSecretName, api.OpenShiftConsoleRouteName) - createTLSSecret(t, client, tlsSecretName, string(componentRouteSpec.Hostname)) - defer func() { - _ = client.Core.Secrets(api.OpenShiftConfigNamespace).Delete(context.TODO(), tlsSecretName, metav1.DeleteOptions{}) - }() - setIngressConfigComponentRoute(t, client, componentRouteSpec) - - checkCustomTLSWasSet(t, client, api.OpenShiftConsoleRouteName, tlsSecretName) - - t.Log("Removing the admin custom cert to verify fallback to the auto-generated cert") - unsetIngressConfigComponentRoute(t, client) - - err := wait.Poll(1*time.Second, framework.AsyncOperationTimeout, func() (bool, error) { - route, getErr := client.Routes.Routes(api.OpenShiftConsoleNamespace).Get(context.TODO(), api.OpenShiftConsoleRouteName, metav1.GetOptions{}) - if getErr != nil { - return false, getErr - } - if route.Spec.TLS == nil || len(route.Spec.TLS.Certificate) == 0 { - return false, nil - } - adminSecret, secretErr := client.Core.Secrets(api.OpenShiftConfigNamespace).Get(context.TODO(), tlsSecretName, metav1.GetOptions{}) - if secretErr != nil { - if apierrors.IsNotFound(secretErr) { - return true, nil - } - return false, secretErr - } - if route.Spec.TLS.Certificate != string(adminSecret.Data["tls.crt"]) { - return true, nil - } - return false, nil - }) - if err != nil { - t.Fatalf("the route did not fall back to the auto-generated cert after removing the admin cert: %v", err) - } - -} - -func TestHTTP2CertNotOnDownloadsRoute(t *testing.T) { - client, _ := framework.StandardSetup(t) - defer framework.StandardCleanup(t, client) - - route, err := client.Routes.Routes(api.OpenShiftConsoleNamespace).Get(context.TODO(), api.OpenShiftConsoleDownloadsRouteName, metav1.GetOptions{}) - if err != nil { - t.Fatalf("could not get the downloads route: %v", err) - } - if route.Spec.TLS != nil && len(route.Spec.TLS.Certificate) > 0 { - t.Error("expected the downloads route to have no TLS cert set") - } -}