diff --git a/api/flowcollector/v1beta2/flowcollector_types.go b/api/flowcollector/v1beta2/flowcollector_types.go index 012f299893..0a2194faa8 100644 --- a/api/flowcollector/v1beta2/flowcollector_types.go +++ b/api/flowcollector/v1beta2/flowcollector_types.go @@ -188,7 +188,8 @@ type FlowCollectorIPFIX struct { // - `EbpfManager`, to enable using eBPF Manager to manage NetObserv eBPF programs. [Unsupported (*)].
// - `UDNMapping`, to enable interfaces mapping to UDN.
// - `IPSec`, to track flows between nodes with IPsec encryption.
-// +kubebuilder:validation:Enum:="PacketDrop";"DNSTracking";"FlowRTT";"NetworkEvents";"PacketTranslation";"EbpfManager";"UDNMapping";"IPSec" +// - `TLSTracking`, to track TLS usage.
+// +kubebuilder:validation:Enum:="PacketDrop";"DNSTracking";"FlowRTT";"NetworkEvents";"PacketTranslation";"EbpfManager";"UDNMapping";"IPSec";"TLSTracking" type AgentFeature string const ( @@ -200,6 +201,7 @@ const ( EbpfManager AgentFeature = "EbpfManager" UDNMapping AgentFeature = "UDNMapping" IPSec AgentFeature = "IPSec" + TLSTracking AgentFeature = "TLSTracking" ) // Name of an eBPF agent alert. @@ -404,6 +406,7 @@ type FlowCollectorEBPF struct { // This feature requires mounting the kernel debug filesystem, so the eBPF agent pods must run as privileged via `spec.agent.ebpf.privileged`. // It requires using the OVN-Kubernetes network plugin with the Observability feature.
// - `IPSec`, to track flows between nodes with IPsec encryption.
+ // - `TLSTracking`, to track TLS usage.
// +optional Features []AgentFeature `json:"features,omitempty"` diff --git a/api/flowcollector/v1beta2/helper.go b/api/flowcollector/v1beta2/helper.go index 95346f0fc0..c58ac7abd7 100644 --- a/api/flowcollector/v1beta2/helper.go +++ b/api/flowcollector/v1beta2/helper.go @@ -119,6 +119,10 @@ func (spec *FlowCollectorEBPF) IsIPSecEnabled() bool { return spec.IsAgentFeatureEnabled(IPSec) } +func (spec *FlowCollectorEBPF) IsTLSTrackingEnabled() bool { + return spec.IsAgentFeatureEnabled(TLSTracking) +} + func (spec *FlowCollectorEBPF) IsEBPFMetricsEnabled() bool { return spec.Metrics.Enable == nil || *spec.Metrics.Enable } diff --git a/bundle/manifests/flows.netobserv.io_flowcollectors.yaml b/bundle/manifests/flows.netobserv.io_flowcollectors.yaml index e4556cb6d0..64975fcd0b 100644 --- a/bundle/manifests/flows.netobserv.io_flowcollectors.yaml +++ b/bundle/manifests/flows.netobserv.io_flowcollectors.yaml @@ -1141,6 +1141,7 @@ spec: This feature requires mounting the kernel debug filesystem, so the eBPF agent pods must run as privileged via `spec.agent.ebpf.privileged`. It requires using the OVN-Kubernetes network plugin with the Observability feature.
- `IPSec`, to track flows between nodes with IPsec encryption.
+ - `TLSTracking`, to track TLS usage.
items: description: |- Agent feature, can be one of:
@@ -1152,6 +1153,7 @@ spec: - `EbpfManager`, to enable using eBPF Manager to manage NetObserv eBPF programs. [Unsupported (*)].
- `UDNMapping`, to enable interfaces mapping to UDN.
- `IPSec`, to track flows between nodes with IPsec encryption.
+ - `TLSTracking`, to track TLS usage.
enum: - PacketDrop - DNSTracking @@ -1161,6 +1163,7 @@ spec: - EbpfManager - UDNMapping - IPSec + - TLSTracking type: string type: array flowFilter: diff --git a/config/crd/bases/flows.netobserv.io_flowcollectors.yaml b/config/crd/bases/flows.netobserv.io_flowcollectors.yaml index 1900755baa..5b003f32ab 100644 --- a/config/crd/bases/flows.netobserv.io_flowcollectors.yaml +++ b/config/crd/bases/flows.netobserv.io_flowcollectors.yaml @@ -1067,6 +1067,7 @@ spec: This feature requires mounting the kernel debug filesystem, so the eBPF agent pods must run as privileged via `spec.agent.ebpf.privileged`. It requires using the OVN-Kubernetes network plugin with the Observability feature.
- `IPSec`, to track flows between nodes with IPsec encryption.
+ - `TLSTracking`, to track TLS usage.
items: description: |- Agent feature, can be one of:
@@ -1078,6 +1079,7 @@ spec: - `EbpfManager`, to enable using eBPF Manager to manage NetObserv eBPF programs. [Unsupported (*)].
- `UDNMapping`, to enable interfaces mapping to UDN.
- `IPSec`, to track flows between nodes with IPsec encryption.
+ - `TLSTracking`, to track TLS usage.
enum: - PacketDrop - DNSTracking @@ -1087,6 +1089,7 @@ spec: - EbpfManager - UDNMapping - IPSec + - TLSTracking type: string type: array flowFilter: diff --git a/config/samples/flowmetrics/tls_egress_traffic.yaml b/config/samples/flowmetrics/tls_egress_traffic.yaml new file mode 100644 index 0000000000..90be6f8174 --- /dev/null +++ b/config/samples/flowmetrics/tls_egress_traffic.yaml @@ -0,0 +1,37 @@ +apiVersion: flows.netobserv.io/v1alpha1 +kind: FlowMetric +metadata: + name: tls-egress-traffic + namespace: netobserv +spec: + type: Counter + valueField: Bytes + labels: [SrcSubnetLabel,SrcK8S_Namespace,SrcK8S_OwnerName,SrcK8S_OwnerType,DstSubnetLabel,DstK8S_Namespace,DstK8S_OwnerName,DstK8S_OwnerType,Proto,TLSVersion,TLSTypes] + direction: Egress + filters: + - field: SrcK8S_Namespace + matchType: Presence + charts: + - dashboardName: TLS + title: "Egress TLS traffic" + unit: percent + type: SingleStat + queries: + - promQL: 'sum(rate(netobserv_tls_egress_traffic{TLSVersion!=""}[2m])) / sum(rate(netobserv_tls_egress_traffic[2m]))' + legend: "" + - dashboardName: TLS + sectionName: Per namespace + title: Egress traffic without TLS + unit: Bps + type: StackArea + queries: + - promQL: 'topk(10, sum(rate(netobserv_tls_egress_traffic{TLSVersion=""}[2m])) by (SrcK8S_Namespace))' + legend: "{{SrcK8S_Namespace}}" + - dashboardName: TLS + sectionName: Per version + title: Egress traffic per TLS version + unit: Bps + type: StackArea + queries: + - promQL: 'topk(10, sum(rate(netobserv_tls_egress_traffic{TLSVersion!~"|.*0x.*"}[2m])) by (TLSVersion))' + legend: "{{TLSVersion}}" diff --git a/config/samples/flowmetrics/tls_ingress_traffic.yaml b/config/samples/flowmetrics/tls_ingress_traffic.yaml new file mode 100644 index 0000000000..b2eb92f225 --- /dev/null +++ b/config/samples/flowmetrics/tls_ingress_traffic.yaml @@ -0,0 +1,37 @@ +apiVersion: flows.netobserv.io/v1alpha1 +kind: FlowMetric +metadata: + name: tls-ingress-traffic + namespace: netobserv +spec: + type: Counter + valueField: Bytes + labels: [SrcSubnetLabel,SrcK8S_Namespace,SrcK8S_OwnerName,SrcK8S_OwnerType,DstSubnetLabel,DstK8S_Namespace,DstK8S_OwnerName,DstK8S_OwnerType,Proto,TLSVersion,TLSTypes] + direction: Ingress + filters: + - field: DstK8S_Namespace + matchType: Presence + charts: + - dashboardName: TLS + title: "Ingress TLS traffic" + unit: percent + type: SingleStat + queries: + - promQL: 'sum(rate(netobserv_tls_ingress_traffic{TLSVersion!=""}[2m])) / sum(rate(netobserv_tls_ingress_traffic[2m]))' + legend: "" + - dashboardName: TLS + sectionName: Per namespace + title: Ingress traffic without TLS + unit: Bps + type: StackArea + queries: + - promQL: 'topk(10, sum(rate(netobserv_tls_ingress_traffic{TLSVersion=""}[2m])) by (DstK8S_Namespace))' + legend: "{{DstK8S_Namespace}}" + - dashboardName: TLS + sectionName: Per version + title: Ingress traffic per TLS version + unit: Bps + type: StackArea + queries: + - promQL: 'topk(10, sum(rate(netobserv_tls_ingress_traffic{TLSVersion!~"|.*0x.*"}[2m])) by (TLSVersion))' + legend: "{{TLSVersion}}" diff --git a/config/samples/flows_v1beta2_flowcollector.yaml b/config/samples/flows_v1beta2_flowcollector.yaml index 082ad1c477..675b55974d 100644 --- a/config/samples/flows_v1beta2_flowcollector.yaml +++ b/config/samples/flows_v1beta2_flowcollector.yaml @@ -27,6 +27,7 @@ spec: # - "EbpfManager" # - "UDNMapping" # - "IPSec" + # - "TLSTracking" interfaces: [] excludeInterfaces: ["lo"] # kafkaBatchSize: 1048576 diff --git a/docs/FlowCollector.md b/docs/FlowCollector.md index 056a547a95..333b278ce8 100644 --- a/docs/FlowCollector.md +++ b/docs/FlowCollector.md @@ -313,9 +313,10 @@ IMPORTANT: This feature is available as a Technology Preview.
- `UDNMapping`: Enable interfaces mapping to User Defined Networks (UDN).
This feature requires mounting the kernel debug filesystem, so the eBPF agent pods must run as privileged via `spec.agent.ebpf.privileged`. It requires using the OVN-Kubernetes network plugin with the Observability feature.
-- `IPSec`, to track flows between nodes with IPsec encryption.

+- `IPSec`, to track flows between nodes with IPsec encryption.
+- `TLSTracking`, to track TLS usage.


- Enum: PacketDrop, DNSTracking, FlowRTT, NetworkEvents, PacketTranslation, EbpfManager, UDNMapping, IPSec
+ Enum: PacketDrop, DNSTracking, FlowRTT, NetworkEvents, PacketTranslation, EbpfManager, UDNMapping, IPSec, TLSTracking
false diff --git a/helm/crds/flows.netobserv.io_flowcollectors.yaml b/helm/crds/flows.netobserv.io_flowcollectors.yaml index 7dd489f468..b57389b4d4 100644 --- a/helm/crds/flows.netobserv.io_flowcollectors.yaml +++ b/helm/crds/flows.netobserv.io_flowcollectors.yaml @@ -1071,6 +1071,7 @@ spec: This feature requires mounting the kernel debug filesystem, so the eBPF agent pods must run as privileged via `spec.agent.ebpf.privileged`. It requires using the OVN-Kubernetes network plugin with the Observability feature.
- `IPSec`, to track flows between nodes with IPsec encryption.
+ - `TLSTracking`, to track TLS usage.
items: description: |- Agent feature, can be one of:
@@ -1082,6 +1083,7 @@ spec: - `EbpfManager`, to enable using eBPF Manager to manage NetObserv eBPF programs. [Unsupported (*)].
- `UDNMapping`, to enable interfaces mapping to UDN.
- `IPSec`, to track flows between nodes with IPsec encryption.
+ - `TLSTracking`, to track TLS usage.
enum: - PacketDrop - DNSTracking @@ -1091,6 +1093,7 @@ spec: - EbpfManager - UDNMapping - IPSec + - TLSTracking type: string type: array flowFilter: diff --git a/internal/controller/consoleplugin/config/static-frontend-config.yaml b/internal/controller/consoleplugin/config/static-frontend-config.yaml index 8edb61e1bb..1881312597 100644 --- a/internal/controller/consoleplugin/config/static-frontend-config.yaml +++ b/internal/controller/consoleplugin/config/static-frontend-config.yaml @@ -408,7 +408,7 @@ columns: default: false width: 15 - id: Proto - group: L3 Layer + group: Protocol Info name: Protocol tooltip: The value of the protocol number in the IP packet header field: Proto @@ -416,36 +416,68 @@ columns: default: true width: 10 - id: Dscp - group: L3 Layer + group: Protocol Info name: DSCP tooltip: The value of the Differentiated Services Code Point field: Dscp filter: dscp + default: false + width: 10 + - id: TCPFlags + group: Protocol Info + name: TCP Flags + tooltip: Logical OR combination of unique TCP flags comprised in the flow, according to RFC-9293, with additional custom values. + field: Flags + filter: tcp_flags + default: false + width: 10 + - id: TLSVersion + group: Protocol Info + name: TLS Version + tooltip: TLS version found in handshake headers + field: TLSVersion + filter: tls_version default: true width: 10 + feature: tlsTracking + - id: TLSCipherSuite + group: Protocol Info + name: TLS Cipher Suite + field: TLSCipherSuite + filter: tls_cipher_suite + width: 15 + feature: tlsTracking + - id: TLSCurve + group: Protocol Info + name: TLS Curve + field: TLSCurve + filter: tls_curve + width: 10 + feature: tlsTracking + - id: TLSTypes + group: Protocol Info + name: TLS Types + tooltip: TLS packet types, such as ClientHello or AppData + field: TLSTypes + filter: tls_types + width: 15 + feature: tlsTracking - id: IcmpType - group: ICMP - name: Type + group: Protocol Info + name: ICMP Type tooltip: The type of the ICMP message field: IcmpType filter: icmp_type default: false width: 10 - id: IcmpCode - group: ICMP - name: Code + group: Protocol Info + name: ICMP Code tooltip: The code of the ICMP message field: IcmpCode filter: icmp_code default: false width: 10 - - id: TCPFlags - name: TCP Flags - tooltip: Logical OR combination of unique TCP flags comprised in the flow, according to RFC-9293, with additional custom values. - field: Flags - filter: tcp_flags - default: false - width: 10 - id: FlowDirection name: Node Direction tooltip: The interpreted direction of the flow observed at the Node observation point. @@ -1117,6 +1149,30 @@ filters: - A protocol number like 6, 17 - A IANA name like TCP, UDP docUrl: https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml + - id: tls_version + name: TLS version + component: text + placeholder: 'E.g: TLS 1.2' + hint: Specify a version of TLS. + feature: tlsTracking + - id: tls_cipher_suite + name: TLS cipher suite + component: text + placeholder: 'E.g: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256' + hint: Specify a TLS cipher suite. + feature: tlsTracking + - id: tls_curve + name: TLS curve + component: text + placeholder: 'E.g: X25519' + hint: Specify a TLS curve name. + feature: tlsTracking + - id: tls_types + name: TLS packet type + component: text + placeholder: 'E.g: ClientHello, AppData' + hint: Specify a TLS type of packet. + feature: tlsTracking - id: dscp name: DSCP component: autocomplete @@ -1524,6 +1580,18 @@ fields: - name: Proto type: number description: L4 protocol + - name: TLSVersion + type: string + description: TLS version + - name: TLSTypes + type: string[] + description: TLS message types (bitfield) + - name: TLSCipherSuite + type: string + description: TLS cipher suite + - name: TLSCurve + type: string + description: TLS curve name - name: Dscp type: number description: Differentiated Services Code Point (DSCP) value diff --git a/internal/controller/consoleplugin/consoleplugin_objects.go b/internal/controller/consoleplugin/consoleplugin_objects.go index d9ea78b1e8..9eefbf8375 100644 --- a/internal/controller/consoleplugin/consoleplugin_objects.go +++ b/internal/controller/consoleplugin/consoleplugin_objects.go @@ -456,6 +456,7 @@ func (b *builder) getPromConfig(ctx context.Context) cfg.PrometheusConfig { return config } +// nolint:cyclop // no real complexity here, just long boilerplate func (b *builder) setFrontendConfig(fconf *cfg.FrontendConfig, metrics []cfg.MetricInfo) (string, error) { if b.desired.Agent.EBPF.IsPktDropEnabled() { fconf.Features = append(fconf.Features, "pktDrop") @@ -489,6 +490,10 @@ func (b *builder) setFrontendConfig(fconf *cfg.FrontendConfig, metrics []cfg.Met fconf.Features = append(fconf.Features, "ipsec") } + if b.desired.Agent.EBPF.IsTLSTrackingEnabled() { + fconf.Features = append(fconf.Features, "tlsTracking") + } + fconf.RecordTypes = helper.GetRecordTypes(&b.desired.Processor) fconf.PortNaming = b.desired.ConsolePlugin.PortNaming fconf.QuickFilters = b.desired.ConsolePlugin.QuickFilters diff --git a/internal/controller/ebpf/agent_controller.go b/internal/controller/ebpf/agent_controller.go index 4b7aa2e5c6..3a83bc08b9 100644 --- a/internal/controller/ebpf/agent_controller.go +++ b/internal/controller/ebpf/agent_controller.go @@ -73,6 +73,7 @@ const ( envEnableEbpfMgr = "EBPF_PROGRAM_MANAGER_MODE" envEnableUDNMapping = "ENABLE_UDN_MAPPING" envEnableIPsec = "ENABLE_IPSEC_TRACKING" + envEnableTLSTracking = "ENABLE_TLS_TRACKING" envDNSTrackingPort = "DNS_TRACKING_PORT" envPreferredInterface = "PREFERRED_INTERFACE_FOR_MAC_PREFIX" envAttachMode = "TC_ATTACH_MODE" @@ -782,6 +783,13 @@ func getEnvConfig(coll *flowslatest.FlowCollector, cinfo *cluster.Info) []corev1 }) } + if coll.Spec.Agent.EBPF.IsTLSTrackingEnabled() { + config = append(config, corev1.EnvVar{ + Name: envEnableTLSTracking, + Value: "true", + }) + } + if coll.Spec.Agent.EBPF.IsEBPFMetricsEnabled() { config = append(config, corev1.EnvVar{ Name: envEnableMetrics, diff --git a/internal/controller/ebpf/bpfmanager-controller.go b/internal/controller/ebpf/bpfmanager-controller.go index afc75f6a68..8717ef982a 100644 --- a/internal/controller/ebpf/bpfmanager-controller.go +++ b/internal/controller/ebpf/bpfmanager-controller.go @@ -61,7 +61,7 @@ func (c *AgentController) bpfmanAttachNetobserv(ctx context.Context, fc *flowsla func prepareBpfApplication(bpfApp *bpfmaniov1alpha1.ClusterBpfApplication, fc *flowslatest.FlowCollector, netobservBCImage string) { samplingValue := make([]byte, 4) dnsPortValue := make([]byte, 2) - var enableDNSValue, enableRTTValue, enableFLowFilterValue, enableNetworkEvents, traceValue, networkEventsGroupIDValue, enablePktTranslation, enableIPSecValue []byte + var enableDNSValue, enableRTTValue, enableFLowFilterValue, enableNetworkEvents, traceValue, networkEventsGroupIDValue, enablePktTranslation, enableIPSecValue, enableTLSTracking []byte binary.NativeEndian.PutUint32(samplingValue, uint32(*fc.Spec.Agent.EBPF.Sampling)) @@ -93,6 +93,10 @@ func prepareBpfApplication(bpfApp *bpfmaniov1alpha1.ClusterBpfApplication, fc *f enableIPSecValue = append(enableIPSecValue, uint8(1)) } + if fc.Spec.Agent.EBPF.IsTLSTrackingEnabled() { + enableTLSTracking = append(enableTLSTracking, uint8(1)) + } + bpfApp.Labels = map[string]string{ "app": netobservApp, } @@ -124,6 +128,7 @@ func prepareBpfApplication(bpfApp *bpfmaniov1alpha1.ClusterBpfApplication, fc *f "network_events_monitoring_groupid": networkEventsGroupIDValue, "enable_pkt_translation_tracking": enablePktTranslation, "enable_ipsec": enableIPSecValue, + "enable_tls_usage_tracking": enableTLSTracking, } bpfApp.Spec.BpfAppCommon.ByteCode = bpfmaniov1alpha1.ByteCodeSelector{ diff --git a/internal/pkg/helper/cardinality/cardinality.json b/internal/pkg/helper/cardinality/cardinality.json index b9c1d2c9fb..077293b7d6 100644 --- a/internal/pkg/helper/cardinality/cardinality.json +++ b/internal/pkg/helper/cardinality/cardinality.json @@ -68,6 +68,7 @@ "XlatDstAddr": "avoid", "Udns": "careful", "IPSecStatus": "fine", + "TLSVersion": "fine", "_RecordType": "fine", "_HashId": "avoid" } diff --git a/internal/pkg/metrics/predefined_metrics.go b/internal/pkg/metrics/predefined_metrics.go index e9029708f6..5065c229cd 100644 --- a/internal/pkg/metrics/predefined_metrics.go +++ b/internal/pkg/metrics/predefined_metrics.go @@ -21,9 +21,9 @@ const ( var ( latencyBuckets = []string{".005", ".01", ".02", ".03", ".04", ".05", ".075", ".1", ".25", "1"} mapLabels = map[string][]string{ - tagNodes: {"K8S_ClusterName", "SrcK8S_Zone", "DstK8S_Zone", "SrcK8S_HostName", "DstK8S_HostName"}, - tagNamespaces: {"K8S_ClusterName", "SrcK8S_Zone", "DstK8S_Zone", "SrcK8S_Namespace", "DstK8S_Namespace", "K8S_FlowLayer", "SrcSubnetLabel", "DstSubnetLabel"}, - tagWorkloads: {"K8S_ClusterName", "SrcK8S_Zone", "DstK8S_Zone", "SrcK8S_Namespace", "DstK8S_Namespace", "K8S_FlowLayer", "SrcSubnetLabel", "DstSubnetLabel", "SrcK8S_NetworkName", "DstK8S_NetworkName", "SrcK8S_OwnerName", "DstK8S_OwnerName", "SrcK8S_OwnerType", "DstK8S_OwnerType", "SrcK8S_Type", "DstK8S_Type"}, + tagNodes: {"K8S_ClusterName", "SrcK8S_Zone", "DstK8S_Zone", "SrcK8S_HostName", "DstK8S_HostName", "TLSVersion"}, + tagNamespaces: {"K8S_ClusterName", "SrcK8S_Zone", "DstK8S_Zone", "SrcK8S_Namespace", "DstK8S_Namespace", "K8S_FlowLayer", "SrcSubnetLabel", "DstSubnetLabel", "TLSVersion"}, + tagWorkloads: {"K8S_ClusterName", "SrcK8S_Zone", "DstK8S_Zone", "SrcK8S_Namespace", "DstK8S_Namespace", "K8S_FlowLayer", "SrcSubnetLabel", "DstSubnetLabel", "SrcK8S_NetworkName", "DstK8S_NetworkName", "SrcK8S_OwnerName", "DstK8S_OwnerName", "SrcK8S_OwnerType", "DstK8S_OwnerType", "SrcK8S_Type", "DstK8S_Type", "TLSVersion"}, } mapValueFields = map[string]string{ tagBytes: "Bytes", @@ -322,6 +322,9 @@ func GetDefinitions(fc *flowslatest.FlowCollectorSpec, allMetrics bool) []metric if !fc.Agent.EBPF.IsUDNMappingEnabled() && !fc.Processor.HasSecondaryIndexes() { labelsToRemove = append(labelsToRemove, "SrcK8S_NetworkName", "DstK8S_NetworkName") } + if !fc.Agent.EBPF.IsTLSTrackingEnabled() { + labelsToRemove = append(labelsToRemove, "TLSVersion") + } var filterRecordType *metricslatest.MetricFilter if fc.Processor.LogTypes != nil {