diff --git a/api/flowcollector/v1beta2/flowcollector_types.go b/api/flowcollector/v1beta2/flowcollector_types.go index 3be46f531..22dc5db18 100644 --- a/api/flowcollector/v1beta2/flowcollector_types.go +++ b/api/flowcollector/v1beta2/flowcollector_types.go @@ -183,7 +183,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" +// - `OpenSSLTracking`, to track SSL/TLS encrypted traffic using OpenSSL uprobes [Technology Preview].
+// +kubebuilder:validation:Enum:="PacketDrop";"DNSTracking";"FlowRTT";"NetworkEvents";"PacketTranslation";"EbpfManager";"UDNMapping";"IPSec";"OpenSSLTracking" type AgentFeature string const ( @@ -195,6 +196,7 @@ const ( EbpfManager AgentFeature = "EbpfManager" UDNMapping AgentFeature = "UDNMapping" IPSec AgentFeature = "IPSec" + OpenSSLTracking AgentFeature = "OpenSSLTracking" ) // Name of an eBPF agent alert. diff --git a/api/flowcollector/v1beta2/flowcollector_validation_webhook.go b/api/flowcollector/v1beta2/flowcollector_validation_webhook.go index 5ae837743..c6bdd697f 100644 --- a/api/flowcollector/v1beta2/flowcollector_validation_webhook.go +++ b/api/flowcollector/v1beta2/flowcollector_validation_webhook.go @@ -25,10 +25,11 @@ var ( CurrentClusterInfo *cluster.Info needPrivileged = []AgentFeature{UDNMapping, NetworkEvents} neededOpenShiftVersion = map[AgentFeature]string{ - PacketDrop: "4.14.0", - UDNMapping: "4.18.0", - NetworkEvents: "4.19.0", - EbpfManager: "4.19.0", + PacketDrop: "4.14.0", + UDNMapping: "4.18.0", + NetworkEvents: "4.19.0", + EbpfManager: "4.19.0", + OpenSSLTracking: "4.14.0", // Requires uprobe support } ) diff --git a/api/flowcollector/v1beta2/helper.go b/api/flowcollector/v1beta2/helper.go index 96ae00522..ad29fb594 100644 --- a/api/flowcollector/v1beta2/helper.go +++ b/api/flowcollector/v1beta2/helper.go @@ -109,6 +109,10 @@ func (spec *FlowCollectorEBPF) IsIPSecEnabled() bool { return spec.IsAgentFeatureEnabled(IPSec) } +func (spec *FlowCollectorEBPF) IsOpenSSLTrackingEnabled() bool { + return spec.IsAgentFeatureEnabled(OpenSSLTracking) +} + 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 9872b57ad..90965cf2e 100644 --- a/bundle/manifests/flows.netobserv.io_flowcollectors.yaml +++ b/bundle/manifests/flows.netobserv.io_flowcollectors.yaml @@ -1152,6 +1152,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.
+ - `OpenSSLTracking`, to track SSL/TLS encrypted traffic using OpenSSL uprobes [Technology Preview].
enum: - PacketDrop - DNSTracking @@ -1161,6 +1162,7 @@ spec: - EbpfManager - UDNMapping - IPSec + - OpenSSLTracking 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 b1f7cf234..949a2cc32 100644 --- a/config/crd/bases/flows.netobserv.io_flowcollectors.yaml +++ b/config/crd/bases/flows.netobserv.io_flowcollectors.yaml @@ -1078,6 +1078,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.
+ - `OpenSSLTracking`, to track SSL/TLS encrypted traffic using OpenSSL uprobes [Technology Preview].
enum: - PacketDrop - DNSTracking @@ -1087,6 +1088,7 @@ spec: - EbpfManager - UDNMapping - IPSec + - OpenSSLTracking type: string type: array flowFilter: diff --git a/helm/crds/flows.netobserv.io_flowcollectors.yaml b/helm/crds/flows.netobserv.io_flowcollectors.yaml index 39d7af3cd..3e96b6a60 100644 --- a/helm/crds/flows.netobserv.io_flowcollectors.yaml +++ b/helm/crds/flows.netobserv.io_flowcollectors.yaml @@ -1082,6 +1082,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.
+ - `OpenSSLTracking`, to track SSL/TLS encrypted traffic using OpenSSL uprobes [Technology Preview].
enum: - PacketDrop - DNSTracking @@ -1091,6 +1092,7 @@ spec: - EbpfManager - UDNMapping - IPSec + - OpenSSLTracking type: string type: array flowFilter: diff --git a/internal/controller/ebpf/agent_controller.go b/internal/controller/ebpf/agent_controller.go index f83f07792..46d6251a6 100644 --- a/internal/controller/ebpf/agent_controller.go +++ b/internal/controller/ebpf/agent_controller.go @@ -72,6 +72,8 @@ const ( envEnableEbpfMgr = "EBPF_PROGRAM_MANAGER_MODE" envEnableUDNMapping = "ENABLE_UDN_MAPPING" envEnableIPsec = "ENABLE_IPSEC_TRACKING" + envEnableOpenSSLTracking = "ENABLE_OPENSSL_TRACKING" + envOpenSSLPath = "OPENSSL_PATH" envDNSTrackingPort = "DNS_TRACKING_PORT" envPreferredInterface = "PREFERRED_INTERFACE_FOR_MAC_PREFIX" envAttachMode = "TC_ATTACH_MODE" @@ -100,6 +102,7 @@ const ( const ( defaultDNSTrackingPort = "53" + defaultOpenSSLPath = "/usr/lib64/libssl.so.3" bpfmanMapsVolumeName = "bpfman-maps" bpfManBpfFSPath = "/run/netobserv/maps" ) @@ -762,6 +765,13 @@ func getEnvConfig(coll *flowslatest.FlowCollector, cinfo *cluster.Info) []corev1 }) } + if coll.Spec.Agent.EBPF.IsOpenSSLTrackingEnabled() { + config = append(config, corev1.EnvVar{ + Name: envEnableOpenSSLTracking, + Value: "true", + }) + } + if coll.Spec.Agent.EBPF.IsEBPFMetricsEnabled() { config = append(config, corev1.EnvVar{ Name: envEnableMetrics, @@ -810,6 +820,7 @@ func getEnvConfig(coll *flowslatest.FlowCollector, cinfo *cluster.Info) []corev1 envNetworkEventsGroupID: defaultNetworkEventsGroupID, envPreferredInterface: defaultPreferredInterface, envAttachMode: defaultAttach, + envOpenSSLPath: defaultOpenSSLPath, } advancedConfig := helper.GetAdvancedAgentConfig(coll.Spec.Agent.EBPF.Advanced) moreConfig := helper.BuildEnvFromDefaults(advancedConfig.Env, defaults) diff --git a/internal/controller/ebpf/agent_controller_test.go b/internal/controller/ebpf/agent_controller_test.go index ef6e482f7..d545bd738 100644 --- a/internal/controller/ebpf/agent_controller_test.go +++ b/internal/controller/ebpf/agent_controller_test.go @@ -111,6 +111,7 @@ func TestGetEnvConfig_Default(t *testing.T) { }}, {Name: "DNS_TRACKING_PORT", Value: "53"}, {Name: "NETWORK_EVENTS_MONITORING_GROUP_ID", Value: "10"}, + {Name: "OPENSSL_PATH", Value: "/usr/lib64/libssl.so.3"}, {Name: "PREFERRED_INTERFACE_FOR_MAC_PREFIX", Value: "0a:58=eth0"}, {Name: "TC_ATTACH_MODE", Value: "tcx"}, }, env) @@ -159,6 +160,7 @@ func TestGetEnvConfig_WithOverrides(t *testing.T) { }}, {Name: "DNS_TRACKING_PORT", Value: "5353"}, {Name: "NETWORK_EVENTS_MONITORING_GROUP_ID", Value: "any"}, + {Name: "OPENSSL_PATH", Value: "/usr/lib64/libssl.so.3"}, {Name: "PREFERRED_INTERFACE_FOR_MAC_PREFIX", Value: "0a:58=ens5"}, {Name: "TC_ATTACH_MODE", Value: "any"}, }, env) @@ -190,6 +192,7 @@ func TestGetEnvConfig_OCP4_14(t *testing.T) { }}, {Name: "DNS_TRACKING_PORT", Value: "53"}, {Name: "NETWORK_EVENTS_MONITORING_GROUP_ID", Value: "10"}, + {Name: "OPENSSL_PATH", Value: "/usr/lib64/libssl.so.3"}, {Name: "PREFERRED_INTERFACE_FOR_MAC_PREFIX", Value: "0a:58=eth0"}, {Name: "TC_ATTACH_MODE", Value: "tc"}, }, env) diff --git a/internal/controller/ebpf/bpfmanager-controller.go b/internal/controller/ebpf/bpfmanager-controller.go index 055257ebd..60992d54a 100644 --- a/internal/controller/ebpf/bpfmanager-controller.go +++ b/internal/controller/ebpf/bpfmanager-controller.go @@ -59,9 +59,17 @@ func (c *AgentController) bpfmanAttachNetobserv(ctx context.Context, fc *flowsla } func prepareBpfApplication(bpfApp *bpfmaniov1alpha1.ClusterBpfApplication, fc *flowslatest.FlowCollector, netobservBCImage string) { + openSSLPath := setupGlobalData(bpfApp, fc) + setupByteCode(bpfApp, netobservBCImage) + setupBasePrograms(bpfApp) + setupOptionalPrograms(bpfApp, fc, openSSLPath) +} + +func setupGlobalData(bpfApp *bpfmaniov1alpha1.ClusterBpfApplication, fc *flowslatest.FlowCollector) 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, enableOpenSSLValue []byte + openSSLPath := defaultOpenSSLPath binary.NativeEndian.PutUint32(samplingValue, uint32(*fc.Spec.Agent.EBPF.Sampling)) @@ -93,6 +101,10 @@ func prepareBpfApplication(bpfApp *bpfmaniov1alpha1.ClusterBpfApplication, fc *f enableIPSecValue = append(enableIPSecValue, uint8(1)) } + if fc.Spec.Agent.EBPF.IsOpenSSLTrackingEnabled() { + enableOpenSSLValue = append(enableOpenSSLValue, uint8(1)) + } + bpfApp.Labels = map[string]string{ "app": netobservApp, } @@ -105,6 +117,8 @@ func prepareBpfApplication(bpfApp *bpfmaniov1alpha1.ClusterBpfApplication, fc *f dnsPortValue = []byte(v) } else if k == envNetworkEventsGroupID { networkEventsGroupIDValue = []byte(v) + } else if k == envOpenSSLPath { + openSSLPath = v } } @@ -124,14 +138,22 @@ func prepareBpfApplication(bpfApp *bpfmaniov1alpha1.ClusterBpfApplication, fc *f "network_events_monitoring_groupid": networkEventsGroupIDValue, "enable_pkt_translation_tracking": enablePktTranslation, "enable_ipsec": enableIPSecValue, + "enable_openssl_tracking": enableOpenSSLValue, } + return openSSLPath +} + +func setupByteCode(bpfApp *bpfmaniov1alpha1.ClusterBpfApplication, netobservBCImage string) { bpfApp.Spec.BpfAppCommon.ByteCode = bpfmaniov1alpha1.ByteCodeSelector{ Image: &bpfmaniov1alpha1.ByteCodeImage{ Url: netobservBCImage, ImagePullPolicy: bpfmaniov1alpha1.PullIfNotPresent, }, } +} + +func setupBasePrograms(bpfApp *bpfmaniov1alpha1.ClusterBpfApplication) { bpfApp.Spec.Programs = []bpfmaniov1alpha1.ClBpfApplicationProgram{ { Name: "tcx_ingress_flow_parse", @@ -164,128 +186,155 @@ func prepareBpfApplication(bpfApp *bpfmaniov1alpha1.ClusterBpfApplication, fc *f }, }, } +} +func setupOptionalPrograms(bpfApp *bpfmaniov1alpha1.ClusterBpfApplication, fc *flowslatest.FlowCollector, openSSLPath string) { if fc.Spec.Agent.EBPF.IsFlowRTTEnabled() { - bpfApp.Spec.Programs = append(bpfApp.Spec.Programs, []bpfmaniov1alpha1.ClBpfApplicationProgram{ - { - Name: "tcp_rcv_fentry", - Type: bpfmaniov1alpha1.ProgTypeFentry, - FEntry: &bpfmaniov1alpha1.ClFentryProgramInfo{ - ClFentryLoadInfo: bpfmaniov1alpha1.ClFentryLoadInfo{ - Function: "tcp_rcv_established", - }, - Links: []bpfmaniov1alpha1.ClFentryAttachInfo{ - { - Mode: bpfmaniov1alpha1.Attach, - }, - }, - }, - }, - }...) + addRTTPrograms(bpfApp) } if fc.Spec.Agent.EBPF.IsNetworkEventsEnabled() { - bpfApp.Spec.Programs = append(bpfApp.Spec.Programs, []bpfmaniov1alpha1.ClBpfApplicationProgram{ - { - Name: "network_events_monitoring", - Type: bpfmaniov1alpha1.ProgTypeKprobe, - KProbe: &bpfmaniov1alpha1.ClKprobeProgramInfo{ - Links: []bpfmaniov1alpha1.ClKprobeAttachInfo{ - { - Function: "psample_sample_packet", - }, - }, - }, - }, - }...) + addNetworkEventsPrograms(bpfApp) } if fc.Spec.Agent.EBPF.IsPktDropEnabled() { - bpfApp.Spec.Programs = append(bpfApp.Spec.Programs, []bpfmaniov1alpha1.ClBpfApplicationProgram{ - { - Name: "kfree_skb", - Type: bpfmaniov1alpha1.ProgTypeTracepoint, - TracePoint: &bpfmaniov1alpha1.ClTracepointProgramInfo{ - Links: []bpfmaniov1alpha1.ClTracepointAttachInfo{ - { - Name: "skb/kfree_skb", - }, - }, - }, - }, - }...) + addPktDropPrograms(bpfApp) } if fc.Spec.Agent.EBPF.IsPacketTranslationEnabled() { - bpfApp.Spec.Programs = append(bpfApp.Spec.Programs, []bpfmaniov1alpha1.ClBpfApplicationProgram{ - { - Name: "track_nat_manip_pkt", - Type: bpfmaniov1alpha1.ProgTypeKprobe, - KProbe: &bpfmaniov1alpha1.ClKprobeProgramInfo{ - Links: []bpfmaniov1alpha1.ClKprobeAttachInfo{ - { - Function: "nf_nat_manip_pkt", - }, - }, - }, - }, - }...) + addPacketTranslationPrograms(bpfApp) } if fc.Spec.Agent.EBPF.IsIPSecEnabled() { - bpfApp.Spec.Programs = append(bpfApp.Spec.Programs, []bpfmaniov1alpha1.ClBpfApplicationProgram{ - { - Name: "xfrm_input_kprobe", - Type: bpfmaniov1alpha1.ProgTypeKprobe, - KProbe: &bpfmaniov1alpha1.ClKprobeProgramInfo{ - Links: []bpfmaniov1alpha1.ClKprobeAttachInfo{ - { - Function: "xfrm_input", - }, + addIPSecPrograms(bpfApp) + } + + if fc.Spec.Agent.EBPF.IsOpenSSLTrackingEnabled() { + addOpenSSLPrograms(bpfApp, openSSLPath) + } +} + +func addRTTPrograms(bpfApp *bpfmaniov1alpha1.ClusterBpfApplication) { + bpfApp.Spec.Programs = append(bpfApp.Spec.Programs, bpfmaniov1alpha1.ClBpfApplicationProgram{ + Name: "tcp_rcv_fentry", + Type: bpfmaniov1alpha1.ProgTypeFentry, + FEntry: &bpfmaniov1alpha1.ClFentryProgramInfo{ + ClFentryLoadInfo: bpfmaniov1alpha1.ClFentryLoadInfo{ + Function: "tcp_rcv_established", + }, + Links: []bpfmaniov1alpha1.ClFentryAttachInfo{ + { + Mode: bpfmaniov1alpha1.Attach, + }, + }, + }, + }) +} + +func addNetworkEventsPrograms(bpfApp *bpfmaniov1alpha1.ClusterBpfApplication) { + bpfApp.Spec.Programs = append(bpfApp.Spec.Programs, bpfmaniov1alpha1.ClBpfApplicationProgram{ + Name: "network_events_monitoring", + Type: bpfmaniov1alpha1.ProgTypeKprobe, + KProbe: &bpfmaniov1alpha1.ClKprobeProgramInfo{ + Links: []bpfmaniov1alpha1.ClKprobeAttachInfo{ + { + Function: "psample_sample_packet", + }, + }, + }, + }) +} + +func addPktDropPrograms(bpfApp *bpfmaniov1alpha1.ClusterBpfApplication) { + bpfApp.Spec.Programs = append(bpfApp.Spec.Programs, bpfmaniov1alpha1.ClBpfApplicationProgram{ + Name: "kfree_skb", + Type: bpfmaniov1alpha1.ProgTypeTracepoint, + TracePoint: &bpfmaniov1alpha1.ClTracepointProgramInfo{ + Links: []bpfmaniov1alpha1.ClTracepointAttachInfo{ + { + Name: "skb/kfree_skb", + }, + }, + }, + }) +} + +func addPacketTranslationPrograms(bpfApp *bpfmaniov1alpha1.ClusterBpfApplication) { + bpfApp.Spec.Programs = append(bpfApp.Spec.Programs, bpfmaniov1alpha1.ClBpfApplicationProgram{ + Name: "track_nat_manip_pkt", + Type: bpfmaniov1alpha1.ProgTypeKprobe, + KProbe: &bpfmaniov1alpha1.ClKprobeProgramInfo{ + Links: []bpfmaniov1alpha1.ClKprobeAttachInfo{ + { + Function: "nf_nat_manip_pkt", + }, + }, + }, + }) +} + +func addIPSecPrograms(bpfApp *bpfmaniov1alpha1.ClusterBpfApplication) { + bpfApp.Spec.Programs = append(bpfApp.Spec.Programs, + bpfmaniov1alpha1.ClBpfApplicationProgram{ + Name: "xfrm_input_kprobe", + Type: bpfmaniov1alpha1.ProgTypeKprobe, + KProbe: &bpfmaniov1alpha1.ClKprobeProgramInfo{ + Links: []bpfmaniov1alpha1.ClKprobeAttachInfo{ + { + Function: "xfrm_input", }, }, }, - }...) - bpfApp.Spec.Programs = append(bpfApp.Spec.Programs, []bpfmaniov1alpha1.ClBpfApplicationProgram{ - { - Name: "xfrm_input_kretprobe", - Type: bpfmaniov1alpha1.ProgTypeKretprobe, - KRetProbe: &bpfmaniov1alpha1.ClKretprobeProgramInfo{ - Links: []bpfmaniov1alpha1.ClKretprobeAttachInfo{ - { - Function: "xfrm_input", - }, + }, + bpfmaniov1alpha1.ClBpfApplicationProgram{ + Name: "xfrm_input_kretprobe", + Type: bpfmaniov1alpha1.ProgTypeKretprobe, + KRetProbe: &bpfmaniov1alpha1.ClKretprobeProgramInfo{ + Links: []bpfmaniov1alpha1.ClKretprobeAttachInfo{ + { + Function: "xfrm_input", }, }, }, - }...) - bpfApp.Spec.Programs = append(bpfApp.Spec.Programs, []bpfmaniov1alpha1.ClBpfApplicationProgram{ - { - Name: "xfrm_output_kprobe", - Type: bpfmaniov1alpha1.ProgTypeKprobe, - KProbe: &bpfmaniov1alpha1.ClKprobeProgramInfo{ - Links: []bpfmaniov1alpha1.ClKprobeAttachInfo{ - { - Function: "xfrm_output", - }, + }, + bpfmaniov1alpha1.ClBpfApplicationProgram{ + Name: "xfrm_output_kprobe", + Type: bpfmaniov1alpha1.ProgTypeKprobe, + KProbe: &bpfmaniov1alpha1.ClKprobeProgramInfo{ + Links: []bpfmaniov1alpha1.ClKprobeAttachInfo{ + { + Function: "xfrm_output", }, }, }, - }...) - bpfApp.Spec.Programs = append(bpfApp.Spec.Programs, []bpfmaniov1alpha1.ClBpfApplicationProgram{ - { - Name: "xfrm_output_kretprobe", - Type: bpfmaniov1alpha1.ProgTypeKretprobe, - KRetProbe: &bpfmaniov1alpha1.ClKretprobeProgramInfo{ - Links: []bpfmaniov1alpha1.ClKretprobeAttachInfo{ - { - Function: "xfrm_output", - }, + }, + bpfmaniov1alpha1.ClBpfApplicationProgram{ + Name: "xfrm_output_kretprobe", + Type: bpfmaniov1alpha1.ProgTypeKretprobe, + KRetProbe: &bpfmaniov1alpha1.ClKretprobeProgramInfo{ + Links: []bpfmaniov1alpha1.ClKretprobeAttachInfo{ + { + Function: "xfrm_output", }, }, }, - }...) - } + }, + ) +} + +func addOpenSSLPrograms(bpfApp *bpfmaniov1alpha1.ClusterBpfApplication, openSSLPath string) { + bpfApp.Spec.Programs = append(bpfApp.Spec.Programs, bpfmaniov1alpha1.ClBpfApplicationProgram{ + Name: "probe_entry_SSL_write", + Type: bpfmaniov1alpha1.ProgTypeUprobe, + UProbe: &bpfmaniov1alpha1.ClUprobeProgramInfo{ + Links: []bpfmaniov1alpha1.ClUprobeAttachInfo{ + { + Target: openSSLPath, + Function: "SSL_write", + }, + }, + }, + }) } func (c *AgentController) createBpfApplication(ctx context.Context, bpfApp *bpfmaniov1alpha1.ClusterBpfApplication) error {