From ce18672c6c415974578a92b14caa6fb9147c5fcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20He=C3=9Felmann?= Date: Mon, 7 Apr 2025 11:16:33 +0200 Subject: [PATCH 01/20] ROX-28296: reencrypt route operator integration --- operator/api/v1alpha1/central_types.go | 64 ++++++++++++++++- .../api/v1alpha1/zz_generated.deepcopy.go | 70 +++++++++++++++++++ .../platform.stackrox.io_centrals.yaml | 45 +++++++++++- .../rhacs-operator.clusterserviceversion.yaml | 47 ++++++++++++- .../bases/platform.stackrox.io_centrals.yaml | 45 +++++++++++- .../rhacs-operator.clusterserviceversion.yaml | 37 +++++++++- .../internal/central/reconciler/reconciler.go | 5 +- .../central/values/translation/translation.go | 31 ++++++-- .../values/translation/translation_test.go | 60 ++++++++++++++++ operator/internal/route/translation.go | 58 +++++++++++++++ operator/internal/route/translation_test.go | 68 ++++++++++++++++++ .../central/central-misc/050-assert.yaml | 47 +++++++++++++ .../central-misc/050-set-exposure-route.yaml | 20 ++++++ 13 files changed, 585 insertions(+), 12 deletions(-) create mode 100644 operator/internal/route/translation.go create mode 100644 operator/internal/route/translation_test.go create mode 100644 operator/tests/central/central-misc/050-assert.yaml create mode 100644 operator/tests/central/central-misc/050-set-exposure-route.yaml diff --git a/operator/api/v1alpha1/central_types.go b/operator/api/v1alpha1/central_types.go index a7a7aa82c046c..0ff223e3ef534 100644 --- a/operator/api/v1alpha1/central_types.go +++ b/operator/api/v1alpha1/central_types.go @@ -157,6 +157,14 @@ type CentralComponentSpec struct { DeploymentSpec `json:",inline"` } +// GetExposure provides a way to retrieve the Exposure setting that is safe to use on a nil receiver object. +func (c *CentralComponentSpec) GetExposure() *Exposure { + if c == nil { + return nil + } + return c.Exposure +} + // GetDB returns Central's db config func (c *CentralComponentSpec) GetDB() *CentralDBSpec { if c == nil { @@ -437,6 +445,14 @@ type Exposure struct { NodePort *ExposureNodePort `json:"nodePort,omitempty"` } +// GetRoute provides a way to retrieve the Route setting that is safe to use on a nil receiver object. +func (c *Exposure) GetRoute() *ExposureRoute { + if c == nil { + return nil + } + return c.Route +} + // ExposureLoadBalancer defines settings for exposing central via a LoadBalancer. type ExposureLoadBalancer struct { //+kubebuilder:default=false @@ -475,9 +491,55 @@ type ExposureRoute struct { Enabled *bool `json:"enabled,omitempty"` // Specify a custom hostname for the central route. - // If unspecified, an appropriate default value will be automatically chosen by OpenShift route operator. + // If unspecified, an appropriate default value will be automatically chosen by the OpenShift route operator. //+operator-sdk:csv:customresourcedefinitions:type=spec,order=2 Host *string `json:"host,omitempty"` + + // Set up a central route with reencrypt tls termination. + // For reencrypt routes, the request is terminated on the OpenShift router with a custom certificate. + // The request is then reencrypted by the OpenShift router and sent to central. + // --tls--> --tls--> + //+operator-sdk:csv:customresourcedefinitions:type=spec,order=3 + Reencrypt *ExposureRouteReencrypt `json:"reencrypt,omitempty"` +} + +// ExposureRouteReencrypt defines settings for exposing central via a reencrypt Route. +type ExposureRouteReencrypt struct { + //+kubebuilder:default=false + //+operator-sdk:csv:customresourcedefinitions:type=spec,order=1 + Enabled *bool `json:"enabled,omitempty"` + + // Specify a custom hostname for the central reencrypt route. + // If unspecified, an appropriate default value will be automatically chosen by the OpenShift route operator. + //+operator-sdk:csv:customresourcedefinitions:type=spec,order=2 + Host *string `json:"host,omitempty"` + + // TLS settings for exposing central via a reencrypt Route. + TLS *ExposureRouteReencryptTLS `json:"tls,omitempty"` +} + +// ExposureRouteReencryptTLS defines TLS settings for exposing central via a reencrypt Route. +type ExposureRouteReencryptTLS struct { + // PEM encoded certificate chain that may be used to establish a complete chain of trust. + //+operator-sdk:csv:customresourcedefinitions:type=spec,order=1 + CaCertificate *string `json:"caCertificate,omitempty"` + + // The PEM encoded certificate that is served on the route. Must be a single serving + // certificate instead of a certificate chain. + // Defaults to a certificate signed by the OpenShift certificate authority is used. + //+operator-sdk:csv:customresourcedefinitions:type=spec,order=2 + Certificate *string `json:"certificate,omitempty"` + + // The CA certificate of the final destination. Should be provided because the OpenShift + // router uses it for health checks on the secure connection. + // Defaults to the Central certificate authority. + //+operator-sdk:csv:customresourcedefinitions:type=spec,order=3 + DestinationCACertificate *string `json:"destinationCACertificate,omitempty"` + + // The PEM encoded private key of the certificate that is served on the route. + // Defaults to a certificate signed by the OpenShift certificate authority. + //+operator-sdk:csv:customresourcedefinitions:type=spec,order=4 + Key *string `json:"key,omitempty"` } // Telemetry defines telemetry settings for Central. diff --git a/operator/api/v1alpha1/zz_generated.deepcopy.go b/operator/api/v1alpha1/zz_generated.deepcopy.go index a39a967ffd5d3..b5ca286c998b5 100644 --- a/operator/api/v1alpha1/zz_generated.deepcopy.go +++ b/operator/api/v1alpha1/zz_generated.deepcopy.go @@ -806,6 +806,11 @@ func (in *ExposureRoute) DeepCopyInto(out *ExposureRoute) { *out = new(string) **out = **in } + if in.Reencrypt != nil { + in, out := &in.Reencrypt, &out.Reencrypt + *out = new(ExposureRouteReencrypt) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExposureRoute. @@ -818,6 +823,71 @@ func (in *ExposureRoute) DeepCopy() *ExposureRoute { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExposureRouteReencrypt) DeepCopyInto(out *ExposureRouteReencrypt) { + *out = *in + if in.Enabled != nil { + in, out := &in.Enabled, &out.Enabled + *out = new(bool) + **out = **in + } + if in.Host != nil { + in, out := &in.Host, &out.Host + *out = new(string) + **out = **in + } + if in.TLS != nil { + in, out := &in.TLS, &out.TLS + *out = new(ExposureRouteReencryptTLS) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExposureRouteReencrypt. +func (in *ExposureRouteReencrypt) DeepCopy() *ExposureRouteReencrypt { + if in == nil { + return nil + } + out := new(ExposureRouteReencrypt) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExposureRouteReencryptTLS) DeepCopyInto(out *ExposureRouteReencryptTLS) { + *out = *in + if in.CaCertificate != nil { + in, out := &in.CaCertificate, &out.CaCertificate + *out = new(string) + **out = **in + } + if in.Certificate != nil { + in, out := &in.Certificate, &out.Certificate + *out = new(string) + **out = **in + } + if in.DestinationCACertificate != nil { + in, out := &in.DestinationCACertificate, &out.DestinationCACertificate + *out = new(string) + **out = **in + } + if in.Key != nil { + in, out := &in.Key, &out.Key + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExposureRouteReencryptTLS. +func (in *ExposureRouteReencryptTLS) DeepCopy() *ExposureRouteReencryptTLS { + if in == nil { + return nil + } + out := new(ExposureRouteReencryptTLS) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *GlobalMonitoring) DeepCopyInto(out *GlobalMonitoring) { *out = *in diff --git a/operator/bundle/manifests/platform.stackrox.io_centrals.yaml b/operator/bundle/manifests/platform.stackrox.io_centrals.yaml index 8624ea9430f46..415efe58300d4 100644 --- a/operator/bundle/manifests/platform.stackrox.io_centrals.yaml +++ b/operator/bundle/manifests/platform.stackrox.io_centrals.yaml @@ -384,8 +384,51 @@ spec: host: description: |- Specify a custom hostname for the central route. - If unspecified, an appropriate default value will be automatically chosen by OpenShift route operator. + If unspecified, an appropriate default value will be automatically chosen by the OpenShift route operator. type: string + reencrypt: + description: |- + Set up a central route with reencrypt tls termination. + For reencrypt routes, the request is terminated on the OpenShift router with a custom certificate. + The request is then reencrypted by the OpenShift router and sent to central. + --tls--> --tls--> + properties: + enabled: + default: false + type: boolean + host: + description: |- + Specify a custom hostname for the central reencrypt route. + If unspecified, an appropriate default value will be automatically chosen by the OpenShift route operator. + type: string + tls: + description: TLS settings for exposing central via + a reencrypt Route. + properties: + caCertificate: + description: PEM encoded certificate chain that + may be used to establish a complete chain of + trust. + type: string + certificate: + description: |- + The PEM encoded certificate that is served on the route. Must be a single serving + certificate instead of a certificate chain. + Defaults to a certificate signed by the OpenShift certificate authority is used. + type: string + destinationCACertificate: + description: |- + The CA certificate of the final destination. Should be provided because the OpenShift + router uses it for health checks on the secure connection. + Defaults to the Central certificate authority. + type: string + key: + description: |- + The PEM encoded private key of the certificate that is served on the route. + Defaults to a certificate signed by the OpenShift certificate authority. + type: string + type: object + type: object type: object type: object hostAliases: diff --git a/operator/bundle/manifests/rhacs-operator.clusterserviceversion.yaml b/operator/bundle/manifests/rhacs-operator.clusterserviceversion.yaml index 996b16f885096..28861b6c6ea17 100644 --- a/operator/bundle/manifests/rhacs-operator.clusterserviceversion.yaml +++ b/operator/bundle/manifests/rhacs-operator.clusterserviceversion.yaml @@ -392,9 +392,54 @@ spec: - description: 'Specify a custom hostname for the central route. If unspecified, an appropriate default value will be automatically chosen - by OpenShift route operator.' + by the OpenShift route operator.' displayName: Host path: central.exposure.route.host + - description: 'Set up a central route with reencrypt tls termination. + + For reencrypt routes, the request is terminated on the OpenShift router + with a custom certificate. + + The request is then reencrypted by the OpenShift router and sent to central. + + --tls--> --tls--> ' + displayName: Reencrypt + path: central.exposure.route.reencrypt + - displayName: Enabled + path: central.exposure.route.reencrypt.enabled + - description: 'Specify a custom hostname for the central reencrypt route. + + If unspecified, an appropriate default value will be automatically chosen + by the OpenShift route operator.' + displayName: Host + path: central.exposure.route.reencrypt.host + - description: PEM encoded certificate chain that may be used to establish a + complete chain of trust. + displayName: Ca Certificate + path: central.exposure.route.reencrypt.tls.caCertificate + - description: 'The PEM encoded certificate that is served on the route. Must + be a single serving + + certificate instead of a certificate chain. + + Defaults to a certificate signed by the OpenShift certificate authority + is used.' + displayName: Certificate + path: central.exposure.route.reencrypt.tls.certificate + - description: 'The CA certificate of the final destination. Should be provided + because the OpenShift + + router uses it for health checks on the secure connection. + + Defaults to the Central certificate authority.' + displayName: Destination CACertificate + path: central.exposure.route.reencrypt.tls.destinationCACertificate + - description: 'The PEM encoded private key of the certificate that is served + on the route. + + Defaults to a certificate signed by the OpenShift certificate authority.' + displayName: Key + path: central.exposure.route.reencrypt.tls.key - description: 'Expose the monitoring endpoint. A new service, "monitoring", with port 9090, will be created as well as a network policy allowing diff --git a/operator/config/crd/bases/platform.stackrox.io_centrals.yaml b/operator/config/crd/bases/platform.stackrox.io_centrals.yaml index 6b1416851c1f2..d7d2472909474 100644 --- a/operator/config/crd/bases/platform.stackrox.io_centrals.yaml +++ b/operator/config/crd/bases/platform.stackrox.io_centrals.yaml @@ -384,8 +384,51 @@ spec: host: description: |- Specify a custom hostname for the central route. - If unspecified, an appropriate default value will be automatically chosen by OpenShift route operator. + If unspecified, an appropriate default value will be automatically chosen by the OpenShift route operator. type: string + reencrypt: + description: |- + Set up a central route with reencrypt tls termination. + For reencrypt routes, the request is terminated on the OpenShift router with a custom certificate. + The request is then reencrypted by the OpenShift router and sent to central. + --tls--> --tls--> + properties: + enabled: + default: false + type: boolean + host: + description: |- + Specify a custom hostname for the central reencrypt route. + If unspecified, an appropriate default value will be automatically chosen by the OpenShift route operator. + type: string + tls: + description: TLS settings for exposing central via + a reencrypt Route. + properties: + caCertificate: + description: PEM encoded certificate chain that + may be used to establish a complete chain of + trust. + type: string + certificate: + description: |- + The PEM encoded certificate that is served on the route. Must be a single serving + certificate instead of a certificate chain. + Defaults to a certificate signed by the OpenShift certificate authority is used. + type: string + destinationCACertificate: + description: |- + The CA certificate of the final destination. Should be provided because the OpenShift + router uses it for health checks on the secure connection. + Defaults to the Central certificate authority. + type: string + key: + description: |- + The PEM encoded private key of the certificate that is served on the route. + Defaults to a certificate signed by the OpenShift certificate authority. + type: string + type: object + type: object type: object type: object hostAliases: diff --git a/operator/config/manifests/bases/rhacs-operator.clusterserviceversion.yaml b/operator/config/manifests/bases/rhacs-operator.clusterserviceversion.yaml index 379902a6857ca..9d38621727c15 100644 --- a/operator/config/manifests/bases/rhacs-operator.clusterserviceversion.yaml +++ b/operator/config/manifests/bases/rhacs-operator.clusterserviceversion.yaml @@ -85,6 +85,12 @@ spec: path: central.exposure.route - displayName: Enabled path: central.exposure.route.enabled + - displayName: Enabled + path: central.exposure.route.reencrypt.enabled + - description: PEM encoded certificate chain that may be used to establish a + complete chain of trust. + displayName: Ca Certificate + path: central.exposure.route.reencrypt.tls.caCertificate - description: |- Expose the monitoring endpoint. A new service, "monitoring", with port 9090, will be created as well as a network policy allowing @@ -248,9 +254,20 @@ spec: - urn:alm:descriptor:com.tectonic.ui:fieldDependency:.enabled:true - description: |- Specify a custom hostname for the central route. - If unspecified, an appropriate default value will be automatically chosen by OpenShift route operator. + If unspecified, an appropriate default value will be automatically chosen by the OpenShift route operator. displayName: Host path: central.exposure.route.host + - description: |- + Specify a custom hostname for the central reencrypt route. + If unspecified, an appropriate default value will be automatically chosen by the OpenShift route operator. + displayName: Host + path: central.exposure.route.reencrypt.host + - description: |- + The PEM encoded certificate that is served on the route. Must be a single serving + certificate instead of a certificate chain. + Defaults to a certificate signed by the OpenShift certificate authority is used. + displayName: Certificate + path: central.exposure.route.reencrypt.tls.certificate - description: |- The size of the persistent volume when created through the claim. If a claim was automatically created, this can be used after the initial deployment to resize (grow) the volume (only supported by some @@ -344,6 +361,19 @@ spec: - description: Expose Central through a node port. displayName: Node Port path: central.exposure.nodePort + - description: |- + Set up a central route with reencrypt tls termination. + For reencrypt routes, the request is terminated on the OpenShift router with a custom certificate. + The request is then reencrypted by the OpenShift router and sent to central. + --tls--> --tls--> + displayName: Reencrypt + path: central.exposure.route.reencrypt + - description: |- + The CA certificate of the final destination. Should be provided because the OpenShift + router uses it for health checks on the secure connection. + Defaults to the Central certificate authority. + displayName: Destination CACertificate + path: central.exposure.route.reencrypt.tls.destinationCACertificate - description: |- The name of the storage class to use for the PVC. If your cluster is not configured with a default storage class, you must select a value here. @@ -401,6 +431,11 @@ spec: be used if modifications need to be applied. displayName: Config map that will override postgresql.conf and pg_hba.conf path: central.db.configOverride + - description: |- + The PEM encoded private key of the certificate that is served on the route. + Defaults to a certificate signed by the OpenShift certificate authority. + displayName: Key + path: central.exposure.route.reencrypt.tls.key - description: |- Configures monitoring endpoint for Central. The monitoring endpoint allows other services to collect metrics from Central, provided in diff --git a/operator/internal/central/reconciler/reconciler.go b/operator/internal/central/reconciler/reconciler.go index 8c0d7564d29c4..76c380bba8165 100644 --- a/operator/internal/central/reconciler/reconciler.go +++ b/operator/internal/central/reconciler/reconciler.go @@ -10,6 +10,7 @@ import ( "github.com/stackrox/rox/operator/internal/legacy" "github.com/stackrox/rox/operator/internal/proxy" "github.com/stackrox/rox/operator/internal/reconciler" + "github.com/stackrox/rox/operator/internal/route" "github.com/stackrox/rox/operator/internal/utils" "github.com/stackrox/rox/operator/internal/values/translation" "github.com/stackrox/rox/pkg/version" @@ -59,7 +60,9 @@ func RegisterNewReconciler(mgr ctrl.Manager, selector string) error { // owned by the operator so we can't guarantee labels for cache // are set properly. legacy.NewImagePullSecretReferenceInjector(mgr.GetAPIReader(), "imagePullSecrets", - "stackrox", "stackrox-scanner", "stackrox-scanner-v4")), + "stackrox", "stackrox-scanner", "stackrox-scanner-v4"), + route.NewRouteInjector(mgr.GetClient(), mgr.GetLogger()), + ), opts..., ) } diff --git a/operator/internal/central/values/translation/translation.go b/operator/internal/central/values/translation/translation.go index 92dc76f002e18..9fd780446ebed 100644 --- a/operator/internal/central/values/translation/translation.go +++ b/operator/internal/central/values/translation/translation.go @@ -148,6 +148,30 @@ func getEnv(c platform.Central) *translation.ValuesBuilder { return &ret } +func getExposureRouteValues(r *platform.ExposureRoute) *translation.ValuesBuilder { + if r == nil { + return nil + } + route := translation.NewValuesBuilder() + route.SetBool("enabled", r.Enabled) + route.SetString("host", r.Host) + if r.Reencrypt != nil { + reencrypt := translation.NewValuesBuilder() + reencrypt.SetBool("enabled", r.Reencrypt.Enabled) + reencrypt.SetString("host", r.Reencrypt.Host) + if r.Reencrypt.TLS != nil { + tls := translation.NewValuesBuilder() + tls.SetString("caCertificate", r.Reencrypt.TLS.CaCertificate) + tls.SetString("certificate", r.Reencrypt.TLS.Certificate) + tls.SetString("destinationCACertificate", r.Reencrypt.TLS.DestinationCACertificate) + tls.SetString("key", r.Reencrypt.TLS.Key) + reencrypt.AddChild("tls", &tls) + } + route.AddChild("reencrypt", &reencrypt) + } + return &route +} + func getCentralDBPersistenceValues(p *platform.DBPersistence) *translation.ValuesBuilder { persistence := translation.NewValuesBuilder() if hostPath := p.GetHostPath(); hostPath != "" { @@ -191,12 +215,7 @@ func getCentralComponentValues(c *platform.CentralComponentSpec) (*translation.V np.SetInt32("port", c.Exposure.NodePort.Port) exposure.AddChild("nodePort", &np) } - if c.Exposure.Route != nil { - route := translation.NewValuesBuilder() - route.SetBool("enabled", c.Exposure.Route.Enabled) - route.SetString("host", c.Exposure.Route.Host) - exposure.AddChild("route", &route) - } + exposure.AddChild("route", getExposureRouteValues(c.Exposure.Route)) cv.AddChild("exposure", &exposure) } diff --git a/operator/internal/central/values/translation/translation_test.go b/operator/internal/central/values/translation/translation_test.go index afcff2764060e..1fc1c76eea461 100644 --- a/operator/internal/central/values/translation/translation_test.go +++ b/operator/internal/central/values/translation/translation_test.go @@ -910,6 +910,66 @@ func TestTranslate(t *testing.T) { }, }, + "reencrypt route with custom hostname and trust bundle": { + args: args{ + c: platform.Central{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "stackrox", + }, + Spec: platform.CentralSpec{ + Central: &platform.CentralComponentSpec{ + Exposure: &platform.Exposure{ + Route: &platform.ExposureRoute{ + Enabled: &truth, + Reencrypt: &platform.ExposureRouteReencrypt{ + Host: pointer.String("custom-route.stackrox.io"), + TLS: &platform.ExposureRouteReencryptTLS{ + CaCertificate: pointer.String("custom CA"), + Certificate: pointer.String("custom cert"), + DestinationCACertificate: pointer.String("custom dest CA"), + Key: pointer.String("custom key"), + }, + }, + }, + }, + }, + }, + }, + pvcs: []*corev1.PersistentVolumeClaim{defaultPvc}, + }, + want: chartutil.Values{ + "monitoring": map[string]interface{}{ + "openshift": map[string]interface{}{ + "enabled": true, + }, + }, + "central": map[string]interface{}{ + "exposure": map[string]interface{}{ + "route": map[string]interface{}{ + "enabled": true, + "reencrypt": map[string]interface{}{ + "host": "custom-route.stackrox.io", + "tls": map[string]interface{}{ + "caCertificate": "custom CA", + "certificate": "custom cert", + "destinationCACertificate": "custom dest CA", + "key": "custom key", + }, + }, + }, + }, + "exposeMonitoring": false, + "db": map[string]interface{}{ + "persistence": map[string]interface{}{ + "persistentVolumeClaim": map[string]interface{}{ + "createClaim": false, + }, + }, + }, + }, + }, + }, + "add managed service setting": { args: args{ c: platform.Central{ diff --git a/operator/internal/route/translation.go b/operator/internal/route/translation.go new file mode 100644 index 0000000000000..b894b0b712a6e --- /dev/null +++ b/operator/internal/route/translation.go @@ -0,0 +1,58 @@ +package route + +import ( + "context" + "fmt" + + "github.com/go-logr/logr" + "github.com/stackrox/rox/operator/internal/values/translation" + "github.com/stackrox/rox/pkg/k8sutil" + "helm.sh/helm/v3/pkg/chartutil" + corev1 "k8s.io/api/core/v1" + ctrlClient "sigs.k8s.io/controller-runtime/pkg/client" +) + +const ( + tlsSecretCAKey = "ca.pem" + tlsSecretName = "central-tls" +) + +// NewRouteInjector returns an object which injects the Central certificate authority into route chart values. +func NewRouteInjector(client ctrlClient.Reader, log logr.Logger) *routeInjector { + return &routeInjector{ + client: client, + log: log, + } +} + +type routeInjector struct { + client ctrlClient.Reader + log logr.Logger +} + +var _ translation.Enricher = &routeInjector{} + +// Enrich injects the Central certificate authority into the reencrypt route. +func (i *routeInjector) Enrich(ctx context.Context, obj k8sutil.Object, vals chartutil.Values) (chartutil.Values, error) { + namespaceName := obj.GetNamespace() + tlsSecret := &corev1.Secret{} + + if err := i.client.Get(ctx, ctrlClient.ObjectKey{Name: tlsSecretName, Namespace: namespaceName}, tlsSecret); err != nil { + return nil, fmt.Errorf("getting secret %s/%s: %w", namespaceName, tlsSecretName, err) + } + + routeVals := chartutil.Values{ + "central": map[string]interface{}{ + "exposure": map[string]interface{}{ + "route": map[string]interface{}{ + "reencrypt": map[string]interface{}{ + "tls": map[string]interface{}{ + "destinationCACertificate": string(tlsSecret.Data[tlsSecretCAKey]), + }, + }, + }, + }, + }, + } + return chartutil.CoalesceTables(vals, routeVals), nil +} diff --git a/operator/internal/route/translation_test.go b/operator/internal/route/translation_test.go new file mode 100644 index 0000000000000..20851b22d711f --- /dev/null +++ b/operator/internal/route/translation_test.go @@ -0,0 +1,68 @@ +package route + +import ( + "context" + "testing" + + "github.com/go-logr/logr" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "helm.sh/helm/v3/pkg/chartutil" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "sigs.k8s.io/controller-runtime/pkg/client/fake" +) + +func Test_injector_Enrich(t *testing.T) { + centralCA := "fake-central-CA" + + tests := map[string]struct { + destCAValue string + want string + }{ + "should default to central CA from central-tls secret": { + destCAValue: "", + want: centralCA, + }, + "should take destinationCACertificate from the input values": { + destCAValue: "fake-input-CA", + want: "fake-input-CA", + }, + } + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + obj := &unstructured.Unstructured{} + obj.SetNamespace("some-ns") + tlsSecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "some-ns", + Name: tlsSecretName, + }, + Data: map[string][]byte{tlsSecretCAKey: []byte(centralCA)}, + } + i := NewRouteInjector(fake.NewFakeClient(tlsSecret), logr.New(nil)) + vals := chartutil.Values{} + if tt.destCAValue != "" { + vals["central"] = map[string]interface{}{ + "exposure": map[string]interface{}{ + "route": map[string]interface{}{ + "reencrypt": map[string]interface{}{ + "tls": map[string]interface{}{ + "destinationCACertificate": string(tt.destCAValue), + }, + }, + }, + }, + } + } + + gotValues, err := i.Enrich(context.Background(), obj, vals) + require.NoError(t, err) + gotCA, err := gotValues.PathValue("central.exposure.route.reencrypt.tls.destinationCACertificate") + require.NoError(t, err) + + assert.Equal(t, tt.want, gotCA) + }) + } +} diff --git a/operator/tests/central/central-misc/050-assert.yaml b/operator/tests/central/central-misc/050-assert.yaml new file mode 100644 index 0000000000000..9c8a1c5ca5742 --- /dev/null +++ b/operator/tests/central/central-misc/050-assert.yaml @@ -0,0 +1,47 @@ +# Make kuttl ignore this file unless running against openshift. +apiVersion: kuttl.dev/v1beta1 +kind: TestFile +testRunSelector: + matchLabels: + openshift: "true" +--- +apiVersion: route.openshift.io/v1 +kind: Route +metadata: + name: central +spec: + host: custom-route.host + to: + kind: Service + name: central + port: + targetPort: https + tls: + termination: passthrough +--- +apiVersion: route.openshift.io/v1 +kind: Route +metadata: + name: central-mtls +spec: + to: + kind: Service + name: central + port: + targetPort: https + tls: + termination: passthrough +--- +apiVersion: route.openshift.io/v1 +kind: Route +metadata: + name: central-reencrypt +spec: + host: custom-reencrypt-route.host + to: + kind: Service + name: central + port: + targetPort: https + tls: + termination: reencrypt diff --git a/operator/tests/central/central-misc/050-set-exposure-route.yaml b/operator/tests/central/central-misc/050-set-exposure-route.yaml new file mode 100644 index 0000000000000..36d76b2e26d50 --- /dev/null +++ b/operator/tests/central/central-misc/050-set-exposure-route.yaml @@ -0,0 +1,20 @@ +# Make kuttl ignore this file unless running against openshift. +apiVersion: kuttl.dev/v1beta1 +kind: TestFile +testRunSelector: + matchLabels: + openshift: "true" +--- +apiVersion: platform.stackrox.io/v1alpha1 +kind: Central +metadata: + name: stackrox-central-services +spec: + central: + exposure: + route: + enabled: true + host: custom-route.host + reencrypt: + enabled: true + host: custom-reencrypt-route.host From 0afbf204c698b28a17e449adec40e01f689724a8 Mon Sep 17 00:00:00 2001 From: Stephan Hesselmann Date: Thu, 10 Apr 2025 14:34:10 +0200 Subject: [PATCH 02/20] Update operator/api/v1alpha1/central_types.go Co-authored-by: Marcin Owsiany --- operator/api/v1alpha1/central_types.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/operator/api/v1alpha1/central_types.go b/operator/api/v1alpha1/central_types.go index 0ff223e3ef534..f2a4d1f17b202 100644 --- a/operator/api/v1alpha1/central_types.go +++ b/operator/api/v1alpha1/central_types.go @@ -498,7 +498,7 @@ type ExposureRoute struct { // Set up a central route with reencrypt tls termination. // For reencrypt routes, the request is terminated on the OpenShift router with a custom certificate. // The request is then reencrypted by the OpenShift router and sent to central. - // --tls--> --tls--> + // [user] --TLS--> [OpenShift router] --TLS--> [central] //+operator-sdk:csv:customresourcedefinitions:type=spec,order=3 Reencrypt *ExposureRouteReencrypt `json:"reencrypt,omitempty"` } From 75f861f0e41fcc24aa4cede6755258468e81f49c Mon Sep 17 00:00:00 2001 From: Stephan Hesselmann Date: Thu, 10 Apr 2025 14:34:38 +0200 Subject: [PATCH 03/20] Update operator/api/v1alpha1/central_types.go Co-authored-by: Marcin Owsiany --- operator/api/v1alpha1/central_types.go | 1 + 1 file changed, 1 insertion(+) diff --git a/operator/api/v1alpha1/central_types.go b/operator/api/v1alpha1/central_types.go index f2a4d1f17b202..0607941dde48d 100644 --- a/operator/api/v1alpha1/central_types.go +++ b/operator/api/v1alpha1/central_types.go @@ -515,6 +515,7 @@ type ExposureRouteReencrypt struct { Host *string `json:"host,omitempty"` // TLS settings for exposing central via a reencrypt Route. + //+operator-sdk:csv:customresourcedefinitions:type=spec,order=3,displayName="TLS Settings" TLS *ExposureRouteReencryptTLS `json:"tls,omitempty"` } From 0c9e6409ff309f7fab75fcfd7574d83988a19bd5 Mon Sep 17 00:00:00 2001 From: Stephan Hesselmann Date: Thu, 10 Apr 2025 14:34:48 +0200 Subject: [PATCH 04/20] Update operator/api/v1alpha1/central_types.go Co-authored-by: Marcin Owsiany --- operator/api/v1alpha1/central_types.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/operator/api/v1alpha1/central_types.go b/operator/api/v1alpha1/central_types.go index 0607941dde48d..4955778a40208 100644 --- a/operator/api/v1alpha1/central_types.go +++ b/operator/api/v1alpha1/central_types.go @@ -499,7 +499,7 @@ type ExposureRoute struct { // For reencrypt routes, the request is terminated on the OpenShift router with a custom certificate. // The request is then reencrypted by the OpenShift router and sent to central. // [user] --TLS--> [OpenShift router] --TLS--> [central] - //+operator-sdk:csv:customresourcedefinitions:type=spec,order=3 + //+operator-sdk:csv:customresourcedefinitions:type=spec,order=3,displayName="Re-Encrypt Route" Reencrypt *ExposureRouteReencrypt `json:"reencrypt,omitempty"` } From 3c58fc63e673aaff483e1a6e18b2e636d2f2f43d Mon Sep 17 00:00:00 2001 From: Stephan Hesselmann Date: Thu, 10 Apr 2025 14:35:01 +0200 Subject: [PATCH 05/20] Update operator/api/v1alpha1/central_types.go Co-authored-by: Marcin Owsiany --- operator/api/v1alpha1/central_types.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/operator/api/v1alpha1/central_types.go b/operator/api/v1alpha1/central_types.go index 4955778a40208..530b75a36eeeb 100644 --- a/operator/api/v1alpha1/central_types.go +++ b/operator/api/v1alpha1/central_types.go @@ -522,7 +522,7 @@ type ExposureRouteReencrypt struct { // ExposureRouteReencryptTLS defines TLS settings for exposing central via a reencrypt Route. type ExposureRouteReencryptTLS struct { // PEM encoded certificate chain that may be used to establish a complete chain of trust. - //+operator-sdk:csv:customresourcedefinitions:type=spec,order=1 + //+operator-sdk:csv:customresourcedefinitions:type=spec,order=1,displayName="CA Certificate" CaCertificate *string `json:"caCertificate,omitempty"` // The PEM encoded certificate that is served on the route. Must be a single serving From 1b0e7961269f396e886a9f7eb4a182f833db4b2f Mon Sep 17 00:00:00 2001 From: Stephan Hesselmann Date: Thu, 10 Apr 2025 14:35:17 +0200 Subject: [PATCH 06/20] Update operator/api/v1alpha1/central_types.go Co-authored-by: Marcin Owsiany --- operator/api/v1alpha1/central_types.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/operator/api/v1alpha1/central_types.go b/operator/api/v1alpha1/central_types.go index 530b75a36eeeb..1fd6967352165 100644 --- a/operator/api/v1alpha1/central_types.go +++ b/operator/api/v1alpha1/central_types.go @@ -521,7 +521,7 @@ type ExposureRouteReencrypt struct { // ExposureRouteReencryptTLS defines TLS settings for exposing central via a reencrypt Route. type ExposureRouteReencryptTLS struct { - // PEM encoded certificate chain that may be used to establish a complete chain of trust. + // The PEM encoded certificate chain that may be used to establish a complete chain of trust. //+operator-sdk:csv:customresourcedefinitions:type=spec,order=1,displayName="CA Certificate" CaCertificate *string `json:"caCertificate,omitempty"` From 817a9c04b8ec9b459a902f6ea3a042b1ae7a82c7 Mon Sep 17 00:00:00 2001 From: Stephan Hesselmann Date: Thu, 10 Apr 2025 14:35:32 +0200 Subject: [PATCH 07/20] Update operator/api/v1alpha1/central_types.go Co-authored-by: Marcin Owsiany --- operator/api/v1alpha1/central_types.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/operator/api/v1alpha1/central_types.go b/operator/api/v1alpha1/central_types.go index 1fd6967352165..599e6505756ca 100644 --- a/operator/api/v1alpha1/central_types.go +++ b/operator/api/v1alpha1/central_types.go @@ -527,7 +527,7 @@ type ExposureRouteReencryptTLS struct { // The PEM encoded certificate that is served on the route. Must be a single serving // certificate instead of a certificate chain. - // Defaults to a certificate signed by the OpenShift certificate authority is used. + // Defaults to a certificate signed by the OpenShift certificate authority. //+operator-sdk:csv:customresourcedefinitions:type=spec,order=2 Certificate *string `json:"certificate,omitempty"` From 060468c1b1c8d8d531ca5a123e5dd76c25686c38 Mon Sep 17 00:00:00 2001 From: Stephan Hesselmann Date: Thu, 10 Apr 2025 14:35:43 +0200 Subject: [PATCH 08/20] Update operator/api/v1alpha1/central_types.go Co-authored-by: Marcin Owsiany --- operator/api/v1alpha1/central_types.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/operator/api/v1alpha1/central_types.go b/operator/api/v1alpha1/central_types.go index 599e6505756ca..3806585d870ee 100644 --- a/operator/api/v1alpha1/central_types.go +++ b/operator/api/v1alpha1/central_types.go @@ -539,7 +539,7 @@ type ExposureRouteReencryptTLS struct { // The PEM encoded private key of the certificate that is served on the route. // Defaults to a certificate signed by the OpenShift certificate authority. - //+operator-sdk:csv:customresourcedefinitions:type=spec,order=4 + //+operator-sdk:csv:customresourcedefinitions:type=spec,order=4,displayName="Private Key" Key *string `json:"key,omitempty"` } From ae95a7244afeadcc19d8303b04da5fd649f6f92f Mon Sep 17 00:00:00 2001 From: Stephan Hesselmann Date: Thu, 10 Apr 2025 14:36:09 +0200 Subject: [PATCH 09/20] Update operator/api/v1alpha1/central_types.go Co-authored-by: Marcin Owsiany --- operator/api/v1alpha1/central_types.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/operator/api/v1alpha1/central_types.go b/operator/api/v1alpha1/central_types.go index 3806585d870ee..dfeda5d09d7d5 100644 --- a/operator/api/v1alpha1/central_types.go +++ b/operator/api/v1alpha1/central_types.go @@ -528,7 +528,7 @@ type ExposureRouteReencryptTLS struct { // The PEM encoded certificate that is served on the route. Must be a single serving // certificate instead of a certificate chain. // Defaults to a certificate signed by the OpenShift certificate authority. - //+operator-sdk:csv:customresourcedefinitions:type=spec,order=2 + //+operator-sdk:csv:customresourcedefinitions:type=spec,order=2,displayName="Certificate" Certificate *string `json:"certificate,omitempty"` // The CA certificate of the final destination. Should be provided because the OpenShift From 883cb4a4aec5e91a85bf8becb580a364c8c1e2c6 Mon Sep 17 00:00:00 2001 From: Stephan Hesselmann Date: Thu, 10 Apr 2025 14:36:32 +0200 Subject: [PATCH 10/20] Update operator/api/v1alpha1/central_types.go Co-authored-by: Marcin Owsiany --- operator/api/v1alpha1/central_types.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/operator/api/v1alpha1/central_types.go b/operator/api/v1alpha1/central_types.go index dfeda5d09d7d5..36030d7a47ca5 100644 --- a/operator/api/v1alpha1/central_types.go +++ b/operator/api/v1alpha1/central_types.go @@ -534,7 +534,7 @@ type ExposureRouteReencryptTLS struct { // The CA certificate of the final destination. Should be provided because the OpenShift // router uses it for health checks on the secure connection. // Defaults to the Central certificate authority. - //+operator-sdk:csv:customresourcedefinitions:type=spec,order=3 + //+operator-sdk:csv:customresourcedefinitions:type=spec,order=3,displayName="Destination CA Certificate" DestinationCACertificate *string `json:"destinationCACertificate,omitempty"` // The PEM encoded private key of the certificate that is served on the route. From caaf8bdfcec2e137bbcf3daed6a924c763429139 Mon Sep 17 00:00:00 2001 From: Stephan Hesselmann Date: Thu, 10 Apr 2025 14:37:02 +0200 Subject: [PATCH 11/20] Update operator/api/v1alpha1/central_types.go Co-authored-by: Marcin Owsiany --- operator/api/v1alpha1/central_types.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/operator/api/v1alpha1/central_types.go b/operator/api/v1alpha1/central_types.go index 36030d7a47ca5..7e6667ef44b82 100644 --- a/operator/api/v1alpha1/central_types.go +++ b/operator/api/v1alpha1/central_types.go @@ -531,7 +531,7 @@ type ExposureRouteReencryptTLS struct { //+operator-sdk:csv:customresourcedefinitions:type=spec,order=2,displayName="Certificate" Certificate *string `json:"certificate,omitempty"` - // The CA certificate of the final destination. Should be provided because the OpenShift + // The CA certificate of the final destination, i.e. of central. Should be provided because the OpenShift // router uses it for health checks on the secure connection. // Defaults to the Central certificate authority. //+operator-sdk:csv:customresourcedefinitions:type=spec,order=3,displayName="Destination CA Certificate" From 65671f0f7add3c449b5289d10a0de496035b099e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20He=C3=9Felmann?= Date: Thu, 10 Apr 2025 15:03:41 +0200 Subject: [PATCH 12/20] address comments --- operator/api/v1alpha1/central_types.go | 25 ++++++------------- .../central/extensions/reconcile_tls.go | 3 ++- operator/internal/common/tls.go | 5 ++++ operator/internal/route/translation.go | 13 ++++------ operator/internal/route/translation_test.go | 6 +++-- 5 files changed, 23 insertions(+), 29 deletions(-) create mode 100644 operator/internal/common/tls.go diff --git a/operator/api/v1alpha1/central_types.go b/operator/api/v1alpha1/central_types.go index 7e6667ef44b82..99fb4bc396633 100644 --- a/operator/api/v1alpha1/central_types.go +++ b/operator/api/v1alpha1/central_types.go @@ -157,14 +157,6 @@ type CentralComponentSpec struct { DeploymentSpec `json:",inline"` } -// GetExposure provides a way to retrieve the Exposure setting that is safe to use on a nil receiver object. -func (c *CentralComponentSpec) GetExposure() *Exposure { - if c == nil { - return nil - } - return c.Exposure -} - // GetDB returns Central's db config func (c *CentralComponentSpec) GetDB() *CentralDBSpec { if c == nil { @@ -445,14 +437,6 @@ type Exposure struct { NodePort *ExposureNodePort `json:"nodePort,omitempty"` } -// GetRoute provides a way to retrieve the Route setting that is safe to use on a nil receiver object. -func (c *Exposure) GetRoute() *ExposureRoute { - if c == nil { - return nil - } - return c.Route -} - // ExposureLoadBalancer defines settings for exposing central via a LoadBalancer. type ExposureLoadBalancer struct { //+kubebuilder:default=false @@ -486,6 +470,8 @@ type ExposureNodePort struct { // ExposureRoute defines settings for exposing central via a Route. type ExposureRoute struct { + // Expose central with a passthrough route. + // The passthrough route is still required when using a reencrypt route. //+kubebuilder:default=false //+operator-sdk:csv:customresourcedefinitions:type=spec,order=1 Enabled *bool `json:"enabled,omitempty"` @@ -505,6 +491,8 @@ type ExposureRoute struct { // ExposureRouteReencrypt defines settings for exposing central via a reencrypt Route. type ExposureRouteReencrypt struct { + // Expose central with a reencrypt route. + // Requires a passthrough route for sensor communication. //+kubebuilder:default=false //+operator-sdk:csv:customresourcedefinitions:type=spec,order=1 Enabled *bool `json:"enabled,omitempty"` @@ -522,6 +510,7 @@ type ExposureRouteReencrypt struct { // ExposureRouteReencryptTLS defines TLS settings for exposing central via a reencrypt Route. type ExposureRouteReencryptTLS struct { // The PEM encoded certificate chain that may be used to establish a complete chain of trust. + // Defaults to the OpenShift certificate authority. //+operator-sdk:csv:customresourcedefinitions:type=spec,order=1,displayName="CA Certificate" CaCertificate *string `json:"caCertificate,omitempty"` @@ -531,8 +520,8 @@ type ExposureRouteReencryptTLS struct { //+operator-sdk:csv:customresourcedefinitions:type=spec,order=2,displayName="Certificate" Certificate *string `json:"certificate,omitempty"` - // The CA certificate of the final destination, i.e. of central. Should be provided because the OpenShift - // router uses it for health checks on the secure connection. + // The CA certificate of the final destination, i.e. of central. + // Used by the OpenShift router for health checks on the secure connection. // Defaults to the Central certificate authority. //+operator-sdk:csv:customresourcedefinitions:type=spec,order=3,displayName="Destination CA Certificate" DestinationCACertificate *string `json:"destinationCACertificate,omitempty"` diff --git a/operator/internal/central/extensions/reconcile_tls.go b/operator/internal/central/extensions/reconcile_tls.go index 0e379f90c9a00..ec148ae2e0641 100644 --- a/operator/internal/central/extensions/reconcile_tls.go +++ b/operator/internal/central/extensions/reconcile_tls.go @@ -11,6 +11,7 @@ import ( "github.com/pkg/errors" "github.com/stackrox/rox/generated/storage" platform "github.com/stackrox/rox/operator/api/v1alpha1" + "github.com/stackrox/rox/operator/internal/common" commonExtensions "github.com/stackrox/rox/operator/internal/common/extensions" commonLabels "github.com/stackrox/rox/operator/internal/common/labels" "github.com/stackrox/rox/operator/internal/types" @@ -64,7 +65,7 @@ func (r *createCentralTLSExtensionRun) Execute(ctx context.Context) error { // reconcileInitBundleSecrets not called due to ROX-9023. TODO(ROX-9969): call after the init-bundle cert rotation stabilization. } - if err := r.EnsureSecret(ctx, "central-tls", r.validateAndConsumeCentralTLSData, r.generateCentralTLSData, commonLabels.TLSSecretLabels()); err != nil { + if err := r.EnsureSecret(ctx, common.TLSSecretName, r.validateAndConsumeCentralTLSData, r.generateCentralTLSData, commonLabels.TLSSecretLabels()); err != nil { return errors.Wrap(err, "reconciling central-tls secret failed") } diff --git a/operator/internal/common/tls.go b/operator/internal/common/tls.go new file mode 100644 index 0000000000000..d81d3a3f767b1 --- /dev/null +++ b/operator/internal/common/tls.go @@ -0,0 +1,5 @@ +package common + +const ( + TLSSecretName = "central-tls" +) diff --git a/operator/internal/route/translation.go b/operator/internal/route/translation.go index b894b0b712a6e..b75acf162b4c3 100644 --- a/operator/internal/route/translation.go +++ b/operator/internal/route/translation.go @@ -5,18 +5,15 @@ import ( "fmt" "github.com/go-logr/logr" + "github.com/stackrox/rox/operator/internal/common" "github.com/stackrox/rox/operator/internal/values/translation" "github.com/stackrox/rox/pkg/k8sutil" + "github.com/stackrox/rox/pkg/mtls" "helm.sh/helm/v3/pkg/chartutil" corev1 "k8s.io/api/core/v1" ctrlClient "sigs.k8s.io/controller-runtime/pkg/client" ) -const ( - tlsSecretCAKey = "ca.pem" - tlsSecretName = "central-tls" -) - // NewRouteInjector returns an object which injects the Central certificate authority into route chart values. func NewRouteInjector(client ctrlClient.Reader, log logr.Logger) *routeInjector { return &routeInjector{ @@ -37,8 +34,8 @@ func (i *routeInjector) Enrich(ctx context.Context, obj k8sutil.Object, vals cha namespaceName := obj.GetNamespace() tlsSecret := &corev1.Secret{} - if err := i.client.Get(ctx, ctrlClient.ObjectKey{Name: tlsSecretName, Namespace: namespaceName}, tlsSecret); err != nil { - return nil, fmt.Errorf("getting secret %s/%s: %w", namespaceName, tlsSecretName, err) + if err := i.client.Get(ctx, ctrlClient.ObjectKey{Name: common.TLSSecretName, Namespace: namespaceName}, tlsSecret); err != nil { + return nil, fmt.Errorf("getting secret %s/%s: %w", namespaceName, common.TLSSecretName, err) } routeVals := chartutil.Values{ @@ -47,7 +44,7 @@ func (i *routeInjector) Enrich(ctx context.Context, obj k8sutil.Object, vals cha "route": map[string]interface{}{ "reencrypt": map[string]interface{}{ "tls": map[string]interface{}{ - "destinationCACertificate": string(tlsSecret.Data[tlsSecretCAKey]), + "destinationCACertificate": string(tlsSecret.Data[mtls.CACertFileName]), }, }, }, diff --git a/operator/internal/route/translation_test.go b/operator/internal/route/translation_test.go index 20851b22d711f..325af1b70159e 100644 --- a/operator/internal/route/translation_test.go +++ b/operator/internal/route/translation_test.go @@ -5,6 +5,8 @@ import ( "testing" "github.com/go-logr/logr" + "github.com/stackrox/rox/operator/internal/common" + "github.com/stackrox/rox/pkg/mtls" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "helm.sh/helm/v3/pkg/chartutil" @@ -37,9 +39,9 @@ func Test_injector_Enrich(t *testing.T) { tlsSecret := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Namespace: "some-ns", - Name: tlsSecretName, + Name: common.TLSSecretName, }, - Data: map[string][]byte{tlsSecretCAKey: []byte(centralCA)}, + Data: map[string][]byte{mtls.CACertFileName: []byte(centralCA)}, } i := NewRouteInjector(fake.NewFakeClient(tlsSecret), logr.New(nil)) vals := chartutil.Values{} From e74a7e42a064934bc9351bece25168f12b3cb40f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20He=C3=9Felmann?= Date: Thu, 10 Apr 2025 15:13:02 +0200 Subject: [PATCH 13/20] use utils.GetWithFallbackToUncached --- .../internal/central/reconciler/reconciler.go | 2 +- operator/internal/route/translation.go | 16 +++++++++++++--- operator/internal/route/translation_test.go | 2 +- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/operator/internal/central/reconciler/reconciler.go b/operator/internal/central/reconciler/reconciler.go index 76c380bba8165..9e7c7b6b3d61d 100644 --- a/operator/internal/central/reconciler/reconciler.go +++ b/operator/internal/central/reconciler/reconciler.go @@ -61,7 +61,7 @@ func RegisterNewReconciler(mgr ctrl.Manager, selector string) error { // are set properly. legacy.NewImagePullSecretReferenceInjector(mgr.GetAPIReader(), "imagePullSecrets", "stackrox", "stackrox-scanner", "stackrox-scanner-v4"), - route.NewRouteInjector(mgr.GetClient(), mgr.GetLogger()), + route.NewRouteInjector(mgr.GetClient(), mgr.GetAPIReader(), mgr.GetLogger()), ), opts..., ) diff --git a/operator/internal/route/translation.go b/operator/internal/route/translation.go index b75acf162b4c3..34baf42b0f260 100644 --- a/operator/internal/route/translation.go +++ b/operator/internal/route/translation.go @@ -6,6 +6,7 @@ import ( "github.com/go-logr/logr" "github.com/stackrox/rox/operator/internal/common" + "github.com/stackrox/rox/operator/internal/utils" "github.com/stackrox/rox/operator/internal/values/translation" "github.com/stackrox/rox/pkg/k8sutil" "github.com/stackrox/rox/pkg/mtls" @@ -15,15 +16,18 @@ import ( ) // NewRouteInjector returns an object which injects the Central certificate authority into route chart values. -func NewRouteInjector(client ctrlClient.Reader, log logr.Logger) *routeInjector { +// It takes a context and controller client. +func NewRouteInjector(client ctrlClient.Client, direct ctrlClient.Reader, log logr.Logger) *routeInjector { return &routeInjector{ client: client, + direct: direct, log: log, } } type routeInjector struct { - client ctrlClient.Reader + client ctrlClient.Client + direct ctrlClient.Reader log logr.Logger } @@ -31,10 +35,16 @@ var _ translation.Enricher = &routeInjector{} // Enrich injects the Central certificate authority into the reencrypt route. func (i *routeInjector) Enrich(ctx context.Context, obj k8sutil.Object, vals chartutil.Values) (chartutil.Values, error) { + destCAPath := "central.exposure.route.reencrypt.tls.destinationCACertificate" + if destCA, err := vals.PathValue(destCAPath); destCA != "" && err == nil { + return vals, nil + } + namespaceName := obj.GetNamespace() tlsSecret := &corev1.Secret{} - if err := i.client.Get(ctx, ctrlClient.ObjectKey{Name: common.TLSSecretName, Namespace: namespaceName}, tlsSecret); err != nil { + key := ctrlClient.ObjectKey{Name: common.TLSSecretName, Namespace: namespaceName} + if err := utils.GetWithFallbackToUncached(ctx, i.client, i.direct, key, tlsSecret); err != nil { return nil, fmt.Errorf("getting secret %s/%s: %w", namespaceName, common.TLSSecretName, err) } diff --git a/operator/internal/route/translation_test.go b/operator/internal/route/translation_test.go index 325af1b70159e..ae5aec0a9cf1b 100644 --- a/operator/internal/route/translation_test.go +++ b/operator/internal/route/translation_test.go @@ -43,7 +43,7 @@ func Test_injector_Enrich(t *testing.T) { }, Data: map[string][]byte{mtls.CACertFileName: []byte(centralCA)}, } - i := NewRouteInjector(fake.NewFakeClient(tlsSecret), logr.New(nil)) + i := NewRouteInjector(fake.NewFakeClient(tlsSecret), fake.NewFakeClient(tlsSecret), logr.New(nil)) vals := chartutil.Values{} if tt.destCAValue != "" { vals["central"] = map[string]interface{}{ From c9ec5fd140712d86dfd93f8719671dd20379824d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20He=C3=9Felmann?= Date: Fri, 11 Apr 2025 09:03:16 +0200 Subject: [PATCH 14/20] generate manifests --- .../platform.stackrox.io_centrals.yaml | 20 ++++++---- .../rhacs-operator.clusterserviceversion.yaml | 37 ++++++++++++------- .../bases/platform.stackrox.io_centrals.yaml | 20 ++++++---- .../rhacs-operator.clusterserviceversion.yaml | 34 +++++++++++------ 4 files changed, 71 insertions(+), 40 deletions(-) diff --git a/operator/bundle/manifests/platform.stackrox.io_centrals.yaml b/operator/bundle/manifests/platform.stackrox.io_centrals.yaml index 415efe58300d4..909f9234eaa5a 100644 --- a/operator/bundle/manifests/platform.stackrox.io_centrals.yaml +++ b/operator/bundle/manifests/platform.stackrox.io_centrals.yaml @@ -380,6 +380,9 @@ spec: properties: enabled: default: false + description: |- + Expose central with a passthrough route. + The passthrough route is still required when using a reencrypt route. type: boolean host: description: |- @@ -391,10 +394,13 @@ spec: Set up a central route with reencrypt tls termination. For reencrypt routes, the request is terminated on the OpenShift router with a custom certificate. The request is then reencrypted by the OpenShift router and sent to central. - --tls--> --tls--> + [user] --TLS--> [OpenShift router] --TLS--> [central] properties: enabled: default: false + description: |- + Expose central with a reencrypt route. + Requires a passthrough route for sensor communication. type: boolean host: description: |- @@ -406,20 +412,20 @@ spec: a reencrypt Route. properties: caCertificate: - description: PEM encoded certificate chain that - may be used to establish a complete chain of - trust. + description: |- + The PEM encoded certificate chain that may be used to establish a complete chain of trust. + Defaults to the OpenShift certificate authority. type: string certificate: description: |- The PEM encoded certificate that is served on the route. Must be a single serving certificate instead of a certificate chain. - Defaults to a certificate signed by the OpenShift certificate authority is used. + Defaults to a certificate signed by the OpenShift certificate authority. type: string destinationCACertificate: description: |- - The CA certificate of the final destination. Should be provided because the OpenShift - router uses it for health checks on the secure connection. + The CA certificate of the final destination, i.e. of central. + Used by the OpenShift router for health checks on the secure connection. Defaults to the Central certificate authority. type: string key: diff --git a/operator/bundle/manifests/rhacs-operator.clusterserviceversion.yaml b/operator/bundle/manifests/rhacs-operator.clusterserviceversion.yaml index 28861b6c6ea17..89de36c221c55 100644 --- a/operator/bundle/manifests/rhacs-operator.clusterserviceversion.yaml +++ b/operator/bundle/manifests/rhacs-operator.clusterserviceversion.yaml @@ -387,7 +387,10 @@ spec: path: central.exposure.nodePort.port x-descriptors: - urn:alm:descriptor:com.tectonic.ui:fieldDependency:central.exposure.nodePort.enabled:true - - displayName: Enabled + - description: 'Expose central with a passthrough route. + + The passthrough route is still required when using a reencrypt route.' + displayName: Enabled path: central.exposure.route.enabled - description: 'Specify a custom hostname for the central route. @@ -402,10 +405,13 @@ spec: The request is then reencrypted by the OpenShift router and sent to central. - --tls--> --tls--> ' - displayName: Reencrypt + [user] --TLS--> [OpenShift router] --TLS--> [central]' + displayName: Re-Encrypt Route path: central.exposure.route.reencrypt - - displayName: Enabled + - description: 'Expose central with a reencrypt route. + + Requires a passthrough route for sensor communication.' + displayName: Enabled path: central.exposure.route.reencrypt.enabled - description: 'Specify a custom hostname for the central reencrypt route. @@ -413,32 +419,35 @@ spec: by the OpenShift route operator.' displayName: Host path: central.exposure.route.reencrypt.host - - description: PEM encoded certificate chain that may be used to establish a - complete chain of trust. - displayName: Ca Certificate + - description: TLS settings for exposing central via a reencrypt Route. + displayName: TLS Settings + path: central.exposure.route.reencrypt.tls + - description: 'The PEM encoded certificate chain that may be used to establish + a complete chain of trust. + + Defaults to the OpenShift certificate authority.' + displayName: CA Certificate path: central.exposure.route.reencrypt.tls.caCertificate - description: 'The PEM encoded certificate that is served on the route. Must be a single serving certificate instead of a certificate chain. - Defaults to a certificate signed by the OpenShift certificate authority - is used.' + Defaults to a certificate signed by the OpenShift certificate authority.' displayName: Certificate path: central.exposure.route.reencrypt.tls.certificate - - description: 'The CA certificate of the final destination. Should be provided - because the OpenShift + - description: 'The CA certificate of the final destination, i.e. of central. - router uses it for health checks on the secure connection. + Used by the OpenShift router for health checks on the secure connection. Defaults to the Central certificate authority.' - displayName: Destination CACertificate + displayName: Destination CA Certificate path: central.exposure.route.reencrypt.tls.destinationCACertificate - description: 'The PEM encoded private key of the certificate that is served on the route. Defaults to a certificate signed by the OpenShift certificate authority.' - displayName: Key + displayName: Private Key path: central.exposure.route.reencrypt.tls.key - description: 'Expose the monitoring endpoint. A new service, "monitoring", diff --git a/operator/config/crd/bases/platform.stackrox.io_centrals.yaml b/operator/config/crd/bases/platform.stackrox.io_centrals.yaml index d7d2472909474..a3294173da99c 100644 --- a/operator/config/crd/bases/platform.stackrox.io_centrals.yaml +++ b/operator/config/crd/bases/platform.stackrox.io_centrals.yaml @@ -380,6 +380,9 @@ spec: properties: enabled: default: false + description: |- + Expose central with a passthrough route. + The passthrough route is still required when using a reencrypt route. type: boolean host: description: |- @@ -391,10 +394,13 @@ spec: Set up a central route with reencrypt tls termination. For reencrypt routes, the request is terminated on the OpenShift router with a custom certificate. The request is then reencrypted by the OpenShift router and sent to central. - --tls--> --tls--> + [user] --TLS--> [OpenShift router] --TLS--> [central] properties: enabled: default: false + description: |- + Expose central with a reencrypt route. + Requires a passthrough route for sensor communication. type: boolean host: description: |- @@ -406,20 +412,20 @@ spec: a reencrypt Route. properties: caCertificate: - description: PEM encoded certificate chain that - may be used to establish a complete chain of - trust. + description: |- + The PEM encoded certificate chain that may be used to establish a complete chain of trust. + Defaults to the OpenShift certificate authority. type: string certificate: description: |- The PEM encoded certificate that is served on the route. Must be a single serving certificate instead of a certificate chain. - Defaults to a certificate signed by the OpenShift certificate authority is used. + Defaults to a certificate signed by the OpenShift certificate authority. type: string destinationCACertificate: description: |- - The CA certificate of the final destination. Should be provided because the OpenShift - router uses it for health checks on the secure connection. + The CA certificate of the final destination, i.e. of central. + Used by the OpenShift router for health checks on the secure connection. Defaults to the Central certificate authority. type: string key: diff --git a/operator/config/manifests/bases/rhacs-operator.clusterserviceversion.yaml b/operator/config/manifests/bases/rhacs-operator.clusterserviceversion.yaml index 9d38621727c15..389ab09dc93ba 100644 --- a/operator/config/manifests/bases/rhacs-operator.clusterserviceversion.yaml +++ b/operator/config/manifests/bases/rhacs-operator.clusterserviceversion.yaml @@ -83,13 +83,20 @@ spec: - description: Expose Central through an OpenShift route. displayName: Route path: central.exposure.route - - displayName: Enabled + - description: |- + Expose central with a passthrough route. + The passthrough route is still required when using a reencrypt route. + displayName: Enabled path: central.exposure.route.enabled - - displayName: Enabled + - description: |- + Expose central with a reencrypt route. + Requires a passthrough route for sensor communication. + displayName: Enabled path: central.exposure.route.reencrypt.enabled - - description: PEM encoded certificate chain that may be used to establish a - complete chain of trust. - displayName: Ca Certificate + - description: |- + The PEM encoded certificate chain that may be used to establish a complete chain of trust. + Defaults to the OpenShift certificate authority. + displayName: CA Certificate path: central.exposure.route.reencrypt.tls.caCertificate - description: |- Expose the monitoring endpoint. A new service, "monitoring", @@ -265,7 +272,7 @@ spec: - description: |- The PEM encoded certificate that is served on the route. Must be a single serving certificate instead of a certificate chain. - Defaults to a certificate signed by the OpenShift certificate authority is used. + Defaults to a certificate signed by the OpenShift certificate authority. displayName: Certificate path: central.exposure.route.reencrypt.tls.certificate - description: |- @@ -365,14 +372,17 @@ spec: Set up a central route with reencrypt tls termination. For reencrypt routes, the request is terminated on the OpenShift router with a custom certificate. The request is then reencrypted by the OpenShift router and sent to central. - --tls--> --tls--> - displayName: Reencrypt + [user] --TLS--> [OpenShift router] --TLS--> [central] + displayName: Re-Encrypt Route path: central.exposure.route.reencrypt + - description: TLS settings for exposing central via a reencrypt Route. + displayName: TLS Settings + path: central.exposure.route.reencrypt.tls - description: |- - The CA certificate of the final destination. Should be provided because the OpenShift - router uses it for health checks on the secure connection. + The CA certificate of the final destination, i.e. of central. + Used by the OpenShift router for health checks on the secure connection. Defaults to the Central certificate authority. - displayName: Destination CACertificate + displayName: Destination CA Certificate path: central.exposure.route.reencrypt.tls.destinationCACertificate - description: |- The name of the storage class to use for the PVC. If your cluster is not configured with a default storage @@ -434,7 +444,7 @@ spec: - description: |- The PEM encoded private key of the certificate that is served on the route. Defaults to a certificate signed by the OpenShift certificate authority. - displayName: Key + displayName: Private Key path: central.exposure.route.reencrypt.tls.key - description: |- Configures monitoring endpoint for Central. The monitoring endpoint From 86ab4ca6d9bc27c5f65b621c8259eb4073037e72 Mon Sep 17 00:00:00 2001 From: Stephan Hesselmann Date: Tue, 15 Apr 2025 13:08:15 +0200 Subject: [PATCH 15/20] Update operator/api/v1alpha1/central_types.go Co-authored-by: Marcin Owsiany --- operator/api/v1alpha1/central_types.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/operator/api/v1alpha1/central_types.go b/operator/api/v1alpha1/central_types.go index 99fb4bc396633..6baf15a9b0d5b 100644 --- a/operator/api/v1alpha1/central_types.go +++ b/operator/api/v1alpha1/central_types.go @@ -481,7 +481,7 @@ type ExposureRoute struct { //+operator-sdk:csv:customresourcedefinitions:type=spec,order=2 Host *string `json:"host,omitempty"` - // Set up a central route with reencrypt tls termination. + // Set up a central route with reencrypt TLS termination. // For reencrypt routes, the request is terminated on the OpenShift router with a custom certificate. // The request is then reencrypted by the OpenShift router and sent to central. // [user] --TLS--> [OpenShift router] --TLS--> [central] From c2e91a898b4a3ab68f66b16699851b6953715d11 Mon Sep 17 00:00:00 2001 From: Stephan Hesselmann Date: Tue, 15 Apr 2025 13:11:21 +0200 Subject: [PATCH 16/20] Update operator/internal/route/translation.go Co-authored-by: Marcin Owsiany --- operator/internal/route/translation.go | 1 - 1 file changed, 1 deletion(-) diff --git a/operator/internal/route/translation.go b/operator/internal/route/translation.go index 34baf42b0f260..06df052871398 100644 --- a/operator/internal/route/translation.go +++ b/operator/internal/route/translation.go @@ -42,7 +42,6 @@ func (i *routeInjector) Enrich(ctx context.Context, obj k8sutil.Object, vals cha namespaceName := obj.GetNamespace() tlsSecret := &corev1.Secret{} - key := ctrlClient.ObjectKey{Name: common.TLSSecretName, Namespace: namespaceName} if err := utils.GetWithFallbackToUncached(ctx, i.client, i.direct, key, tlsSecret); err != nil { return nil, fmt.Errorf("getting secret %s/%s: %w", namespaceName, common.TLSSecretName, err) From e77bb30eda5620454e4d620ad6bb466645057fc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20He=C3=9Felmann?= Date: Tue, 15 Apr 2025 13:11:04 +0200 Subject: [PATCH 17/20] adjust comment and rename const --- operator/api/v1alpha1/central_types.go | 3 +-- .../bundle/manifests/platform.stackrox.io_centrals.yaml | 6 ++---- .../manifests/rhacs-operator.clusterserviceversion.yaml | 6 ++---- .../config/crd/bases/platform.stackrox.io_centrals.yaml | 6 ++---- .../bases/rhacs-operator.clusterserviceversion.yaml | 6 ++---- operator/internal/central/extensions/reconcile_tls.go | 2 +- operator/internal/common/tls.go | 2 +- operator/internal/route/translation.go | 4 ++-- operator/internal/route/translation_test.go | 2 +- 9 files changed, 14 insertions(+), 23 deletions(-) diff --git a/operator/api/v1alpha1/central_types.go b/operator/api/v1alpha1/central_types.go index 6baf15a9b0d5b..db2562fcf2f16 100644 --- a/operator/api/v1alpha1/central_types.go +++ b/operator/api/v1alpha1/central_types.go @@ -471,7 +471,6 @@ type ExposureNodePort struct { // ExposureRoute defines settings for exposing central via a Route. type ExposureRoute struct { // Expose central with a passthrough route. - // The passthrough route is still required when using a reencrypt route. //+kubebuilder:default=false //+operator-sdk:csv:customresourcedefinitions:type=spec,order=1 Enabled *bool `json:"enabled,omitempty"` @@ -492,7 +491,7 @@ type ExposureRoute struct { // ExposureRouteReencrypt defines settings for exposing central via a reencrypt Route. type ExposureRouteReencrypt struct { // Expose central with a reencrypt route. - // Requires a passthrough route for sensor communication. + // Should not be used for sensor communication. //+kubebuilder:default=false //+operator-sdk:csv:customresourcedefinitions:type=spec,order=1 Enabled *bool `json:"enabled,omitempty"` diff --git a/operator/bundle/manifests/platform.stackrox.io_centrals.yaml b/operator/bundle/manifests/platform.stackrox.io_centrals.yaml index 909f9234eaa5a..b6e18db03be34 100644 --- a/operator/bundle/manifests/platform.stackrox.io_centrals.yaml +++ b/operator/bundle/manifests/platform.stackrox.io_centrals.yaml @@ -380,9 +380,7 @@ spec: properties: enabled: default: false - description: |- - Expose central with a passthrough route. - The passthrough route is still required when using a reencrypt route. + description: Expose central with a passthrough route. type: boolean host: description: |- @@ -400,7 +398,7 @@ spec: default: false description: |- Expose central with a reencrypt route. - Requires a passthrough route for sensor communication. + Should not be used for sensor communication. type: boolean host: description: |- diff --git a/operator/bundle/manifests/rhacs-operator.clusterserviceversion.yaml b/operator/bundle/manifests/rhacs-operator.clusterserviceversion.yaml index 89de36c221c55..277dc905e73ce 100644 --- a/operator/bundle/manifests/rhacs-operator.clusterserviceversion.yaml +++ b/operator/bundle/manifests/rhacs-operator.clusterserviceversion.yaml @@ -387,9 +387,7 @@ spec: path: central.exposure.nodePort.port x-descriptors: - urn:alm:descriptor:com.tectonic.ui:fieldDependency:central.exposure.nodePort.enabled:true - - description: 'Expose central with a passthrough route. - - The passthrough route is still required when using a reencrypt route.' + - description: Expose central with a passthrough route. displayName: Enabled path: central.exposure.route.enabled - description: 'Specify a custom hostname for the central route. @@ -410,7 +408,7 @@ spec: path: central.exposure.route.reencrypt - description: 'Expose central with a reencrypt route. - Requires a passthrough route for sensor communication.' + Should not be used for sensor communication.' displayName: Enabled path: central.exposure.route.reencrypt.enabled - description: 'Specify a custom hostname for the central reencrypt route. diff --git a/operator/config/crd/bases/platform.stackrox.io_centrals.yaml b/operator/config/crd/bases/platform.stackrox.io_centrals.yaml index a3294173da99c..37b6f3074681d 100644 --- a/operator/config/crd/bases/platform.stackrox.io_centrals.yaml +++ b/operator/config/crd/bases/platform.stackrox.io_centrals.yaml @@ -380,9 +380,7 @@ spec: properties: enabled: default: false - description: |- - Expose central with a passthrough route. - The passthrough route is still required when using a reencrypt route. + description: Expose central with a passthrough route. type: boolean host: description: |- @@ -400,7 +398,7 @@ spec: default: false description: |- Expose central with a reencrypt route. - Requires a passthrough route for sensor communication. + Should not be used for sensor communication. type: boolean host: description: |- diff --git a/operator/config/manifests/bases/rhacs-operator.clusterserviceversion.yaml b/operator/config/manifests/bases/rhacs-operator.clusterserviceversion.yaml index 389ab09dc93ba..0aa200983aaa3 100644 --- a/operator/config/manifests/bases/rhacs-operator.clusterserviceversion.yaml +++ b/operator/config/manifests/bases/rhacs-operator.clusterserviceversion.yaml @@ -83,14 +83,12 @@ spec: - description: Expose Central through an OpenShift route. displayName: Route path: central.exposure.route - - description: |- - Expose central with a passthrough route. - The passthrough route is still required when using a reencrypt route. + - description: Expose central with a passthrough route. displayName: Enabled path: central.exposure.route.enabled - description: |- Expose central with a reencrypt route. - Requires a passthrough route for sensor communication. + Should not be used for sensor communication. displayName: Enabled path: central.exposure.route.reencrypt.enabled - description: |- diff --git a/operator/internal/central/extensions/reconcile_tls.go b/operator/internal/central/extensions/reconcile_tls.go index ec148ae2e0641..4d4a356f7c006 100644 --- a/operator/internal/central/extensions/reconcile_tls.go +++ b/operator/internal/central/extensions/reconcile_tls.go @@ -65,7 +65,7 @@ func (r *createCentralTLSExtensionRun) Execute(ctx context.Context) error { // reconcileInitBundleSecrets not called due to ROX-9023. TODO(ROX-9969): call after the init-bundle cert rotation stabilization. } - if err := r.EnsureSecret(ctx, common.TLSSecretName, r.validateAndConsumeCentralTLSData, r.generateCentralTLSData, commonLabels.TLSSecretLabels()); err != nil { + if err := r.EnsureSecret(ctx, common.CentralTLSSecretName, r.validateAndConsumeCentralTLSData, r.generateCentralTLSData, commonLabels.TLSSecretLabels()); err != nil { return errors.Wrap(err, "reconciling central-tls secret failed") } diff --git a/operator/internal/common/tls.go b/operator/internal/common/tls.go index d81d3a3f767b1..308331b9276d0 100644 --- a/operator/internal/common/tls.go +++ b/operator/internal/common/tls.go @@ -1,5 +1,5 @@ package common const ( - TLSSecretName = "central-tls" + CentralTLSSecretName = "central-tls" ) diff --git a/operator/internal/route/translation.go b/operator/internal/route/translation.go index 06df052871398..b93245c77be36 100644 --- a/operator/internal/route/translation.go +++ b/operator/internal/route/translation.go @@ -42,9 +42,9 @@ func (i *routeInjector) Enrich(ctx context.Context, obj k8sutil.Object, vals cha namespaceName := obj.GetNamespace() tlsSecret := &corev1.Secret{} - key := ctrlClient.ObjectKey{Name: common.TLSSecretName, Namespace: namespaceName} + key := ctrlClient.ObjectKey{Name: common.CentralTLSSecretName, Namespace: namespaceName} if err := utils.GetWithFallbackToUncached(ctx, i.client, i.direct, key, tlsSecret); err != nil { - return nil, fmt.Errorf("getting secret %s/%s: %w", namespaceName, common.TLSSecretName, err) + return nil, fmt.Errorf("getting secret %s/%s: %w", namespaceName, common.CentralTLSSecretName, err) } routeVals := chartutil.Values{ diff --git a/operator/internal/route/translation_test.go b/operator/internal/route/translation_test.go index ae5aec0a9cf1b..556b4167442e9 100644 --- a/operator/internal/route/translation_test.go +++ b/operator/internal/route/translation_test.go @@ -39,7 +39,7 @@ func Test_injector_Enrich(t *testing.T) { tlsSecret := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Namespace: "some-ns", - Name: common.TLSSecretName, + Name: common.CentralTLSSecretName, }, Data: map[string][]byte{mtls.CACertFileName: []byte(centralCA)}, } From c33279527045e6aafb81f8992f3d5b99ff479c94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20He=C3=9Felmann?= Date: Tue, 15 Apr 2025 14:33:56 +0200 Subject: [PATCH 18/20] fix manifests --- operator/bundle/manifests/platform.stackrox.io_centrals.yaml | 2 +- .../bundle/manifests/rhacs-operator.clusterserviceversion.yaml | 2 +- operator/config/crd/bases/platform.stackrox.io_centrals.yaml | 2 +- .../manifests/bases/rhacs-operator.clusterserviceversion.yaml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/operator/bundle/manifests/platform.stackrox.io_centrals.yaml b/operator/bundle/manifests/platform.stackrox.io_centrals.yaml index b6e18db03be34..855bcaba40c34 100644 --- a/operator/bundle/manifests/platform.stackrox.io_centrals.yaml +++ b/operator/bundle/manifests/platform.stackrox.io_centrals.yaml @@ -389,7 +389,7 @@ spec: type: string reencrypt: description: |- - Set up a central route with reencrypt tls termination. + Set up a central route with reencrypt TLS termination. For reencrypt routes, the request is terminated on the OpenShift router with a custom certificate. The request is then reencrypted by the OpenShift router and sent to central. [user] --TLS--> [OpenShift router] --TLS--> [central] diff --git a/operator/bundle/manifests/rhacs-operator.clusterserviceversion.yaml b/operator/bundle/manifests/rhacs-operator.clusterserviceversion.yaml index 277dc905e73ce..654f4bc9833c0 100644 --- a/operator/bundle/manifests/rhacs-operator.clusterserviceversion.yaml +++ b/operator/bundle/manifests/rhacs-operator.clusterserviceversion.yaml @@ -396,7 +396,7 @@ spec: by the OpenShift route operator.' displayName: Host path: central.exposure.route.host - - description: 'Set up a central route with reencrypt tls termination. + - description: 'Set up a central route with reencrypt TLS termination. For reencrypt routes, the request is terminated on the OpenShift router with a custom certificate. diff --git a/operator/config/crd/bases/platform.stackrox.io_centrals.yaml b/operator/config/crd/bases/platform.stackrox.io_centrals.yaml index 37b6f3074681d..375aed4250cf2 100644 --- a/operator/config/crd/bases/platform.stackrox.io_centrals.yaml +++ b/operator/config/crd/bases/platform.stackrox.io_centrals.yaml @@ -389,7 +389,7 @@ spec: type: string reencrypt: description: |- - Set up a central route with reencrypt tls termination. + Set up a central route with reencrypt TLS termination. For reencrypt routes, the request is terminated on the OpenShift router with a custom certificate. The request is then reencrypted by the OpenShift router and sent to central. [user] --TLS--> [OpenShift router] --TLS--> [central] diff --git a/operator/config/manifests/bases/rhacs-operator.clusterserviceversion.yaml b/operator/config/manifests/bases/rhacs-operator.clusterserviceversion.yaml index 0aa200983aaa3..3d845f169b289 100644 --- a/operator/config/manifests/bases/rhacs-operator.clusterserviceversion.yaml +++ b/operator/config/manifests/bases/rhacs-operator.clusterserviceversion.yaml @@ -367,7 +367,7 @@ spec: displayName: Node Port path: central.exposure.nodePort - description: |- - Set up a central route with reencrypt tls termination. + Set up a central route with reencrypt TLS termination. For reencrypt routes, the request is terminated on the OpenShift router with a custom certificate. The request is then reencrypted by the OpenShift router and sent to central. [user] --TLS--> [OpenShift router] --TLS--> [central] From 567d1441dbdbf00c1a66367ad00cbceaa0c0300b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20He=C3=9Felmann?= Date: Tue, 15 Apr 2025 14:57:56 +0200 Subject: [PATCH 19/20] rename central -> Central in route config --- operator/api/v1alpha1/central_types.go | 26 +++++++++---------- .../platform.stackrox.io_centrals.yaml | 18 ++++++------- .../rhacs-operator.clusterserviceversion.yaml | 18 ++++++------- .../bases/platform.stackrox.io_centrals.yaml | 18 ++++++------- .../rhacs-operator.clusterserviceversion.yaml | 18 ++++++------- 5 files changed, 49 insertions(+), 49 deletions(-) diff --git a/operator/api/v1alpha1/central_types.go b/operator/api/v1alpha1/central_types.go index db2562fcf2f16..b24d6748ac0b2 100644 --- a/operator/api/v1alpha1/central_types.go +++ b/operator/api/v1alpha1/central_types.go @@ -422,7 +422,7 @@ type DBPersistentVolumeClaim struct { StorageClassName *string `json:"storageClassName,omitempty"` } -// Exposure defines how central is exposed. +// Exposure defines how Central is exposed. type Exposure struct { // Expose Central through an OpenShift route. //+operator-sdk:csv:customresourcedefinitions:type=spec,order=1,displayName="Route" @@ -468,45 +468,45 @@ type ExposureNodePort struct { Port *int32 `json:"port,omitempty"` } -// ExposureRoute defines settings for exposing central via a Route. +// ExposureRoute defines settings for exposing Central via a Route. type ExposureRoute struct { - // Expose central with a passthrough route. + // Expose Central with a passthrough route. //+kubebuilder:default=false //+operator-sdk:csv:customresourcedefinitions:type=spec,order=1 Enabled *bool `json:"enabled,omitempty"` - // Specify a custom hostname for the central route. + // Specify a custom hostname for the Central route. // If unspecified, an appropriate default value will be automatically chosen by the OpenShift route operator. //+operator-sdk:csv:customresourcedefinitions:type=spec,order=2 Host *string `json:"host,omitempty"` - // Set up a central route with reencrypt TLS termination. + // Set up a Central route with reencrypt TLS termination. // For reencrypt routes, the request is terminated on the OpenShift router with a custom certificate. - // The request is then reencrypted by the OpenShift router and sent to central. - // [user] --TLS--> [OpenShift router] --TLS--> [central] + // The request is then reencrypted by the OpenShift router and sent to Central. + // [user] --TLS--> [OpenShift router] --TLS--> [Central] //+operator-sdk:csv:customresourcedefinitions:type=spec,order=3,displayName="Re-Encrypt Route" Reencrypt *ExposureRouteReencrypt `json:"reencrypt,omitempty"` } -// ExposureRouteReencrypt defines settings for exposing central via a reencrypt Route. +// ExposureRouteReencrypt defines settings for exposing Central via a reencrypt Route. type ExposureRouteReencrypt struct { - // Expose central with a reencrypt route. + // Expose Central with a reencrypt route. // Should not be used for sensor communication. //+kubebuilder:default=false //+operator-sdk:csv:customresourcedefinitions:type=spec,order=1 Enabled *bool `json:"enabled,omitempty"` - // Specify a custom hostname for the central reencrypt route. + // Specify a custom hostname for the Central reencrypt route. // If unspecified, an appropriate default value will be automatically chosen by the OpenShift route operator. //+operator-sdk:csv:customresourcedefinitions:type=spec,order=2 Host *string `json:"host,omitempty"` - // TLS settings for exposing central via a reencrypt Route. + // TLS settings for exposing Central via a reencrypt Route. //+operator-sdk:csv:customresourcedefinitions:type=spec,order=3,displayName="TLS Settings" TLS *ExposureRouteReencryptTLS `json:"tls,omitempty"` } -// ExposureRouteReencryptTLS defines TLS settings for exposing central via a reencrypt Route. +// ExposureRouteReencryptTLS defines TLS settings for exposing Central via a reencrypt Route. type ExposureRouteReencryptTLS struct { // The PEM encoded certificate chain that may be used to establish a complete chain of trust. // Defaults to the OpenShift certificate authority. @@ -519,7 +519,7 @@ type ExposureRouteReencryptTLS struct { //+operator-sdk:csv:customresourcedefinitions:type=spec,order=2,displayName="Certificate" Certificate *string `json:"certificate,omitempty"` - // The CA certificate of the final destination, i.e. of central. + // The CA certificate of the final destination, i.e. of Central. // Used by the OpenShift router for health checks on the secure connection. // Defaults to the Central certificate authority. //+operator-sdk:csv:customresourcedefinitions:type=spec,order=3,displayName="Destination CA Certificate" diff --git a/operator/bundle/manifests/platform.stackrox.io_centrals.yaml b/operator/bundle/manifests/platform.stackrox.io_centrals.yaml index 855bcaba40c34..2b69fe1942806 100644 --- a/operator/bundle/manifests/platform.stackrox.io_centrals.yaml +++ b/operator/bundle/manifests/platform.stackrox.io_centrals.yaml @@ -380,33 +380,33 @@ spec: properties: enabled: default: false - description: Expose central with a passthrough route. + description: Expose Central with a passthrough route. type: boolean host: description: |- - Specify a custom hostname for the central route. + Specify a custom hostname for the Central route. If unspecified, an appropriate default value will be automatically chosen by the OpenShift route operator. type: string reencrypt: description: |- - Set up a central route with reencrypt TLS termination. + Set up a Central route with reencrypt TLS termination. For reencrypt routes, the request is terminated on the OpenShift router with a custom certificate. - The request is then reencrypted by the OpenShift router and sent to central. - [user] --TLS--> [OpenShift router] --TLS--> [central] + The request is then reencrypted by the OpenShift router and sent to Central. + [user] --TLS--> [OpenShift router] --TLS--> [Central] properties: enabled: default: false description: |- - Expose central with a reencrypt route. + Expose Central with a reencrypt route. Should not be used for sensor communication. type: boolean host: description: |- - Specify a custom hostname for the central reencrypt route. + Specify a custom hostname for the Central reencrypt route. If unspecified, an appropriate default value will be automatically chosen by the OpenShift route operator. type: string tls: - description: TLS settings for exposing central via + description: TLS settings for exposing Central via a reencrypt Route. properties: caCertificate: @@ -422,7 +422,7 @@ spec: type: string destinationCACertificate: description: |- - The CA certificate of the final destination, i.e. of central. + The CA certificate of the final destination, i.e. of Central. Used by the OpenShift router for health checks on the secure connection. Defaults to the Central certificate authority. type: string diff --git a/operator/bundle/manifests/rhacs-operator.clusterserviceversion.yaml b/operator/bundle/manifests/rhacs-operator.clusterserviceversion.yaml index 654f4bc9833c0..7b72107ad6e05 100644 --- a/operator/bundle/manifests/rhacs-operator.clusterserviceversion.yaml +++ b/operator/bundle/manifests/rhacs-operator.clusterserviceversion.yaml @@ -387,37 +387,37 @@ spec: path: central.exposure.nodePort.port x-descriptors: - urn:alm:descriptor:com.tectonic.ui:fieldDependency:central.exposure.nodePort.enabled:true - - description: Expose central with a passthrough route. + - description: Expose Central with a passthrough route. displayName: Enabled path: central.exposure.route.enabled - - description: 'Specify a custom hostname for the central route. + - description: 'Specify a custom hostname for the Central route. If unspecified, an appropriate default value will be automatically chosen by the OpenShift route operator.' displayName: Host path: central.exposure.route.host - - description: 'Set up a central route with reencrypt TLS termination. + - description: 'Set up a Central route with reencrypt TLS termination. For reencrypt routes, the request is terminated on the OpenShift router with a custom certificate. - The request is then reencrypted by the OpenShift router and sent to central. + The request is then reencrypted by the OpenShift router and sent to Central. - [user] --TLS--> [OpenShift router] --TLS--> [central]' + [user] --TLS--> [OpenShift router] --TLS--> [Central]' displayName: Re-Encrypt Route path: central.exposure.route.reencrypt - - description: 'Expose central with a reencrypt route. + - description: 'Expose Central with a reencrypt route. Should not be used for sensor communication.' displayName: Enabled path: central.exposure.route.reencrypt.enabled - - description: 'Specify a custom hostname for the central reencrypt route. + - description: 'Specify a custom hostname for the Central reencrypt route. If unspecified, an appropriate default value will be automatically chosen by the OpenShift route operator.' displayName: Host path: central.exposure.route.reencrypt.host - - description: TLS settings for exposing central via a reencrypt Route. + - description: TLS settings for exposing Central via a reencrypt Route. displayName: TLS Settings path: central.exposure.route.reencrypt.tls - description: 'The PEM encoded certificate chain that may be used to establish @@ -434,7 +434,7 @@ spec: Defaults to a certificate signed by the OpenShift certificate authority.' displayName: Certificate path: central.exposure.route.reencrypt.tls.certificate - - description: 'The CA certificate of the final destination, i.e. of central. + - description: 'The CA certificate of the final destination, i.e. of Central. Used by the OpenShift router for health checks on the secure connection. diff --git a/operator/config/crd/bases/platform.stackrox.io_centrals.yaml b/operator/config/crd/bases/platform.stackrox.io_centrals.yaml index 375aed4250cf2..a4d3565b8ece3 100644 --- a/operator/config/crd/bases/platform.stackrox.io_centrals.yaml +++ b/operator/config/crd/bases/platform.stackrox.io_centrals.yaml @@ -380,33 +380,33 @@ spec: properties: enabled: default: false - description: Expose central with a passthrough route. + description: Expose Central with a passthrough route. type: boolean host: description: |- - Specify a custom hostname for the central route. + Specify a custom hostname for the Central route. If unspecified, an appropriate default value will be automatically chosen by the OpenShift route operator. type: string reencrypt: description: |- - Set up a central route with reencrypt TLS termination. + Set up a Central route with reencrypt TLS termination. For reencrypt routes, the request is terminated on the OpenShift router with a custom certificate. - The request is then reencrypted by the OpenShift router and sent to central. - [user] --TLS--> [OpenShift router] --TLS--> [central] + The request is then reencrypted by the OpenShift router and sent to Central. + [user] --TLS--> [OpenShift router] --TLS--> [Central] properties: enabled: default: false description: |- - Expose central with a reencrypt route. + Expose Central with a reencrypt route. Should not be used for sensor communication. type: boolean host: description: |- - Specify a custom hostname for the central reencrypt route. + Specify a custom hostname for the Central reencrypt route. If unspecified, an appropriate default value will be automatically chosen by the OpenShift route operator. type: string tls: - description: TLS settings for exposing central via + description: TLS settings for exposing Central via a reencrypt Route. properties: caCertificate: @@ -422,7 +422,7 @@ spec: type: string destinationCACertificate: description: |- - The CA certificate of the final destination, i.e. of central. + The CA certificate of the final destination, i.e. of Central. Used by the OpenShift router for health checks on the secure connection. Defaults to the Central certificate authority. type: string diff --git a/operator/config/manifests/bases/rhacs-operator.clusterserviceversion.yaml b/operator/config/manifests/bases/rhacs-operator.clusterserviceversion.yaml index 3d845f169b289..bb5cd7d737ebd 100644 --- a/operator/config/manifests/bases/rhacs-operator.clusterserviceversion.yaml +++ b/operator/config/manifests/bases/rhacs-operator.clusterserviceversion.yaml @@ -83,11 +83,11 @@ spec: - description: Expose Central through an OpenShift route. displayName: Route path: central.exposure.route - - description: Expose central with a passthrough route. + - description: Expose Central with a passthrough route. displayName: Enabled path: central.exposure.route.enabled - description: |- - Expose central with a reencrypt route. + Expose Central with a reencrypt route. Should not be used for sensor communication. displayName: Enabled path: central.exposure.route.reencrypt.enabled @@ -258,12 +258,12 @@ spec: x-descriptors: - urn:alm:descriptor:com.tectonic.ui:fieldDependency:.enabled:true - description: |- - Specify a custom hostname for the central route. + Specify a custom hostname for the Central route. If unspecified, an appropriate default value will be automatically chosen by the OpenShift route operator. displayName: Host path: central.exposure.route.host - description: |- - Specify a custom hostname for the central reencrypt route. + Specify a custom hostname for the Central reencrypt route. If unspecified, an appropriate default value will be automatically chosen by the OpenShift route operator. displayName: Host path: central.exposure.route.reencrypt.host @@ -367,17 +367,17 @@ spec: displayName: Node Port path: central.exposure.nodePort - description: |- - Set up a central route with reencrypt TLS termination. + Set up a Central route with reencrypt TLS termination. For reencrypt routes, the request is terminated on the OpenShift router with a custom certificate. - The request is then reencrypted by the OpenShift router and sent to central. - [user] --TLS--> [OpenShift router] --TLS--> [central] + The request is then reencrypted by the OpenShift router and sent to Central. + [user] --TLS--> [OpenShift router] --TLS--> [Central] displayName: Re-Encrypt Route path: central.exposure.route.reencrypt - - description: TLS settings for exposing central via a reencrypt Route. + - description: TLS settings for exposing Central via a reencrypt Route. displayName: TLS Settings path: central.exposure.route.reencrypt.tls - description: |- - The CA certificate of the final destination, i.e. of central. + The CA certificate of the final destination, i.e. of Central. Used by the OpenShift router for health checks on the secure connection. Defaults to the Central certificate authority. displayName: Destination CA Certificate From 6f58aca4ed15487e3449cb6c49b89123d960fbd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20He=C3=9Felmann?= Date: Tue, 15 Apr 2025 21:34:07 +0200 Subject: [PATCH 20/20] exit early if reencrypt route is disabled --- operator/internal/route/translation.go | 9 ++++++ operator/internal/route/translation_test.go | 34 +++++++++++++++------ 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/operator/internal/route/translation.go b/operator/internal/route/translation.go index b93245c77be36..dbd48caea44ef 100644 --- a/operator/internal/route/translation.go +++ b/operator/internal/route/translation.go @@ -35,6 +35,15 @@ var _ translation.Enricher = &routeInjector{} // Enrich injects the Central certificate authority into the reencrypt route. func (i *routeInjector) Enrich(ctx context.Context, obj k8sutil.Object, vals chartutil.Values) (chartutil.Values, error) { + enabledPath := "central.exposure.route.reencrypt.enabled" + enabled, err := vals.PathValue(enabledPath) + if err != nil { + return vals, nil + } + if isEnabled, ok := enabled.(bool); !ok || !isEnabled { + return vals, nil + } + destCAPath := "central.exposure.route.reencrypt.tls.destinationCACertificate" if destCA, err := vals.PathValue(destCAPath); destCA != "" && err == nil { return vals, nil diff --git a/operator/internal/route/translation_test.go b/operator/internal/route/translation_test.go index 556b4167442e9..4a2e65febde23 100644 --- a/operator/internal/route/translation_test.go +++ b/operator/internal/route/translation_test.go @@ -20,17 +20,25 @@ func Test_injector_Enrich(t *testing.T) { centralCA := "fake-central-CA" tests := map[string]struct { + enabled bool destCAValue string want string }{ "should default to central CA from central-tls secret": { + enabled: true, destCAValue: "", want: centralCA, }, "should take destinationCACertificate from the input values": { + enabled: true, destCAValue: "fake-input-CA", want: "fake-input-CA", }, + "should do nothing if not enabled": { + enabled: false, + destCAValue: "", + want: "", + }, } for name, tt := range tests { t.Run(name, func(t *testing.T) { @@ -44,27 +52,33 @@ func Test_injector_Enrich(t *testing.T) { Data: map[string][]byte{mtls.CACertFileName: []byte(centralCA)}, } i := NewRouteInjector(fake.NewFakeClient(tlsSecret), fake.NewFakeClient(tlsSecret), logr.New(nil)) - vals := chartutil.Values{} - if tt.destCAValue != "" { - vals["central"] = map[string]interface{}{ + vals := chartutil.Values{ + "central": map[string]interface{}{ "exposure": map[string]interface{}{ "route": map[string]interface{}{ "reencrypt": map[string]interface{}{ - "tls": map[string]interface{}{ - "destinationCACertificate": string(tt.destCAValue), - }, + "enabled": tt.enabled, + "tls": map[string]interface{}{}, }, }, }, - } + }, + } + if tt.destCAValue != "" { + tlsVars, err := vals.Table("central.exposure.route.reencrypt.tls") + require.NoError(t, err) + tlsVars["destinationCACertificate"] = tt.destCAValue } gotValues, err := i.Enrich(context.Background(), obj, vals) require.NoError(t, err) gotCA, err := gotValues.PathValue("central.exposure.route.reencrypt.tls.destinationCACertificate") - require.NoError(t, err) - - assert.Equal(t, tt.want, gotCA) + if tt.enabled { + require.NoError(t, err) + assert.Equal(t, tt.want, gotCA) + } else { + require.Error(t, err) + } }) } }