diff --git a/README.md b/README.md index 05078277fe..4efcb41113 100644 --- a/README.md +++ b/README.md @@ -30,17 +30,20 @@ The following architectures are supported: _amd64_, _arm64_, _ppc64le_ and _s390 NetObserv has a couple of dependencies that must be installed on your cluster: -- Cert-manager +- Cert-manager / trust-manager - Prometheus - Loki -Cert-manager has to be installed separately. For example, using helm: +Cert-manager and Trust-manager have to be installed separately. For example, using helm: ```bash helm repo add cert-manager https://charts.jetstack.io -helm install my-cert-manager cert-manager/cert-manager --set crds.enabled=true +helm install cert-manager -n cert-manager --create-namespace cert-manager/cert-manager --set crds.enabled=true +helm upgrade trust-manager oci://quay.io/jetstack/charts/trust-manager --install --namespace cert-manager --wait ``` +If you don't want to use Cert-manager and Trust-manager, you need to provide certificates by other means: refer to [TLS.md](./docs/TLS.md). + Prometheus and Loki can be installed separately, or as dependencies of NetObserv (see below). Loki is not mandatory but improves the overall experience with NetObserv. @@ -54,13 +57,13 @@ Loki is not mandatory but improves the overall experience with NetObserv. helm repo add netobserv https://netobserv.io/static/helm/ --force-update # Standalone install, including dependencies: -helm install my-netobserv -n netobserv --create-namespace --set install.loki=true --set install.prom-stack=true netobserv/netobserv-operator +helm install netobserv -n netobserv --create-namespace --set install.loki=true --set install.prom-stack=true netobserv/netobserv-operator # OR minimal install (Prometheus/Loki must be installed separately) -helm install my-netobserv -n netobserv --create-namespace netobserv/netobserv-operator +helm install netobserv -n netobserv --create-namespace netobserv/netobserv-operator ``` -You can now create a `FlowCollector` resource. Refer to the [Configuration section](#configuration) of this document. A short `FlowCollector` should work, using most default values, plus with the standalone console enabled: +To start generating network flows, you must then create a `FlowCollector` resource ([full API reference](https://github.com/netobserv/netobserv-operator/blob/main/docs/FlowCollector.md#flowsnetobserviov1beta2)) named `cluster`. A short `FlowCollector` should work: ```bash cat < + // - `Disabled` to not configure TLS for the endpoint. Disabling TLS results in a less secure deployment model.
+ // - `Provided` to manually provide the key and certificate references.
+ // - `Auto` (default) to enable automatically based on the running environment.
+ // - `Auto-mTLS` to preconfigure mTLS. [Unsupported (*)].
+ // See also: https://github.com/netobserv/netobserv-operator/blob/main/docs/TLS.md. + // +kubebuilder:validation:Enum:="Disabled";"Provided";"Auto";"Auto-mTLS" + // +kubebuilder:validation:Required + // +kubebuilder:default:="Auto" + TLSType TLSConfigType `json:"tlsType,omitempty"` + + // TLS or mTLS configuration when `type` is set to `Provided`. + // +optional + ProvidedCertificates *ClientServerTLS `json:"providedCertificates,omitempty"` +} + // Add more exporter types below type ExporterType string diff --git a/api/flowcollector/v1beta2/flowcollector_validation_webhook.go b/api/flowcollector/v1beta2/flowcollector_validation_webhook.go index 060316b032..9022d51ff2 100644 --- a/api/flowcollector/v1beta2/flowcollector_validation_webhook.go +++ b/api/flowcollector/v1beta2/flowcollector_validation_webhook.go @@ -260,6 +260,7 @@ func (v *validator) validateFLP() { v.validateFLPFilters() v.validateFLPAlerts() v.validateFLPMetricsForAlerts() + v.validateFLPTLS() } func (v *validator) validateScheduling() { @@ -434,6 +435,27 @@ func (v *validator) validateFLPMetricsForAlerts() { } } +func (v *validator) validateFLPTLS() { + if v.fc.DeploymentModel == DeploymentModelService && v.fc.Processor.Service != nil && v.fc.Processor.Service.TLSType == TLSProvided { + if v.fc.Processor.Service.ProvidedCertificates == nil { + v.errors = append( + v.errors, + errors.New("missing configuration in spec.processor.providedCertificates despite spec.processor.tlsType being set to Provided"), + ) + } else if v.fc.Processor.Service.ProvidedCertificates.CAFile == nil { + v.errors = append( + v.errors, + errors.New("missing configuration in spec.processor.providedCertificates.caFile despite spec.processor.tlsType being set to Provided"), + ) + } else if v.fc.Processor.Service.ProvidedCertificates.ServerCert == nil { + v.errors = append( + v.errors, + errors.New("missing configuration in spec.processor.providedCertificates.serverCert despite spec.processor.tlsType being set to Provided"), + ) + } + } +} + func GetFirstRequiredMetrics(anyRequired, actual []string) string { for _, m := range anyRequired { if slices.Contains(actual, m) { diff --git a/api/flowcollector/v1beta2/flowcollector_validation_webhook_test.go b/api/flowcollector/v1beta2/flowcollector_validation_webhook_test.go index 76de01f4d1..b39bed10a8 100644 --- a/api/flowcollector/v1beta2/flowcollector_validation_webhook_test.go +++ b/api/flowcollector/v1beta2/flowcollector_validation_webhook_test.go @@ -893,6 +893,21 @@ func TestValidateFLP(t *testing.T) { }, }, }, + { + name: "Missing provided TLS config", + ocpVersion: "4.18.0", + fc: &FlowCollector{ + Spec: FlowCollectorSpec{ + DeploymentModel: DeploymentModelService, + Processor: FlowCollectorFLP{ + Service: &ProcessorServiceConfig{ + TLSType: TLSProvided, + }, + }, + }, + }, + expectedError: "missing configuration in spec.processor.providedCertificates despite spec.processor.tlsType being set to Provided", + }, } r := FlowCollector{} diff --git a/api/flowcollector/v1beta2/helper.go b/api/flowcollector/v1beta2/helper.go index 0763c762d7..95346f0fc0 100644 --- a/api/flowcollector/v1beta2/helper.go +++ b/api/flowcollector/v1beta2/helper.go @@ -55,12 +55,18 @@ func (spec *FlowCollectorSpec) UsePrometheus() bool { return spec.Prometheus.Querier.Enable == nil || *spec.Prometheus.Querier.Enable } -func (spec *FlowCollectorSpec) UseConsolePlugin() bool { +func (spec *FlowCollectorSpec) UseWebConsole() bool { return (spec.UseLoki() || spec.UsePrometheus()) && // nil should fallback to default value, which is "true" (spec.ConsolePlugin.Enable == nil || *spec.ConsolePlugin.Enable) } +func (spec *FlowCollectorSpec) UseStandaloneConsole(hasPluginAPI bool) bool { + // defaults to true if there's no plugin API, false otherwise + return (spec.ConsolePlugin.Standalone != nil && *spec.ConsolePlugin.Standalone || + spec.ConsolePlugin.Standalone == nil && !hasPluginAPI) +} + func (spec *FlowCollectorSpec) UseHostNetwork() bool { return spec.DeploymentModel == DeploymentModelDirect } diff --git a/api/flowcollector/v1beta2/zz_generated.deepcopy.go b/api/flowcollector/v1beta2/zz_generated.deepcopy.go index 699cbfe50c..7f6c5838db 100644 --- a/api/flowcollector/v1beta2/zz_generated.deepcopy.go +++ b/api/flowcollector/v1beta2/zz_generated.deepcopy.go @@ -248,6 +248,36 @@ func (in *CertificateReference) DeepCopy() *CertificateReference { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClientServerTLS) DeepCopyInto(out *ClientServerTLS) { + *out = *in + if in.ClientCert != nil { + in, out := &in.ClientCert, &out.ClientCert + *out = new(CertificateReference) + **out = **in + } + if in.ServerCert != nil { + in, out := &in.ServerCert, &out.ServerCert + *out = new(CertificateReference) + **out = **in + } + if in.CAFile != nil { + in, out := &in.CAFile, &out.CAFile + *out = new(FileReference) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClientServerTLS. +func (in *ClientServerTLS) DeepCopy() *ClientServerTLS { + if in == nil { + return nil + } + out := new(ClientServerTLS) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ClientTLS) DeepCopyInto(out *ClientTLS) { *out = *in @@ -559,6 +589,11 @@ func (in *FlowCollectorConsolePlugin) DeepCopyInto(out *FlowCollectorConsolePlug *out = new(bool) **out = **in } + if in.Standalone != nil { + in, out := &in.Standalone, &out.Standalone + *out = new(bool) + **out = **in + } if in.Replicas != nil { in, out := &in.Replicas, &out.Replicas *out = new(int32) @@ -718,6 +753,11 @@ func (in *FlowCollectorFLP) DeepCopyInto(out *FlowCollectorFLP) { *out = new(SlicesConfig) (*in).DeepCopyInto(*out) } + if in.Service != nil { + in, out := &in.Service, &out.Service + *out = new(ProcessorServiceConfig) + (*in).DeepCopyInto(*out) + } if in.Advanced != nil { in, out := &in.Advanced, &out.Advanced *out = new(AdvancedProcessorConfig) @@ -1248,6 +1288,26 @@ func (in *OVNKubernetesConfig) DeepCopy() *OVNKubernetesConfig { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProcessorServiceConfig) DeepCopyInto(out *ProcessorServiceConfig) { + *out = *in + if in.ProvidedCertificates != nil { + in, out := &in.ProvidedCertificates, &out.ProvidedCertificates + *out = new(ClientServerTLS) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProcessorServiceConfig. +func (in *ProcessorServiceConfig) DeepCopy() *ProcessorServiceConfig { + if in == nil { + return nil + } + out := new(ProcessorServiceConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PrometheusQuerier) DeepCopyInto(out *PrometheusQuerier) { *out = *in @@ -1396,8 +1456,8 @@ func (in *ServerTLS) DeepCopyInto(out *ServerTLS) { *out = new(CertificateReference) **out = **in } - if in.ProvidedCaFile != nil { - in, out := &in.ProvidedCaFile, &out.ProvidedCaFile + if in.ProvidedCAFile != nil { + in, out := &in.ProvidedCAFile, &out.ProvidedCAFile *out = new(FileReference) **out = **in } diff --git a/bundle/manifests/flows.netobserv.io_flowcollectors.yaml b/bundle/manifests/flows.netobserv.io_flowcollectors.yaml index 979f28679b..e4556cb6d0 100644 --- a/bundle/manifests/flows.netobserv.io_flowcollectors.yaml +++ b/bundle/manifests/flows.netobserv.io_flowcollectors.yaml @@ -3375,9 +3375,9 @@ spec: type: string type: object tls: - description: TLS client configuration. When using TLS, verify - that the address matches the Kafka port used for TLS, - generally 9093. + description: |- + TLS and mTLS client configuration. When using TLS, verify that the address matches the Kafka port used for TLS, generally 9093. + We recommend the use of mTLS for higher security standards. properties: caCert: description: '`caCert` defines the reference of the @@ -3694,9 +3694,9 @@ spec: type: string type: object tls: - description: TLS client configuration. When using TLS, verify - that the address matches the Kafka port used for TLS, generally - 9093. + description: |- + TLS and mTLS client configuration. When using TLS, verify that the address matches the Kafka port used for TLS, generally 9093. + We recommend the use of mTLS for higher security standards. properties: caCert: description: '`caCert` defines the reference of the certificate @@ -6186,6 +6186,118 @@ spec: More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ type: object type: object + service: + description: Service configuration, only used when `spec.deploymentModel` + is `Service`. + properties: + providedCertificates: + description: TLS or mTLS configuration when `type` is set + to `Provided`. + properties: + caFile: + description: Reference to the CA file. + properties: + file: + description: File name within the config map or secret. + type: string + name: + description: Name of the config map or secret containing + the file. + type: string + namespace: + default: "" + description: |- + Namespace of the config map or secret containing the file. If omitted, the default is to use the same namespace as where NetObserv is deployed. + If the namespace is different, the config map or the secret is copied so that it can be mounted as required. + type: string + type: + description: 'Type for the file reference: `configmap` + or `secret`.' + enum: + - configmap + - secret + type: string + type: object + clientCert: + description: TLS client certificate reference, used for + mTLS. Leave unset for simple TLS. + properties: + certFile: + description: '`certFile` defines the path to the certificate + file name within the config map or secret.' + type: string + certKey: + description: '`certKey` defines the path to the certificate + private key file name within the config map or secret. + Omit when the key is not necessary.' + type: string + name: + description: Name of the config map or secret containing + certificates. + type: string + namespace: + default: "" + description: |- + Namespace of the config map or secret containing certificates. If omitted, the default is to use the same namespace as where NetObserv is deployed. + If the namespace is different, the config map or the secret is copied so that it can be mounted as required. + type: string + type: + description: 'Type for the certificate reference: + `configmap` or `secret`.' + enum: + - configmap + - secret + type: string + type: object + serverCert: + description: TLS server certificate reference. + properties: + certFile: + description: '`certFile` defines the path to the certificate + file name within the config map or secret.' + type: string + certKey: + description: '`certKey` defines the path to the certificate + private key file name within the config map or secret. + Omit when the key is not necessary.' + type: string + name: + description: Name of the config map or secret containing + certificates. + type: string + namespace: + default: "" + description: |- + Namespace of the config map or secret containing certificates. If omitted, the default is to use the same namespace as where NetObserv is deployed. + If the namespace is different, the config map or the secret is copied so that it can be mounted as required. + type: string + type: + description: 'Type for the certificate reference: + `configmap` or `secret`.' + enum: + - configmap + - secret + type: string + type: object + type: object + tlsType: + default: Auto + description: |- + Select the type of TLS configuration:
+ - `Disabled` to not configure TLS for the endpoint. Disabling TLS results in a less secure deployment model.
+ - `Provided` to manually provide the key and certificate references.
+ - `Auto` (default) to enable automatically based on the running environment.
+ - `Auto-mTLS` to preconfigure mTLS. [Unsupported (*)].
+ See also: https://github.com/netobserv/netobserv-operator/blob/main/docs/TLS.md. + enum: + - Disabled + - Provided + - Auto + - Auto-mTLS + type: string + required: + - tlsType + type: object slicesConfig: description: Global configuration managing FlowCollectorSlices custom resources. diff --git a/bundle/manifests/netobserv-operator.clusterserviceversion.yaml b/bundle/manifests/netobserv-operator.clusterserviceversion.yaml index ce8b3b5083..ed96214678 100644 --- a/bundle/manifests/netobserv-operator.clusterserviceversion.yaml +++ b/bundle/manifests/netobserv-operator.clusterserviceversion.yaml @@ -474,6 +474,46 @@ spec: path: processor.metrics.includeList - displayName: Port path: processor.metrics.server.port + - displayName: Service + path: processor.service + - displayName: Provided certificates + path: processor.service.providedCertificates + - displayName: Ca file + path: processor.service.providedCertificates.caFile + - displayName: File + path: processor.service.providedCertificates.caFile.file + - displayName: Name + path: processor.service.providedCertificates.caFile.name + - displayName: Namespace + path: processor.service.providedCertificates.caFile.namespace + - displayName: Type + path: processor.service.providedCertificates.caFile.type + - displayName: Client cert + path: processor.service.providedCertificates.clientCert + - displayName: Cert file + path: processor.service.providedCertificates.clientCert.certFile + - displayName: Cert key + path: processor.service.providedCertificates.clientCert.certKey + - displayName: Name + path: processor.service.providedCertificates.clientCert.name + - displayName: Namespace + path: processor.service.providedCertificates.clientCert.namespace + - displayName: Type + path: processor.service.providedCertificates.clientCert.type + - displayName: Server cert + path: processor.service.providedCertificates.serverCert + - displayName: Cert file + path: processor.service.providedCertificates.serverCert.certFile + - displayName: Cert key + path: processor.service.providedCertificates.serverCert.certKey + - displayName: Name + path: processor.service.providedCertificates.serverCert.name + - displayName: Namespace + path: processor.service.providedCertificates.serverCert.namespace + - displayName: Type + path: processor.service.providedCertificates.serverCert.type + - displayName: Tls type + path: processor.service.tlsType - displayName: Slices config path: processor.slicesConfig - displayName: Collection mode diff --git a/config/crd/bases/flows.netobserv.io_flowcollectors.yaml b/config/crd/bases/flows.netobserv.io_flowcollectors.yaml index 0b3f075c0a..1900755baa 100644 --- a/config/crd/bases/flows.netobserv.io_flowcollectors.yaml +++ b/config/crd/bases/flows.netobserv.io_flowcollectors.yaml @@ -3154,7 +3154,9 @@ spec: type: string type: object tls: - description: TLS client configuration. When using TLS, verify that the address matches the Kafka port used for TLS, generally 9093. + description: |- + TLS and mTLS client configuration. When using TLS, verify that the address matches the Kafka port used for TLS, generally 9093. + We recommend the use of mTLS for higher security standards. properties: caCert: description: '`caCert` defines the reference of the certificate for the Certificate Authority.' @@ -3425,7 +3427,9 @@ spec: type: string type: object tls: - description: TLS client configuration. When using TLS, verify that the address matches the Kafka port used for TLS, generally 9093. + description: |- + TLS and mTLS client configuration. When using TLS, verify that the address matches the Kafka port used for TLS, generally 9093. + We recommend the use of mTLS for higher security standards. properties: caCert: description: '`caCert` defines the reference of the certificate for the Certificate Authority.' @@ -5722,6 +5726,103 @@ spec: More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ type: object type: object + service: + description: Service configuration, only used when `spec.deploymentModel` is `Service`. + properties: + providedCertificates: + description: TLS or mTLS configuration when `type` is set to `Provided`. + properties: + caFile: + description: Reference to the CA file. + properties: + file: + description: File name within the config map or secret. + type: string + name: + description: Name of the config map or secret containing the file. + type: string + namespace: + default: "" + description: |- + Namespace of the config map or secret containing the file. If omitted, the default is to use the same namespace as where NetObserv is deployed. + If the namespace is different, the config map or the secret is copied so that it can be mounted as required. + type: string + type: + description: 'Type for the file reference: `configmap` or `secret`.' + enum: + - configmap + - secret + type: string + type: object + clientCert: + description: TLS client certificate reference, used for mTLS. Leave unset for simple TLS. + properties: + certFile: + description: '`certFile` defines the path to the certificate file name within the config map or secret.' + type: string + certKey: + description: '`certKey` defines the path to the certificate private key file name within the config map or secret. Omit when the key is not necessary.' + type: string + name: + description: Name of the config map or secret containing certificates. + type: string + namespace: + default: "" + description: |- + Namespace of the config map or secret containing certificates. If omitted, the default is to use the same namespace as where NetObserv is deployed. + If the namespace is different, the config map or the secret is copied so that it can be mounted as required. + type: string + type: + description: 'Type for the certificate reference: `configmap` or `secret`.' + enum: + - configmap + - secret + type: string + type: object + serverCert: + description: TLS server certificate reference. + properties: + certFile: + description: '`certFile` defines the path to the certificate file name within the config map or secret.' + type: string + certKey: + description: '`certKey` defines the path to the certificate private key file name within the config map or secret. Omit when the key is not necessary.' + type: string + name: + description: Name of the config map or secret containing certificates. + type: string + namespace: + default: "" + description: |- + Namespace of the config map or secret containing certificates. If omitted, the default is to use the same namespace as where NetObserv is deployed. + If the namespace is different, the config map or the secret is copied so that it can be mounted as required. + type: string + type: + description: 'Type for the certificate reference: `configmap` or `secret`.' + enum: + - configmap + - secret + type: string + type: object + type: object + tlsType: + default: Auto + description: |- + Select the type of TLS configuration:
+ - `Disabled` to not configure TLS for the endpoint. Disabling TLS results in a less secure deployment model.
+ - `Provided` to manually provide the key and certificate references.
+ - `Auto` (default) to enable automatically based on the running environment.
+ - `Auto-mTLS` to preconfigure mTLS. [Unsupported (*)].
+ See also: https://github.com/netobserv/netobserv-operator/blob/main/docs/TLS.md. + enum: + - Disabled + - Provided + - Auto + - Auto-mTLS + type: string + required: + - tlsType + type: object slicesConfig: description: Global configuration managing FlowCollectorSlices custom resources. properties: diff --git a/docs/FlowCollector.md b/docs/FlowCollector.md index 289a6cd099..f93e1e0b56 100644 --- a/docs/FlowCollector.md +++ b/docs/FlowCollector.md @@ -6201,7 +6201,8 @@ Kafka configuration, such as the address and topic, to send enriched flows to. tls object - TLS client configuration. When using TLS, verify that the address matches the Kafka port used for TLS, generally 9093.
+ TLS and mTLS client configuration. When using TLS, verify that the address matches the Kafka port used for TLS, generally 9093. +We recommend the use of mTLS for higher security standards.
false @@ -6363,7 +6364,8 @@ If the namespace is different, the config map or the secret is copied so that it -TLS client configuration. When using TLS, verify that the address matches the Kafka port used for TLS, generally 9093. +TLS and mTLS client configuration. When using TLS, verify that the address matches the Kafka port used for TLS, generally 9093. +We recommend the use of mTLS for higher security standards. @@ -6941,7 +6943,8 @@ Kafka configuration, allowing to use Kafka as a broker as part of the flow colle @@ -7103,7 +7106,8 @@ If the namespace is different, the config map or the secret is copied so that it -TLS client configuration. When using TLS, verify that the address matches the Kafka port used for TLS, generally 9093. +TLS and mTLS client configuration. When using TLS, verify that the address matches the Kafka port used for TLS, generally 9093. +We recommend the use of mTLS for higher security standards.
tls object - TLS client configuration. When using TLS, verify that the address matches the Kafka port used for TLS, generally 9093.
+ TLS and mTLS client configuration. When using TLS, verify that the address matches the Kafka port used for TLS, generally 9093. +We recommend the use of mTLS for higher security standards.
false
@@ -8614,6 +8618,13 @@ For more information, see https://kubernetes.io/docs/concepts/configuration/mana Default: map[limits:map[memory:800Mi] requests:map[cpu:100m memory:100Mi]]
+ + + + + @@ -12094,6 +12105,262 @@ only the result of this request.
false
serviceobject + Service configuration, only used when `spec.deploymentModel` is `Service`.
+
false
slicesConfig object
+### FlowCollector.spec.processor.service +[↩ Parent](#flowcollectorspecprocessor) + + + +Service configuration, only used when `spec.deploymentModel` is `Service`. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
tlsTypeenum + Select the type of TLS configuration:
+- `Disabled` to not configure TLS for the endpoint. Disabling TLS results in a less secure deployment model.
+- `Provided` to manually provide the key and certificate references.
+- `Auto` (default) to enable automatically based on the running environment.
+- `Auto-mTLS` to preconfigure mTLS. [Unsupported (*)].
+See also: https://github.com/netobserv/netobserv-operator/blob/main/docs/TLS.md.
+
+ Enum: Disabled, Provided, Auto, Auto-mTLS
+ Default: Auto
+
true
providedCertificatesobject + TLS or mTLS configuration when `type` is set to `Provided`.
+
false
+ + +### FlowCollector.spec.processor.service.providedCertificates +[↩ Parent](#flowcollectorspecprocessorservice) + + + +TLS or mTLS configuration when `type` is set to `Provided`. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
caFileobject + Reference to the CA file.
+
false
clientCertobject + TLS client certificate reference, used for mTLS. Leave unset for simple TLS.
+
false
serverCertobject + TLS server certificate reference.
+
false
+ + +### FlowCollector.spec.processor.service.providedCertificates.caFile +[↩ Parent](#flowcollectorspecprocessorserviceprovidedcertificates) + + + +Reference to the CA file. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
filestring + File name within the config map or secret.
+
false
namestring + Name of the config map or secret containing the file.
+
false
namespacestring + Namespace of the config map or secret containing the file. If omitted, the default is to use the same namespace as where NetObserv is deployed. +If the namespace is different, the config map or the secret is copied so that it can be mounted as required.
+
+ Default:
+
false
typeenum + Type for the file reference: `configmap` or `secret`.
+
+ Enum: configmap, secret
+
false
+ + +### FlowCollector.spec.processor.service.providedCertificates.clientCert +[↩ Parent](#flowcollectorspecprocessorserviceprovidedcertificates) + + + +TLS client certificate reference, used for mTLS. Leave unset for simple TLS. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
certFilestring + `certFile` defines the path to the certificate file name within the config map or secret.
+
false
certKeystring + `certKey` defines the path to the certificate private key file name within the config map or secret. Omit when the key is not necessary.
+
false
namestring + Name of the config map or secret containing certificates.
+
false
namespacestring + Namespace of the config map or secret containing certificates. If omitted, the default is to use the same namespace as where NetObserv is deployed. +If the namespace is different, the config map or the secret is copied so that it can be mounted as required.
+
+ Default:
+
false
typeenum + Type for the certificate reference: `configmap` or `secret`.
+
+ Enum: configmap, secret
+
false
+ + +### FlowCollector.spec.processor.service.providedCertificates.serverCert +[↩ Parent](#flowcollectorspecprocessorserviceprovidedcertificates) + + + +TLS server certificate reference. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
certFilestring + `certFile` defines the path to the certificate file name within the config map or secret.
+
false
certKeystring + `certKey` defines the path to the certificate private key file name within the config map or secret. Omit when the key is not necessary.
+
false
namestring + Name of the config map or secret containing certificates.
+
false
namespacestring + Namespace of the config map or secret containing certificates. If omitted, the default is to use the same namespace as where NetObserv is deployed. +If the namespace is different, the config map or the secret is copied so that it can be mounted as required.
+
+ Default:
+
false
typeenum + Type for the certificate reference: `configmap` or `secret`.
+
+ Enum: configmap, secret
+
false
+ + ### FlowCollector.spec.processor.slicesConfig [↩ Parent](#flowcollectorspecprocessor) diff --git a/docs/TLS.md b/docs/TLS.md new file mode 100644 index 0000000000..3b8d3662d7 --- /dev/null +++ b/docs/TLS.md @@ -0,0 +1,126 @@ +# TLS and expected certificates + +This document lists all required and optional TLS certificates for NetObserv. You can also refer to the [Helm chart templates](../helm/templates/certificates.yaml) for cert-manager. + +## Required certificates + +Those certificates are always required and are not configurable: + + + + + + + + + + + + + + + + + + + + + + + + +
Service nameResource kindResource nameResource keys
netobserv-webhook-serviceSecretwebhook-server-certtls.crt, tls.key
netobserv-metrics-serviceSecretmanager-metrics-tlstls.crt, tls.key
+ +## Agent to FLP certificates + +When `spec.deploymentModel` is "Service", the traffic from eBPF agents to flowlogs-pipeline pods uses TLS by default. It is possible to disable TLS, though not recommended in production-grade environments, as it decreases the security of the NetObserv deployments. + +In "Kafka" mode, the TLS/SASL configuration depends on your installation. The Kafka clients used in NetObserv support simple TLS, mTLS, SASL as well as no TLS. We recommend the use of mTLS for higher security standards. + +In "Direct" mode, the traffic doesn't leave the host and is not encrypted. + +The tables below apply to the "Service" mode. + +### Auto (TLS) + +When `spec.processor.service.tlsType` is "Auto": + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Needed byResource kindResource nameResource keysNotes
flowlogs-pipelineSecretflowlogs-pipeline-certtls.crt, tls.key
eBPF AgentsConfigMapnetobserv-caservice-ca.crtMust be installed in netobserv-privileged namespace.
+ +### Auto (mTLS) + +When `spec.processor.service.tlsType` is "Auto-mTLS": + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Needed byResource kindResource nameResource keysNotes
flowlogs-pipelineSecretflowlogs-pipeline-certtls.crt, tls.key
flowlogs-pipelineConfigMapnetobserv-caservice-ca.crt
eBPF AgentsSecretebpf-agent-certtls.crt, tls.keyMust be installed in netobserv-privileged namespace.
eBPF AgentsConfigMapnetobserv-caservice-ca.crtMust be installed in netobserv-privileged namespace.
+ +### Provided + +When `spec.processor.service.tlsType` is "Provided", you can specify any Secret or ConfigMap for TLS or mTLS, via `spec.processor.service.providedCertificates`. + +For mTLS, configure `spec.processor.service.providedCertificates.clientCert`. For simple TLS, do not set the client cert config. diff --git a/helm/Chart.yaml b/helm/Chart.yaml index 5d4501e586..ee05b2a313 100644 --- a/helm/Chart.yaml +++ b/helm/Chart.yaml @@ -70,11 +70,8 @@ annotations: url: https://netobserv.io/static/assets/images/about/network-traffic-main.png dependencies: -- name: prometheus - version: "~28.2.0" - repository: https://prometheus-community.github.io/helm-charts - condition: install.prom - name: kube-prometheus-stack + alias: prom-stack version: "~80.4.1" repository: https://prometheus-community.github.io/helm-charts condition: install.prom-stack diff --git a/helm/README.md b/helm/README.md index 79f5ef07a6..a7abcd6003 100644 --- a/helm/README.md +++ b/helm/README.md @@ -26,17 +26,20 @@ The following architectures are supported: _amd64_, _arm64_, _ppc64le_ and _s390 NetObserv has a couple of dependencies that must be installed on your cluster: -- Cert-manager +- Cert-manager / trust-manager - Prometheus - Loki -Cert-manager has to be installed separately. For example, using helm: +Cert-manager and Trust-manager have to be installed separately. For example, using helm: ```bash helm repo add cert-manager https://charts.jetstack.io -helm install my-cert-manager cert-manager/cert-manager --set crds.enabled=true +helm install cert-manager -n cert-manager --create-namespace cert-manager/cert-manager --set crds.enabled=true +helm upgrade trust-manager oci://quay.io/jetstack/charts/trust-manager --install --namespace cert-manager --wait ``` +If you don't want to use Cert-manager and Trust-manager, you need to provide certificates by other means: refer to [TLS.md](https://github.com/netobserv/netobserv-operator/blob/main/docs/TLS.md). + Prometheus and Loki can be installed separately, or as dependencies of NetObserv (see below). Loki is not mandatory but improves the overall experience with NetObserv. @@ -50,13 +53,13 @@ Loki is not mandatory but improves the overall experience with NetObserv. helm repo add netobserv https://netobserv.io/static/helm/ --force-update # Standalone install, including dependencies: -helm install my-netobserv -n netobserv --create-namespace --set install.loki=true --set install.prom-stack=true netobserv/netobserv-operator +helm install netobserv -n netobserv --create-namespace --set install.loki=true --set install.prom-stack=true netobserv/netobserv-operator # OR minimal install (Prometheus/Loki must be installed separately) -helm install my-netobserv -n netobserv --create-namespace netobserv/netobserv-operator +helm install netobserv -n netobserv --create-namespace netobserv/netobserv-operator ``` -You can now create a `FlowCollector` resource ([full API reference](https://github.com/netobserv/netobserv-operator/blob/main/docs/FlowCollector.md#flowsnetobserviov1beta2)). A short `FlowCollector` should work, using most default values, plus with the standalone console enabled: +To start generating network flows, you must then create a `FlowCollector` resource ([full API reference](https://github.com/netobserv/netobserv-operator/blob/main/docs/FlowCollector.md#flowsnetobserviov1beta2)) named `cluster`. A short `FlowCollector` should work: ```bash cat < + - `Disabled` to not configure TLS for the endpoint. Disabling TLS results in a less secure deployment model.
+ - `Provided` to manually provide the key and certificate references.
+ - `Auto` (default) to enable automatically based on the running environment.
+ - `Auto-mTLS` to preconfigure mTLS. [Unsupported (*)].
+ See also: https://github.com/netobserv/netobserv-operator/blob/main/docs/TLS.md. + enum: + - Disabled + - Provided + - Auto + - Auto-mTLS + type: string + required: + - tlsType + type: object slicesConfig: description: Global configuration managing FlowCollectorSlices custom resources. properties: diff --git a/helm/templates/NOTES.txt b/helm/templates/NOTES.txt new file mode 100644 index 0000000000..725dddae8e --- /dev/null +++ b/helm/templates/NOTES.txt @@ -0,0 +1,34 @@ +🛰 Welcome to NetObserv! + +The operator was just installed. The flow collection process only starts after a FlowCollector resource is created. + +⌨️ To create a FlowCollector (example): + +$ cat < warn but continue execution if reg { diff --git a/internal/controller/consoleplugin/consoleplugin_test.go b/internal/controller/consoleplugin/consoleplugin_test.go index 0957c325e9..2ea02e5260 100644 --- a/internal/controller/consoleplugin/consoleplugin_test.go +++ b/internal/controller/consoleplugin/consoleplugin_test.go @@ -264,12 +264,7 @@ func TestConfigMapUpdateCheck(t *testing.T) { old = nEw // update status user cert - loki.LokiManualParams.StatusTLS.UserCert = flowslatest.CertificateReference{ - Type: "secret", - Name: "sec-name", - CertFile: "tls.crt", - CertKey: "tls.key", - } + loki.LokiManualParams.StatusTLS.UserCert = ptr.Deref(helper.DefaultCertificateReference("sec-name", ""), flowslatest.CertificateReference{}) builder = getBuilder(&spec, &loki) nEw, _, _ = builder.configMap(context.Background(), nil) assert.NotEqual(old.Data, nEw.Data) diff --git a/internal/controller/ebpf/agent_controller.go b/internal/controller/ebpf/agent_controller.go index f73c6c2a71..32d3e17a8c 100644 --- a/internal/controller/ebpf/agent_controller.go +++ b/internal/controller/ebpf/agent_controller.go @@ -38,6 +38,8 @@ const ( envFlowsTargetHost = "TARGET_HOST" envFlowsTargetPort = "TARGET_PORT" envTargetTLSCACertPath = "TARGET_TLS_CA_CERT_PATH" + envTargetTLSUserCertPath = "TARGET_TLS_USER_CERT_PATH" + envTargetTLSUserKeyPath = "TARGET_TLS_USER_KEY_PATH" envGRPCReconnect = "GRPC_RECONNECT_TIMER" envGRPCReconnectRnd = "GRPC_RECONNECT_TIMER_RANDOMIZATION" envSampling = "SAMPLING" @@ -237,23 +239,18 @@ func (c *AgentController) desired(ctx context.Context, coll *flowslatest.FlowCol } advancedConfig := helper.GetAdvancedAgentConfig(coll.Spec.Agent.EBPF.Advanced) - if coll.Spec.Agent.EBPF.Metrics.Server.TLS.Type != flowslatest.ServerTLSDisabled { + if coll.Spec.Agent.EBPF.Metrics.Server.TLS.Type != flowslatest.TLSDisabled { var promTLS *flowslatest.CertificateReference switch coll.Spec.Agent.EBPF.Metrics.Server.TLS.Type { - case flowslatest.ServerTLSProvided: + case flowslatest.TLSProvided: promTLS = coll.Spec.Agent.EBPF.Metrics.Server.TLS.Provided if promTLS == nil { rlog.Info("EBPF agent metric tls configuration set to provided but none is provided") } - case flowslatest.ServerTLSAuto: - promTLS = &flowslatest.CertificateReference{ - Type: "secret", - Name: constants.EBPFAgentMetricsSvcName, - CertFile: "tls.crt", - CertKey: "tls.key", - } - case flowslatest.ServerTLSDisabled: - // show never happens added for linting purposes + case flowslatest.TLSAuto: + promTLS = helper.DefaultCertificateReference(constants.EBPFAgentMetricsSvcName, "") + case flowslatest.TLSDisabled, flowslatest.TLSAutoMTLS: + // should never happens added for linting purposes } cert, key := c.volumes.AddCertificate(promTLS, "prom-certs") if cert != "" && key != "" { @@ -503,23 +500,17 @@ func (c *AgentController) envConfig(ctx context.Context, coll *flowslatest.FlowC Value: strconv.Itoa(int(*advancedConfig.Port)), }) } else { - skipTLS := flowslatest.IsEnvEnabled(advancedConfig.Env, "SERVER_NOTLS") - if !skipTLS { + // Service mode + ca, clientCert := helper.GetServiceClientTLSConfig(coll.Spec.Processor.Service, "ebpf-agent-cert", c.ClusterInfo.IsOpenShift()) + if ca != nil { // Send to FLP service using TLS - caConfigMapName := "flowlogs-pipeline-ca" - if c.ClusterInfo.IsOpenShift() { - caConfigMapName = "openshift-service-ca.crt" - } - tlsCfg := flowslatest.ClientTLS{ - Enable: true, - CACert: flowslatest.CertificateReference{ - Type: flowslatest.RefTypeConfigMap, - Name: caConfigMapName, - CertFile: "service-ca.crt", - }, - } - caPath := c.volumes.AddCACertificate(&tlsCfg, "svc-certs") + caPath := c.volumes.AddVolume(ca, "netobserv-ca") config = append(config, corev1.EnvVar{Name: envTargetTLSCACertPath, Value: caPath}) + if clientCert != nil { + certPath, keyPath := c.volumes.AddCertificate(clientCert, "client-certs") + config = append(config, corev1.EnvVar{Name: envTargetTLSUserCertPath, Value: certPath}) + config = append(config, corev1.EnvVar{Name: envTargetTLSUserKeyPath, Value: keyPath}) + } } config = append(config, corev1.EnvVar{ diff --git a/internal/controller/ebpf/agent_metrics.go b/internal/controller/ebpf/agent_metrics.go index b7d763e561..5aa5ef78e8 100644 --- a/internal/controller/ebpf/agent_metrics.go +++ b/internal/controller/ebpf/agent_metrics.go @@ -74,7 +74,7 @@ func (c *AgentController) promService(target *flowslatest.FlowCollectorEBPF) *co }}, }, } - if target.Metrics.Server.TLS.Type == flowslatest.ServerTLSAuto { + if target.Metrics.Server.TLS.Type == flowslatest.TLSAuto { svc.ObjectMeta.Annotations = map[string]string{ constants.OpenShiftCertificateAnnotation: constants.EBPFAgentMetricsSvcName, } diff --git a/internal/controller/flp/flp_common_objects.go b/internal/controller/flp/flp_common_objects.go index 7ef719516b..3a3f10b63b 100644 --- a/internal/controller/flp/flp_common_objects.go +++ b/internal/controller/flp/flp_common_objects.go @@ -33,21 +33,20 @@ const ( startupPeriodSeconds = 10 ) -func newGRPCPipeline(desired *flowslatest.FlowCollectorSpec, volumes *volumes.Builder) config.PipelineBuilderStage { +func newGRPCPipeline(desired *flowslatest.FlowCollectorSpec, volumes *volumes.Builder, isOpenShift bool) config.PipelineBuilderStage { adv := helper.GetAdvancedProcessorConfig(desired) cfg := api.IngestGRPCProto{Port: int(*adv.Port)} - skipTLS := flowslatest.IsEnvEnabled(adv.Env, "SERVER_NOTLS") - if desired.DeploymentModel == flowslatest.DeploymentModelService && !skipTLS { - // Communication from agents uses TLS: set up server certificate - ref := flowslatest.CertificateReference{ - Type: flowslatest.RefTypeSecret, - Name: monoCertSecretName, - CertFile: "tls.crt", - CertKey: "tls.key", + if desired.DeploymentModel == flowslatest.DeploymentModelService { + serverCert, clientCA := helper.GetServiceServerTLSConfig(desired.Processor.Service, monoCertSecretName, isOpenShift) + if serverCert != nil { + // Communication from agents uses TLS: set up server certificate + certPath, keyPath := volumes.AddCertificate(serverCert, "svc-certs") + cfg.CertPath = certPath + cfg.KeyPath = keyPath + if clientCA != nil { + cfg.ClientCAPath = volumes.AddVolume(clientCA, "netobserv-ca") + } } - cert, key := volumes.AddCertificate(&ref, "svc-certs") - cfg.CertPath = cert - cfg.KeyPath = key } return config.NewGRPCPipeline("grpc", cfg) } @@ -68,19 +67,19 @@ func newKafkaPipeline(desired *flowslatest.FlowCollectorSpec, volumes *volumes.B func getPromTLS(desired *flowslatest.FlowCollectorSpec, serviceName string) (*flowslatest.CertificateReference, error) { var promTLS *flowslatest.CertificateReference switch desired.Processor.Metrics.Server.TLS.Type { - case flowslatest.ServerTLSProvided: + case flowslatest.TLSProvided: promTLS = desired.Processor.Metrics.Server.TLS.Provided if promTLS == nil { return nil, fmt.Errorf("processor TLS configuration set to provided but none is provided") } - case flowslatest.ServerTLSAuto: + case flowslatest.TLSAuto: promTLS = &flowslatest.CertificateReference{ Type: "secret", Name: serviceName, CertFile: "tls.crt", CertKey: "tls.key", } - case flowslatest.ServerTLSDisabled: + case flowslatest.TLSDisabled, flowslatest.TLSAutoMTLS: // nothing to do there } return promTLS, nil @@ -256,7 +255,7 @@ func metricsSettings(desired *flowslatest.FlowCollectorSpec, vol *volumes.Builde Prefix: "netobserv_", NoPanic: true, } - if desired.Processor.Metrics.Server.TLS.Type != flowslatest.ServerTLSDisabled { + if desired.Processor.Metrics.Server.TLS.Type != flowslatest.TLSDisabled { cert, key := vol.AddCertificate(promTLS, "prom-certs") if cert != "" && key != "" { metricsSettings.TLS = &api.PromTLSConf{ @@ -330,7 +329,7 @@ func promService(desired *flowslatest.FlowCollectorSpec, svcName, namespace, app }}, }, } - if desired.Processor.Metrics.Server.TLS.Type == flowslatest.ServerTLSAuto { + if desired.Processor.Metrics.Server.TLS.Type == flowslatest.TLSAuto { svc.ObjectMeta.Annotations = map[string]string{ constants.OpenShiftCertificateAnnotation: svcName, } diff --git a/internal/controller/flp/flp_controller.go b/internal/controller/flp/flp_controller.go index b364e9be43..fff9bda7fa 100644 --- a/internal/controller/flp/flp_controller.go +++ b/internal/controller/flp/flp_controller.go @@ -235,14 +235,14 @@ func annotateKafkaCerts(ctx context.Context, info *reconcilers.Common, spec *flo } func reconcileMonitoringCerts(ctx context.Context, info *reconcilers.Common, tlsConfig *flowslatest.ServerTLS, ns string) error { - if tlsConfig.Type == flowslatest.ServerTLSProvided && tlsConfig.Provided != nil { + if tlsConfig.Type == flowslatest.TLSProvided && tlsConfig.Provided != nil { _, err := info.Watcher.ProcessCertRef(ctx, info.Client, tlsConfig.Provided, ns) if err != nil { return err } } - if !tlsConfig.InsecureSkipVerify && tlsConfig.ProvidedCaFile != nil && tlsConfig.ProvidedCaFile.File != "" { - _, err := info.Watcher.ProcessFileReference(ctx, info.Client, *tlsConfig.ProvidedCaFile, ns) + if !tlsConfig.InsecureSkipVerify && tlsConfig.ProvidedCAFile != nil && tlsConfig.ProvidedCAFile.File != "" { + _, err := info.Watcher.ProcessFileReference(ctx, info.Client, *tlsConfig.ProvidedCAFile, ns) if err != nil { return err } diff --git a/internal/controller/flp/flp_monolith_objects.go b/internal/controller/flp/flp_monolith_objects.go index 6b7839325a..f0f72bd3cf 100644 --- a/internal/controller/flp/flp_monolith_objects.go +++ b/internal/controller/flp/flp_monolith_objects.go @@ -129,7 +129,7 @@ func (b *monolithBuilder) configMaps() (*corev1.ConfigMap, string, *corev1.Confi b.info.Loki, b.info.ClusterInfo.GetID(), &b.volumes, - newGRPCPipeline(b.desired, &b.volumes), + newGRPCPipeline(b.desired, &b.volumes, b.info.ClusterInfo.IsOpenShift()), ) if err != nil { return nil, "", nil, err diff --git a/internal/controller/flp/flp_test.go b/internal/controller/flp/flp_test.go index 39bda85b29..6ce8776cd2 100644 --- a/internal/controller/flp/flp_test.go +++ b/internal/controller/flp/flp_test.go @@ -54,7 +54,7 @@ func getConfig() flowslatest.FlowCollectorSpec { Server: flowslatest.MetricsServerConfig{ Port: ptr.To(int32(9090)), TLS: flowslatest.ServerTLS{ - Type: flowslatest.ServerTLSDisabled, + Type: flowslatest.TLSDisabled, }, }, }, diff --git a/internal/controller/networkpolicy/np_objects.go b/internal/controller/networkpolicy/np_objects.go index 79c7e89cc0..27cdb1fecc 100644 --- a/internal/controller/networkpolicy/np_objects.go +++ b/internal/controller/networkpolicy/np_objects.go @@ -111,7 +111,7 @@ func buildMainNetworkPolicy(desired *flowslatest.FlowCollector, mgr *manager.Man allowedNamespacesIn = append(allowedNamespacesIn, constants.UWMonitoringNamespace) } - if desired.Spec.UseConsolePlugin() && mgr.ClusterInfo.HasConsolePlugin() { + if desired.Spec.UseWebConsole() && mgr.ClusterInfo.HasConsolePlugin() { advanced := helper.GetAdvancedPluginConfig(desired.Spec.ConsolePlugin.Advanced) np.Spec.Ingress = append(np.Spec.Ingress, networkingv1.NetworkPolicyIngressRule{ From: []networkingv1.NetworkPolicyPeer{ diff --git a/internal/pkg/helper/monitoring.go b/internal/pkg/helper/monitoring.go index 489f1e0d37..6a74d3ac74 100644 --- a/internal/pkg/helper/monitoring.go +++ b/internal/pkg/helper/monitoring.go @@ -29,7 +29,7 @@ func GetSecretOrConfigMap(file *flowslatest.FileReference) monitoringv1.SecretOr } func GetServiceMonitorTLSConfig(tls *flowslatest.ServerTLS, serverName string, isDownstream bool) (monitoringv1.Scheme, *monitoringv1.TLSConfig) { - if tls.Type == flowslatest.ServerTLSAuto { + if tls.Type == flowslatest.TLSAuto { if isDownstream { return "https", &monitoringv1.TLSConfig{ SafeTLSConfig: monitoringv1.SafeTLSConfig{ @@ -54,15 +54,15 @@ func GetServiceMonitorTLSConfig(tls *flowslatest.ServerTLS, serverName string, i } } - if tls.Type == flowslatest.ServerTLSProvided { + if tls.Type == flowslatest.TLSProvided { tlsOut := monitoringv1.TLSConfig{ SafeTLSConfig: monitoringv1.SafeTLSConfig{ ServerName: ptr.To(serverName), InsecureSkipVerify: &tls.InsecureSkipVerify, }, } - if !tls.InsecureSkipVerify && tls.ProvidedCaFile != nil && tls.ProvidedCaFile.File != "" { - tlsOut.SafeTLSConfig.CA = GetSecretOrConfigMap(tls.ProvidedCaFile) + if !tls.InsecureSkipVerify && tls.ProvidedCAFile != nil && tls.ProvidedCAFile.File != "" { + tlsOut.SafeTLSConfig.CA = GetSecretOrConfigMap(tls.ProvidedCAFile) } return "https", &tlsOut } diff --git a/internal/pkg/helper/tls.go b/internal/pkg/helper/tls.go new file mode 100644 index 0000000000..2b7813e2a7 --- /dev/null +++ b/internal/pkg/helper/tls.go @@ -0,0 +1,77 @@ +package helper + +import ( + flowslatest "github.com/netobserv/netobserv-operator/api/flowcollector/v1beta2" +) + +func DefaultCertificateReference(name, namespace string) *flowslatest.CertificateReference { + return &flowslatest.CertificateReference{ + Type: flowslatest.RefTypeSecret, + Name: name, + Namespace: namespace, + CertFile: "tls.crt", + CertKey: "tls.key", + } +} + +func DefaultCAReference(name, namespace string) *flowslatest.FileReference { + return &flowslatest.FileReference{ + Type: flowslatest.RefTypeConfigMap, + Name: name, + Namespace: namespace, + File: "service-ca.crt", + } +} + +// GetServiceClientTLSConfig returns configs for [ca, client cert] +func GetServiceClientTLSConfig(desired *flowslatest.ProcessorServiceConfig, defaultSecretName string, isOpenShift bool) (*flowslatest.FileReference, *flowslatest.CertificateReference) { + if desired != nil && desired.TLSType != flowslatest.TLSAuto && desired.TLSType != flowslatest.TLSAutoMTLS { + if desired.TLSType == flowslatest.TLSDisabled { + return nil, nil + } + if desired.ProvidedCertificates == nil { + // This should not happen, prevented by the validation webhook + return nil, nil + } + return desired.ProvidedCertificates.CAFile, desired.ProvidedCertificates.ClientCert + } + // Mode auto + caConfigMapName := "netobserv-ca" + if isOpenShift { + caConfigMapName = "openshift-service-ca.crt" + } + ca := DefaultCAReference(caConfigMapName, "") + if desired != nil && desired.TLSType == flowslatest.TLSAutoMTLS { + return ca, DefaultCertificateReference(defaultSecretName, "") + } + return ca, nil +} + +// GetServiceServerTLSConfig returns configs for [server cert, ca] +func GetServiceServerTLSConfig(desired *flowslatest.ProcessorServiceConfig, defaultSecretName string, isOpenShift bool) (*flowslatest.CertificateReference, *flowslatest.FileReference) { + if desired != nil && desired.TLSType != flowslatest.TLSAuto && desired.TLSType != flowslatest.TLSAutoMTLS { + if desired.TLSType == flowslatest.TLSDisabled { + return nil, nil + } + if desired.ProvidedCertificates == nil { + // This should not happen, prevented by the validation webhook + return nil, nil + } + if desired.ProvidedCertificates.ClientCert != nil { + // mTLS => provide the CA for server + return desired.ProvidedCertificates.ServerCert, desired.ProvidedCertificates.CAFile + } + // Simple TLS => no CA for server + return desired.ProvidedCertificates.ServerCert, nil + } + // Mode auto + caConfigMapName := "netobserv-ca" + if isOpenShift { + caConfigMapName = "openshift-service-ca.crt" + } + serverCert := DefaultCertificateReference(defaultSecretName, "") + if desired != nil && desired.TLSType == flowslatest.TLSAutoMTLS { + return serverCert, DefaultCAReference(caConfigMapName, "") + } + return serverCert, nil +} diff --git a/internal/pkg/helper/tls_test.go b/internal/pkg/helper/tls_test.go new file mode 100644 index 0000000000..73232d9496 --- /dev/null +++ b/internal/pkg/helper/tls_test.go @@ -0,0 +1,145 @@ +package helper + +import ( + "testing" + + flowslatest "github.com/netobserv/netobserv-operator/api/flowcollector/v1beta2" + "github.com/stretchr/testify/assert" +) + +func TestGetServiceTLSConfig_Disabled(t *testing.T) { + ca, cert := GetServiceClientTLSConfig(&flowslatest.ProcessorServiceConfig{TLSType: flowslatest.TLSDisabled}, "default-cert-secret", false) + assert.Nil(t, ca) + assert.Nil(t, cert) + + cert, ca = GetServiceServerTLSConfig(&flowslatest.ProcessorServiceConfig{TLSType: flowslatest.TLSDisabled}, "default-cert-secret", false) + assert.Nil(t, ca) + assert.Nil(t, cert) +} + +func TestGetServiceTLSConfig_Auto(t *testing.T) { + ca, cert := GetServiceClientTLSConfig(&flowslatest.ProcessorServiceConfig{TLSType: flowslatest.TLSAuto}, "default-cert-secret", false) + assert.Equal(t, &flowslatest.FileReference{ + Type: flowslatest.RefTypeConfigMap, + Name: "netobserv-ca", + File: "service-ca.crt", + }, ca) + assert.Nil(t, cert) + + cert, ca = GetServiceServerTLSConfig(&flowslatest.ProcessorServiceConfig{TLSType: flowslatest.TLSAuto}, "default-cert-secret", false) + assert.Nil(t, ca) + assert.Equal(t, &flowslatest.CertificateReference{ + Type: flowslatest.RefTypeSecret, + Name: "default-cert-secret", + CertFile: "tls.crt", + CertKey: "tls.key", + }, cert) + + // OpenShift + ca, cert = GetServiceClientTLSConfig(&flowslatest.ProcessorServiceConfig{TLSType: flowslatest.TLSAuto}, "default-cert-secret", true) + assert.Equal(t, &flowslatest.FileReference{ + Type: flowslatest.RefTypeConfigMap, + Name: "openshift-service-ca.crt", + File: "service-ca.crt", + }, ca) + assert.Nil(t, cert) + + cert, ca = GetServiceServerTLSConfig(&flowslatest.ProcessorServiceConfig{TLSType: flowslatest.TLSAuto}, "default-cert-secret", true) + assert.Nil(t, ca) + assert.Equal(t, &flowslatest.CertificateReference{ + Type: flowslatest.RefTypeSecret, + Name: "default-cert-secret", + CertFile: "tls.crt", + CertKey: "tls.key", + }, cert) +} + +func TestGetServiceTLSConfig_AutoMTLS(t *testing.T) { + ca, cert := GetServiceClientTLSConfig(&flowslatest.ProcessorServiceConfig{TLSType: flowslatest.TLSAutoMTLS}, "default-cert-secret-a", false) + assert.Equal(t, &flowslatest.FileReference{ + Type: flowslatest.RefTypeConfigMap, + Name: "netobserv-ca", + File: "service-ca.crt", + }, ca) + assert.Equal(t, &flowslatest.CertificateReference{ + Type: flowslatest.RefTypeSecret, + Name: "default-cert-secret-a", + CertFile: "tls.crt", + CertKey: "tls.key", + }, cert) + + cert, ca = GetServiceServerTLSConfig(&flowslatest.ProcessorServiceConfig{TLSType: flowslatest.TLSAutoMTLS}, "default-cert-secret-b", false) + assert.Equal(t, &flowslatest.FileReference{ + Type: flowslatest.RefTypeConfigMap, + Name: "netobserv-ca", + File: "service-ca.crt", + }, ca) + assert.Equal(t, &flowslatest.CertificateReference{ + Type: flowslatest.RefTypeSecret, + Name: "default-cert-secret-b", + CertFile: "tls.crt", + CertKey: "tls.key", + }, cert) +} + +func TestGetServiceTLSConfig_Provided_TLS(t *testing.T) { + cfg := flowslatest.ProcessorServiceConfig{ + TLSType: flowslatest.TLSProvided, + ProvidedCertificates: &flowslatest.ClientServerTLS{ + ServerCert: DefaultCertificateReference("custom-server-cert", ""), + CAFile: DefaultCAReference("custom-ca", ""), + }, + } + ca, cert := GetServiceClientTLSConfig(&cfg, "default-cert-secret-a", false) + assert.Equal(t, &flowslatest.FileReference{ + Type: flowslatest.RefTypeConfigMap, + Name: "custom-ca", + File: "service-ca.crt", + }, ca) + assert.Nil(t, cert) + + cert, ca = GetServiceServerTLSConfig(&cfg, "default-cert-secret-b", false) + assert.Nil(t, ca) + assert.Equal(t, &flowslatest.CertificateReference{ + Type: flowslatest.RefTypeSecret, + Name: "custom-server-cert", + CertFile: "tls.crt", + CertKey: "tls.key", + }, cert) +} + +func TestGetServiceTLSConfig_Provided_MTLS(t *testing.T) { + cfg := flowslatest.ProcessorServiceConfig{ + TLSType: flowslatest.TLSProvided, + ProvidedCertificates: &flowslatest.ClientServerTLS{ + ServerCert: DefaultCertificateReference("custom-server-cert", ""), + CAFile: DefaultCAReference("custom-ca", ""), + ClientCert: DefaultCertificateReference("custom-client-cert", ""), + }, + } + ca, cert := GetServiceClientTLSConfig(&cfg, "default-cert-secret-a", false) + assert.Equal(t, &flowslatest.FileReference{ + Type: flowslatest.RefTypeConfigMap, + Name: "custom-ca", + File: "service-ca.crt", + }, ca) + assert.Equal(t, &flowslatest.CertificateReference{ + Type: flowslatest.RefTypeSecret, + Name: "custom-client-cert", + CertFile: "tls.crt", + CertKey: "tls.key", + }, cert) + + cert, ca = GetServiceServerTLSConfig(&cfg, "default-cert-secret-b", false) + assert.Equal(t, &flowslatest.FileReference{ + Type: flowslatest.RefTypeConfigMap, + Name: "custom-ca", + File: "service-ca.crt", + }, ca) + assert.Equal(t, &flowslatest.CertificateReference{ + Type: flowslatest.RefTypeSecret, + Name: "custom-server-cert", + CertFile: "tls.crt", + CertKey: "tls.key", + }, cert) +}