diff --git a/.github/workflows/component-tests.yaml b/.github/workflows/component-tests.yaml index e0947817..1ba77169 100644 --- a/.github/workflows/component-tests.yaml +++ b/.github/workflows/component-tests.yaml @@ -49,6 +49,7 @@ jobs: Test_07_RuleBindingApplyTest, Test_08_ApplicationProfilePatching, Test_10_MalwareDetectionTest, + Test_11_EndpointTest, # Test_10_DemoTest # Test_11_DuplicationTest ] diff --git a/main.go b/main.go index 4a847c88..f34c487a 100644 --- a/main.go +++ b/main.go @@ -10,7 +10,6 @@ import ( "strings" "syscall" - "github.com/kubescape/node-agent/internal/validator" "github.com/kubescape/node-agent/pkg/applicationprofilemanager" applicationprofilemanagerv1 "github.com/kubescape/node-agent/pkg/applicationprofilemanager/v1" "github.com/kubescape/node-agent/pkg/config" @@ -44,6 +43,7 @@ import ( seccompmanagerv1 "github.com/kubescape/node-agent/pkg/seccompmanager/v1" "github.com/kubescape/node-agent/pkg/storage/v1" "github.com/kubescape/node-agent/pkg/utils" + "github.com/kubescape/node-agent/pkg/validator" "github.com/kubescape/node-agent/pkg/watcher/dynamicwatcher" "github.com/kubescape/node-agent/pkg/watcher/seccompprofilewatcher" @@ -269,7 +269,7 @@ func main() { } // Create the container handler - mainHandler, err := containerwatcher.CreateIGContainerWatcher(cfg, applicationProfileManager, k8sClient, relevancyManager, networkManagerClient, dnsManagerClient, prometheusExporter, ruleManager, malwareManager, preRunningContainersIDs, &ruleBindingNotify, containerRuntime) + mainHandler, err := containerwatcher.CreateIGContainerWatcher(cfg, applicationProfileManager, k8sClient, relevancyManager, networkManagerClient, dnsManagerClient, prometheusExporter, ruleManager, malwareManager, preRunningContainersIDs, &ruleBindingNotify, containerRuntime, nil) if err != nil { logger.L().Ctx(ctx).Fatal("error creating the container watcher", helpers.Error(err)) } diff --git a/pkg/containerwatcher/container_watcher_interface.go b/pkg/containerwatcher/container_watcher_interface.go index c63b5754..bc9ce8e5 100644 --- a/pkg/containerwatcher/container_watcher_interface.go +++ b/pkg/containerwatcher/container_watcher_interface.go @@ -2,10 +2,37 @@ package containerwatcher import ( "context" + + containercollection "github.com/inspektor-gadget/inspektor-gadget/pkg/container-collection" + "github.com/inspektor-gadget/inspektor-gadget/pkg/socketenricher" + tracercollection "github.com/inspektor-gadget/inspektor-gadget/pkg/tracer-collection" + "github.com/kubescape/node-agent/pkg/utils" ) type ContainerWatcher interface { Ready() bool Start(ctx context.Context) error Stop() + GetTracerCollection() *tracercollection.TracerCollection + GetContainerCollection() *containercollection.ContainerCollection + GetSocketEnricher() *socketenricher.SocketEnricher + GetContainerSelector() *containercollection.ContainerSelector + RegisterCustomTracer(tracer CustomTracer) error + UnregisterCustomTracer(tracer CustomTracer) error + RegisterContainerReceiver(receiver ContainerReceiver) + UnregisterContainerReceiver(receiver ContainerReceiver) +} + +type CustomTracer interface { + Start() error + Stop() error + Name() string +} + +type EventReceiver interface { + ReportEvent(eventType utils.EventType, event utils.K8sEvent) +} + +type ContainerReceiver interface { + ContainerCallback(notif containercollection.PubSubEvent) } diff --git a/pkg/containerwatcher/container_watcher_mock.go b/pkg/containerwatcher/container_watcher_mock.go index c043231d..66989e11 100644 --- a/pkg/containerwatcher/container_watcher_mock.go +++ b/pkg/containerwatcher/container_watcher_mock.go @@ -2,6 +2,10 @@ package containerwatcher import ( "context" + + containercollection "github.com/inspektor-gadget/inspektor-gadget/pkg/container-collection" + "github.com/inspektor-gadget/inspektor-gadget/pkg/socketenricher" + tracercollection "github.com/inspektor-gadget/inspektor-gadget/pkg/tracer-collection" ) type ContainerWatcherMock struct{} @@ -16,4 +20,48 @@ func (c ContainerWatcherMock) Start(_ context.Context) error { func (c ContainerWatcherMock) Stop() {} +func (c ContainerWatcherMock) RegisterCustomTracer(_ CustomTracer) error { + return nil +} + +func (c ContainerWatcherMock) UnregisterCustomTracer(_ CustomTracer) error { + return nil +} + +func (c ContainerWatcherMock) RegisterContainerReceiver(_ ContainerReceiver) {} + +func (c ContainerWatcherMock) UnregisterContainerReceiver(_ ContainerReceiver) {} + +func (c ContainerWatcherMock) GetTracerCollection() *tracercollection.TracerCollection { + return nil +} + +func (c ContainerWatcherMock) GetContainerCollection() *containercollection.ContainerCollection { + return nil +} + +func (c ContainerWatcherMock) GetSocketEnricher() *socketenricher.SocketEnricher { + return nil +} + +func (c ContainerWatcherMock) GetContainerSelector() *containercollection.ContainerSelector { + return nil +} + var _ ContainerWatcher = (*ContainerWatcherMock)(nil) + +type CustomTracerMock struct{} + +func (c CustomTracerMock) Start() error { + return nil +} + +func (c CustomTracerMock) Stop() error { + return nil +} + +func (c CustomTracerMock) Name() string { + return "" +} + +var _ CustomTracer = (*CustomTracerMock)(nil) diff --git a/pkg/containerwatcher/v1/container_watcher.go b/pkg/containerwatcher/v1/container_watcher.go index ec6fce73..a09abd2b 100644 --- a/pkg/containerwatcher/v1/container_watcher.go +++ b/pkg/containerwatcher/v1/container_watcher.go @@ -6,6 +6,7 @@ import ( "os" mapset "github.com/deckarep/golang-set/v2" + "github.com/goradd/maps" containercollection "github.com/inspektor-gadget/inspektor-gadget/pkg/container-collection" containerutilsTypes "github.com/inspektor-gadget/inspektor-gadget/pkg/container-utils/types" tracerseccomp "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/advise/seccomp/tracer" @@ -108,6 +109,10 @@ type IGContainerWatcher struct { httpTracer *tracerhttp.Tracer kubeIPInstance operators.OperatorInstance kubeNameInstance operators.OperatorInstance + // Third party tracers + thirdPartyTracers mapset.Set[containerwatcher.CustomTracer] + // Third party container receivers + thirdPartyContainerReceivers mapset.Set[containerwatcher.ContainerReceiver] // Worker pools capabilitiesWorkerPool *ants.PoolWithFunc @@ -144,7 +149,7 @@ type IGContainerWatcher struct { var _ containerwatcher.ContainerWatcher = (*IGContainerWatcher)(nil) -func CreateIGContainerWatcher(cfg config.Config, applicationProfileManager applicationprofilemanager.ApplicationProfileManagerClient, k8sClient *k8sinterface.KubernetesApi, relevancyManager relevancymanager.RelevancyManagerClient, networkManagerClient networkmanager.NetworkManagerClient, dnsManagerClient dnsmanager.DNSManagerClient, metrics metricsmanager.MetricsManager, ruleManager rulemanager.RuleManagerClient, malwareManager malwaremanager.MalwareManagerClient, preRunningContainers mapset.Set[string], ruleBindingPodNotify *chan rulebinding.RuleBindingNotify, runtime *containerutilsTypes.RuntimeConfig) (*IGContainerWatcher, error) { +func CreateIGContainerWatcher(cfg config.Config, applicationProfileManager applicationprofilemanager.ApplicationProfileManagerClient, k8sClient *k8sinterface.KubernetesApi, relevancyManager relevancymanager.RelevancyManagerClient, networkManagerClient networkmanager.NetworkManagerClient, dnsManagerClient dnsmanager.DNSManagerClient, metrics metricsmanager.MetricsManager, ruleManager rulemanager.RuleManagerClient, malwareManager malwaremanager.MalwareManagerClient, preRunningContainers mapset.Set[string], ruleBindingPodNotify *chan rulebinding.RuleBindingNotify, runtime *containerutilsTypes.RuntimeConfig, thirdPartyEventReceivers *maps.SafeMap[utils.EventType, mapset.Set[containerwatcher.EventReceiver]]) (*IGContainerWatcher, error) { // Use container collection to get notified for new containers containerCollection := &containercollection.ContainerCollection{} // Create a tracer collection instance @@ -162,7 +167,10 @@ func CreateIGContainerWatcher(cfg config.Config, applicationProfileManager appli metrics.ReportEvent(utils.CapabilitiesEventType) k8sContainerID := utils.CreateK8sContainerID(event.K8s.Namespace, event.K8s.PodName, event.K8s.ContainerName) applicationProfileManager.ReportCapability(k8sContainerID, event.CapName) - ruleManager.ReportCapability(event) + ruleManager.ReportEvent(utils.CapabilitiesEventType, &event) + + // Report capabilities to event receivers + reportEventToThirdPartyTracers(utils.CapabilitiesEventType, &event, thirdPartyEventReceivers) }) if err != nil { return nil, fmt.Errorf("creating capabilities worker pool: %w", err) @@ -189,8 +197,11 @@ func CreateIGContainerWatcher(cfg config.Config, applicationProfileManager appli metrics.ReportEvent(utils.ExecveEventType) applicationProfileManager.ReportFileExec(k8sContainerID, path, event.Args) relevancyManager.ReportFileExec(event.Runtime.ContainerID, k8sContainerID, path) - ruleManager.ReportFileExec(event) - malwareManager.ReportFileExec(k8sContainerID, event) + ruleManager.ReportEvent(utils.ExecveEventType, &event) + malwareManager.ReportEvent(utils.ExecveEventType, &event) + + // Report exec events to event receivers + reportEventToThirdPartyTracers(utils.ExecveEventType, &event, thirdPartyEventReceivers) }) if err != nil { return nil, fmt.Errorf("creating exec worker pool: %w", err) @@ -217,8 +228,11 @@ func CreateIGContainerWatcher(cfg config.Config, applicationProfileManager appli metrics.ReportEvent(utils.OpenEventType) applicationProfileManager.ReportFileOpen(k8sContainerID, path, event.Flags) relevancyManager.ReportFileOpen(event.Runtime.ContainerID, k8sContainerID, path) - ruleManager.ReportFileOpen(event) - malwareManager.ReportFileOpen(k8sContainerID, event) + ruleManager.ReportEvent(utils.OpenEventType, &event) + malwareManager.ReportEvent(utils.OpenEventType, &event) + + // Report open events to event receivers + reportEventToThirdPartyTracers(utils.OpenEventType, &event, thirdPartyEventReceivers) }) if err != nil { return nil, fmt.Errorf("creating open worker pool: %w", err) @@ -238,7 +252,10 @@ func CreateIGContainerWatcher(cfg config.Config, applicationProfileManager appli } metrics.ReportEvent(utils.NetworkEventType) networkManagerClient.ReportNetworkEvent(k8sContainerID, event) - ruleManager.ReportNetworkEvent(event) + ruleManager.ReportEvent(utils.NetworkEventType, &event) + + // Report network events to event receivers + reportEventToThirdPartyTracers(utils.NetworkEventType, &event, thirdPartyEventReceivers) }) if err != nil { return nil, fmt.Errorf("creating network worker pool: %w", err) @@ -263,7 +280,10 @@ func CreateIGContainerWatcher(cfg config.Config, applicationProfileManager appli metrics.ReportEvent(utils.DnsEventType) dnsManagerClient.ReportDNSEvent(event) - ruleManager.ReportDNSEvent(event) + ruleManager.ReportEvent(utils.DnsEventType, &event) + + // Report DNS events to event receivers + reportEventToThirdPartyTracers(utils.DnsEventType, &event, thirdPartyEventReceivers) }) if err != nil { return nil, fmt.Errorf("creating dns worker pool: %w", err) @@ -275,7 +295,10 @@ func CreateIGContainerWatcher(cfg config.Config, applicationProfileManager appli return } metrics.ReportEvent(utils.RandomXEventType) - ruleManager.ReportRandomxEvent(event) + ruleManager.ReportEvent(utils.RandomXEventType, &event) + + // Report randomx events to event receivers + reportEventToThirdPartyTracers(utils.RandomXEventType, &event, thirdPartyEventReceivers) }) if err != nil { return nil, fmt.Errorf("creating randomx worker pool: %w", err) @@ -287,7 +310,10 @@ func CreateIGContainerWatcher(cfg config.Config, applicationProfileManager appli return } metrics.ReportEvent(utils.SymlinkEventType) - ruleManager.ReportSymlinkEvent(event) + ruleManager.ReportEvent(utils.SymlinkEventType, &event) + + // Report symlink events to event receivers + reportEventToThirdPartyTracers(utils.SymlinkEventType, &event, thirdPartyEventReceivers) }) if err != nil { return nil, fmt.Errorf("creating symlink worker pool: %w", err) @@ -299,7 +325,10 @@ func CreateIGContainerWatcher(cfg config.Config, applicationProfileManager appli return } metrics.ReportEvent(utils.HardlinkEventType) - ruleManager.ReportHardlinkEvent(event) + ruleManager.ReportEvent(utils.HardlinkEventType, &event) + + // Report hardlink events to event receivers + reportEventToThirdPartyTracers(utils.HardlinkEventType, &event, thirdPartyEventReceivers) }) if err != nil { return nil, fmt.Errorf("creating hardlink worker pool: %w", err) @@ -311,7 +340,10 @@ func CreateIGContainerWatcher(cfg config.Config, applicationProfileManager appli return } metrics.ReportEvent(utils.SSHEventType) - ruleManager.ReportSSHEvent(event) + ruleManager.ReportEvent(utils.SSHEventType, &event) + + // Report ssh events to event receivers + reportEventToThirdPartyTracers(utils.SSHEventType, &event, thirdPartyEventReceivers) }) if err != nil { return nil, fmt.Errorf("creating ssh worker pool: %w", err) @@ -334,6 +366,8 @@ func CreateIGContainerWatcher(cfg config.Config, applicationProfileManager appli metrics.ReportEvent(utils.HTTPEventType) applicationProfileManager.ReportHTTPEvent(k8sContainerID, &event) + + reportEventToThirdPartyTracers(utils.HTTPEventType, &event, thirdPartyEventReceivers) }) if err != nil { @@ -386,13 +420,55 @@ func CreateIGContainerWatcher(cfg config.Config, applicationProfileManager appli httpWorkerChan: make(chan *tracerhttptype.Event, 500000), // cache - ruleBindingPodNotify: ruleBindingPodNotify, - timeBasedContainers: mapset.NewSet[string](), - ruleManagedPods: mapset.NewSet[string](), - runtime: runtime, + ruleBindingPodNotify: ruleBindingPodNotify, + timeBasedContainers: mapset.NewSet[string](), + ruleManagedPods: mapset.NewSet[string](), + runtime: runtime, + thirdPartyTracers: mapset.NewSet[containerwatcher.CustomTracer](), + thirdPartyContainerReceivers: mapset.NewSet[containerwatcher.ContainerReceiver](), }, nil } +func (ch *IGContainerWatcher) GetContainerCollection() *containercollection.ContainerCollection { + return ch.containerCollection +} + +func (ch *IGContainerWatcher) GetTracerCollection() *tracercollection.TracerCollection { + return ch.tracerCollection +} + +func (ch *IGContainerWatcher) GetSocketEnricher() *socketenricher.SocketEnricher { + return ch.socketEnricher +} + +func (ch *IGContainerWatcher) GetContainerSelector() *containercollection.ContainerSelector { + return &ch.containerSelector +} + +func (ch *IGContainerWatcher) RegisterCustomTracer(tracer containerwatcher.CustomTracer) error { + for t := range ch.thirdPartyTracers.Iter() { + if t.Name() == tracer.Name() { + return fmt.Errorf("tracer with name %s already registered", tracer.Name()) + } + } + + ch.thirdPartyTracers.Add(tracer) + return nil +} + +func (ch *IGContainerWatcher) UnregisterCustomTracer(tracer containerwatcher.CustomTracer) error { + ch.thirdPartyTracers.Remove(tracer) + return nil +} + +func (ch *IGContainerWatcher) RegisterContainerReceiver(receiver containerwatcher.ContainerReceiver) { + ch.thirdPartyContainerReceivers.Add(receiver) +} + +func (ch *IGContainerWatcher) UnregisterContainerReceiver(receiver containerwatcher.ContainerReceiver) { + ch.thirdPartyContainerReceivers.Remove(receiver) +} + func (ch *IGContainerWatcher) Start(ctx context.Context) error { if !ch.running { @@ -425,3 +501,11 @@ func (ch *IGContainerWatcher) Stop() { func (ch *IGContainerWatcher) Ready() bool { return ch.running } + +func reportEventToThirdPartyTracers(eventType utils.EventType, event utils.K8sEvent, thirdPartyEventReceivers *maps.SafeMap[utils.EventType, mapset.Set[containerwatcher.EventReceiver]]) { + if thirdPartyEventReceivers != nil && thirdPartyEventReceivers.Has(eventType) { + for receiver := range thirdPartyEventReceivers.Get(eventType).Iter() { + receiver.ReportEvent(eventType, event) + } + } +} diff --git a/pkg/containerwatcher/v1/container_watcher_private.go b/pkg/containerwatcher/v1/container_watcher_private.go index 71c0ae22..c2e948bd 100644 --- a/pkg/containerwatcher/v1/container_watcher_private.go +++ b/pkg/containerwatcher/v1/container_watcher_private.go @@ -89,6 +89,10 @@ func (ch *IGContainerWatcher) startContainerCollection(ctx context.Context) erro ch.ruleManager.ContainerCallback, } + for receiver := range ch.thirdPartyContainerReceivers.Iter() { + containerEventFuncs = append(containerEventFuncs, receiver.ContainerCallback) + } + // Define the different options for the container collection instance opts := []containercollection.ContainerCollectionOption{ // Get Notifications from the container collection @@ -267,6 +271,14 @@ func (ch *IGContainerWatcher) startTracers() error { logger.L().Error("error starting ssh tracing", helpers.Error(err)) return err } + + // Start third party tracers + for tracer := range ch.thirdPartyTracers.Iter() { + if err := tracer.Start(); err != nil { + logger.L().Error("error starting custom tracer", helpers.String("tracer", tracer.Name()), helpers.Error(err)) + return err + } + } } if ch.cfg.EnableHttpDetection { @@ -346,6 +358,14 @@ func (ch *IGContainerWatcher) stopTracers() error { logger.L().Error("error stopping ssh tracing", helpers.Error(err)) errs = errors.Join(errs, err) } + + // Stop third party tracers + for tracer := range ch.thirdPartyTracers.Iter() { + if err := tracer.Stop(); err != nil { + logger.L().Error("error stopping custom tracer", helpers.String("tracer", tracer.Name()), helpers.Error(err)) + errs = errors.Join(errs, err) + } + } } if ch.cfg.EnableHttpDetection { diff --git a/pkg/containerwatcher/v1/open_test.go b/pkg/containerwatcher/v1/open_test.go index 65d05237..7c91dd6f 100644 --- a/pkg/containerwatcher/v1/open_test.go +++ b/pkg/containerwatcher/v1/open_test.go @@ -23,7 +23,7 @@ func BenchmarkIGContainerWatcher_openEventCallback(b *testing.B) { assert.NoError(b, err) mockExporter := metricsmanager.NewMetricsMock() - mainHandler, err := CreateIGContainerWatcher(cfg, nil, nil, relevancyManager, nil, nil, mockExporter, nil, nil, nil, nil, nil) + mainHandler, err := CreateIGContainerWatcher(cfg, nil, nil, relevancyManager, nil, nil, mockExporter, nil, nil, nil, nil, nil, nil) assert.NoError(b, err) event := &traceropentype.Event{ Event: types.Event{ diff --git a/pkg/malwaremanager/malware_manager_interface.go b/pkg/malwaremanager/malware_manager_interface.go index 40653c93..00ce1884 100644 --- a/pkg/malwaremanager/malware_manager_interface.go +++ b/pkg/malwaremanager/malware_manager_interface.go @@ -5,14 +5,11 @@ import ( apitypes "github.com/armosec/armoapi-go/armotypes" containercollection "github.com/inspektor-gadget/inspektor-gadget/pkg/container-collection" - tracerexectype "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/trace/exec/types" - traceropentype "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/trace/open/types" igtypes "github.com/inspektor-gadget/inspektor-gadget/pkg/types" ) type MalwareManagerClient interface { - ReportFileExec(k8sContainerID string, event tracerexectype.Event) - ReportFileOpen(k8sContainerID string, event traceropentype.Event) + ReportEvent(eventType utils.EventType, event utils.K8sEvent) ContainerCallback(notif containercollection.PubSubEvent) } @@ -44,5 +41,5 @@ type MalwareResult interface { type MalwareScanner interface { // Scan scans the event for malware. - Scan(eventType utils.EventType, event interface{}, containerPid uint32) MalwareResult + Scan(eventType utils.EventType, event utils.K8sEvent, containerPid uint32) MalwareResult } diff --git a/pkg/malwaremanager/malwaremanager_mock.go b/pkg/malwaremanager/malwaremanager_mock.go index ccb6fac4..e0a3f3c9 100644 --- a/pkg/malwaremanager/malwaremanager_mock.go +++ b/pkg/malwaremanager/malwaremanager_mock.go @@ -2,8 +2,7 @@ package malwaremanager import ( containercollection "github.com/inspektor-gadget/inspektor-gadget/pkg/container-collection" - tracerexectype "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/trace/exec/types" - traceropentype "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/trace/open/types" + "github.com/kubescape/node-agent/pkg/utils" ) type MalwareManagerMock struct { @@ -15,11 +14,7 @@ func CreateMalwareManagerMock() *MalwareManagerMock { return &MalwareManagerMock{} } -func (r MalwareManagerMock) ReportFileExec(_ string, _ tracerexectype.Event) { - // noop -} - -func (r MalwareManagerMock) ReportFileOpen(_ string, _ traceropentype.Event) { +func (r MalwareManagerMock) ReportEvent(_ utils.EventType, _ utils.K8sEvent) { // noop } diff --git a/pkg/malwaremanager/v1/clamav/clamav.go b/pkg/malwaremanager/v1/clamav/clamav.go index 1a12b9da..10c893f6 100644 --- a/pkg/malwaremanager/v1/clamav/clamav.go +++ b/pkg/malwaremanager/v1/clamav/clamav.go @@ -8,6 +8,7 @@ import ( "github.com/kubescape/go-logger" "github.com/kubescape/go-logger/helpers" "github.com/kubescape/node-agent/pkg/malwaremanager" + "github.com/kubescape/node-agent/pkg/utils" nautils "github.com/kubescape/node-agent/pkg/utils" ) @@ -39,7 +40,7 @@ func CreateClamAVClient(clamavSocket string) (*ClamAVClient, error) { return &clamavClient, nil } -func (c *ClamAVClient) Scan(eventType nautils.EventType, event interface{}, containerPid uint32) malwaremanager.MalwareResult { +func (c *ClamAVClient) Scan(eventType nautils.EventType, event utils.K8sEvent, containerPid uint32) malwaremanager.MalwareResult { // Check if the event is of type tracerexectype.Event or traceropentype.Event. switch eventType { case nautils.ExecveEventType: diff --git a/pkg/malwaremanager/v1/malware_manager.go b/pkg/malwaremanager/v1/malware_manager.go index e7a0ec1d..aee48edf 100644 --- a/pkg/malwaremanager/v1/malware_manager.go +++ b/pkg/malwaremanager/v1/malware_manager.go @@ -143,9 +143,28 @@ func (mm *MalwareManager) getWorkloadIdentifier(podNamespace, podName string) (s return generatedWlid, nil } -func (mm *MalwareManager) ReportFileExec(_ string, event tracerexectype.Event) { +func (mm *MalwareManager) ReportEvent(eventType utils.EventType, event utils.K8sEvent) { + switch eventType { + case utils.ExecveEventType: + exec, ok := event.(*tracerexectype.Event) + if !ok { + logger.L().Error("MalwareManager - failed to cast event to execve event") + return + } + mm.reportFileExec(exec) + case utils.OpenEventType: + open, ok := event.(*traceropentype.Event) + if !ok { + logger.L().Error("MalwareManager - failed to cast event to open event") + return + } + mm.reportFileOpen(open) + } +} + +func (mm *MalwareManager) reportFileExec(event *tracerexectype.Event) { for _, scanner := range mm.malwareScanners { - if result := scanner.Scan(utils.ExecveEventType, &event, mm.containerIdToPid.Get(event.Runtime.ContainerID)); result != nil { + if result := scanner.Scan(utils.ExecveEventType, event, mm.containerIdToPid.Get(event.Runtime.ContainerID)); result != nil { result = mm.enrichMalwareResult(result) result.SetWorkloadDetails(mm.podToWlid.Get(utils.CreateK8sPodID(event.GetNamespace(), event.GetPod()))) mm.exporter.SendMalwareAlert(result) @@ -153,7 +172,7 @@ func (mm *MalwareManager) ReportFileExec(_ string, event tracerexectype.Event) { } if mm.scannedFiles.Has(event.Runtime.ContainerID) && mm.scannedFiles.Get(event.Runtime.ContainerID).Cardinality() <= ScannedFilesMaxBufferLength { - hostFilePath, err := utils.GetHostFilePathFromEvent(&event, mm.containerIdToPid.Get(event.Runtime.ContainerID)) + hostFilePath, err := utils.GetHostFilePathFromEvent(event, mm.containerIdToPid.Get(event.Runtime.ContainerID)) if err != nil { return } @@ -162,7 +181,7 @@ func (mm *MalwareManager) ReportFileExec(_ string, event tracerexectype.Event) { } } -func (mm *MalwareManager) ReportFileOpen(_ string, event traceropentype.Event) { +func (mm *MalwareManager) reportFileOpen(event *traceropentype.Event) { // TODO: Add a check if the file is being opened for read. // Skip directories. @@ -170,7 +189,7 @@ func (mm *MalwareManager) ReportFileOpen(_ string, event traceropentype.Event) { return } - hostFilePath, err := utils.GetHostFilePathFromEvent(&event, mm.containerIdToPid.Get(event.Runtime.ContainerID)) + hostFilePath, err := utils.GetHostFilePathFromEvent(event, mm.containerIdToPid.Get(event.Runtime.ContainerID)) if err != nil { return } @@ -189,7 +208,7 @@ func (mm *MalwareManager) ReportFileOpen(_ string, event traceropentype.Event) { } for _, scanner := range mm.malwareScanners { - if result := scanner.Scan(utils.OpenEventType, &event, mm.containerIdToPid.Get(event.Runtime.ContainerID)); result != nil { + if result := scanner.Scan(utils.OpenEventType, event, mm.containerIdToPid.Get(event.Runtime.ContainerID)); result != nil { result = mm.enrichMalwareResult(result) result.SetWorkloadDetails(mm.podToWlid.Get(utils.CreateK8sPodID(event.GetNamespace(), event.GetPod()))) mm.exporter.SendMalwareAlert(result) diff --git a/pkg/rulebindingmanager/cache/cache.go b/pkg/rulebindingmanager/cache/cache.go index 2db33d19..4efe72fd 100644 --- a/pkg/rulebindingmanager/cache/cache.go +++ b/pkg/rulebindingmanager/cache/cache.go @@ -379,6 +379,11 @@ func (c *RBCache) createRule(r *typesv1.RuntimeAlertRuleBindingRule) []ruleengin return []ruleengine.RuleEvaluator{} } +// Expose the rule creator to be able to create rules from third party. +func (c *RBCache) GetRuleCreator() ruleengine.RuleCreator { + return c.ruleCreator +} + func diff(a, b []rulebindingmanager.RuleBindingNotify) []rulebindingmanager.RuleBindingNotify { m := make(map[string]rulebindingmanager.RuleBindingNotify) diff := make([]rulebindingmanager.RuleBindingNotify, 0) diff --git a/pkg/ruleengine/ruleengine_interface.go b/pkg/ruleengine/ruleengine_interface.go index 3756c183..d512670f 100644 --- a/pkg/ruleengine/ruleengine_interface.go +++ b/pkg/ruleengine/ruleengine_interface.go @@ -17,11 +17,40 @@ const ( RulePrioritySystemIssue = 1000 ) +type RuleDescriptor struct { + // Rule ID + ID string + // Rule Name + Name string + // Rule Description + Description string + // Priority + Priority int + // Tags + Tags []string + // Rule requirements + Requirements RuleSpec + // Create a rule function + RuleCreationFunc func() RuleEvaluator +} + +func (r *RuleDescriptor) HasTags(tags []string) bool { + for _, tag := range tags { + for _, ruleTag := range r.Tags { + if tag == ruleTag { + return true + } + } + } + return false +} + // RuleCreator is an interface for creating rules by tags, IDs, and names type RuleCreator interface { CreateRulesByTags(tags []string) []RuleEvaluator CreateRuleByID(id string) RuleEvaluator CreateRuleByName(name string) RuleEvaluator + RegisterRule(rule RuleDescriptor) } type RuleEvaluator interface { @@ -32,7 +61,7 @@ type RuleEvaluator interface { Name() string // Rule processing - ProcessEvent(eventType utils.EventType, event interface{}, objCache objectcache.ObjectCache) RuleFailure + ProcessEvent(eventType utils.EventType, event utils.K8sEvent, objCache objectcache.ObjectCache) RuleFailure // Rule requirements Requirements() RuleSpec diff --git a/pkg/ruleengine/ruleengine_mock.go b/pkg/ruleengine/ruleengine_mock.go index 0f4894e1..40e46b89 100644 --- a/pkg/ruleengine/ruleengine_mock.go +++ b/pkg/ruleengine/ruleengine_mock.go @@ -25,6 +25,9 @@ func (r *RuleCreatorMock) CreateRuleByName(name string) RuleEvaluator { return &RuleMock{RuleName: name} } +func (r *RuleCreatorMock) RegisterRule(rule RuleDescriptor) { +} + var _ RuleEvaluator = (*RuleMock)(nil) type RuleMock struct { @@ -45,7 +48,7 @@ func (rule *RuleMock) ID() string { func (rule *RuleMock) DeleteRule() { } -func (rule *RuleMock) ProcessEvent(_ utils.EventType, _ interface{}, _ objectcache.ObjectCache) RuleFailure { +func (rule *RuleMock) ProcessEvent(_ utils.EventType, _ utils.K8sEvent, _ objectcache.ObjectCache) RuleFailure { return nil } diff --git a/pkg/ruleengine/v1/factory.go b/pkg/ruleengine/v1/factory.go index df3c0d01..bde9333c 100644 --- a/pkg/ruleengine/v1/factory.go +++ b/pkg/ruleengine/v1/factory.go @@ -5,12 +5,12 @@ import "github.com/kubescape/node-agent/pkg/ruleengine" var _ ruleengine.RuleCreator = (*RuleCreatorImpl)(nil) type RuleCreatorImpl struct { - ruleDescriptions []RuleDescriptor + ruleDescriptions []ruleengine.RuleDescriptor } func NewRuleCreator() *RuleCreatorImpl { return &RuleCreatorImpl{ - ruleDescriptions: []RuleDescriptor{ + ruleDescriptions: []ruleengine.RuleDescriptor{ R0001UnexpectedProcessLaunchedRuleDescriptor, R0002UnexpectedFileAccessRuleDescriptor, R0003UnexpectedSystemCallRuleDescriptor, @@ -67,6 +67,10 @@ func (r *RuleCreatorImpl) CreateRuleByName(name string) ruleengine.RuleEvaluator return nil } -func (r *RuleCreatorImpl) GetAllRuleDescriptors() []RuleDescriptor { +func (r *RuleCreatorImpl) GetAllRuleDescriptors() []ruleengine.RuleDescriptor { return r.ruleDescriptions } + +func (r *RuleCreatorImpl) RegisterRule(rule ruleengine.RuleDescriptor) { + r.ruleDescriptions = append(r.ruleDescriptions, rule) +} diff --git a/pkg/ruleengine/v1/r0001_unexpected_process_launched.go b/pkg/ruleengine/v1/r0001_unexpected_process_launched.go index fdd7ebc5..35342e2d 100644 --- a/pkg/ruleengine/v1/r0001_unexpected_process_launched.go +++ b/pkg/ruleengine/v1/r0001_unexpected_process_launched.go @@ -21,7 +21,7 @@ const ( R0001Name = "Unexpected process launched" ) -var R0001UnexpectedProcessLaunchedRuleDescriptor = RuleDescriptor{ +var R0001UnexpectedProcessLaunchedRuleDescriptor = ruleengine.RuleDescriptor{ ID: R0001ID, Name: R0001Name, Description: "Detecting exec calls that are not whitelisted by application profile", @@ -75,7 +75,7 @@ func (rule *R0001UnexpectedProcessLaunched) generatePatchCommand(event *tracerex event.GetContainer(), getExecPathFromEvent(event), argList) } -func (rule *R0001UnexpectedProcessLaunched) ProcessEvent(eventType utils.EventType, event interface{}, objectCache objectcache.ObjectCache) ruleengine.RuleFailure { +func (rule *R0001UnexpectedProcessLaunched) ProcessEvent(eventType utils.EventType, event utils.K8sEvent, objectCache objectcache.ObjectCache) ruleengine.RuleFailure { if eventType != utils.ExecveEventType { return nil } diff --git a/pkg/ruleengine/v1/r0002_unexpected_file_access.go b/pkg/ruleengine/v1/r0002_unexpected_file_access.go index 17321270..fd91852d 100644 --- a/pkg/ruleengine/v1/r0002_unexpected_file_access.go +++ b/pkg/ruleengine/v1/r0002_unexpected_file_access.go @@ -22,7 +22,7 @@ const ( R0002Name = "Unexpected file access" ) -var R0002UnexpectedFileAccessRuleDescriptor = RuleDescriptor{ +var R0002UnexpectedFileAccessRuleDescriptor = ruleengine.RuleDescriptor{ ID: R0002ID, Name: R0002Name, Description: "Detecting file access that are not whitelisted by application profile. File access is defined by the combination of path and flags", @@ -93,7 +93,7 @@ func (rule *R0002UnexpectedFileAccess) generatePatchCommand(event *traceropentyp return fmt.Sprintf(baseTemplate, ap.GetName(), ap.GetNamespace(), event.GetContainer(), event.FullPath, flagList) } -func (rule *R0002UnexpectedFileAccess) ProcessEvent(eventType utils.EventType, event interface{}, objCache objectcache.ObjectCache) ruleengine.RuleFailure { +func (rule *R0002UnexpectedFileAccess) ProcessEvent(eventType utils.EventType, event utils.K8sEvent, objCache objectcache.ObjectCache) ruleengine.RuleFailure { if eventType != utils.OpenEventType { return nil } diff --git a/pkg/ruleengine/v1/r0003_unexpected_system_call.go b/pkg/ruleengine/v1/r0003_unexpected_system_call.go index 22c2bce9..06c93cee 100644 --- a/pkg/ruleengine/v1/r0003_unexpected_system_call.go +++ b/pkg/ruleengine/v1/r0003_unexpected_system_call.go @@ -18,7 +18,7 @@ const ( R0003Name = "Unexpected system call" ) -var R0003UnexpectedSystemCallRuleDescriptor = RuleDescriptor{ +var R0003UnexpectedSystemCallRuleDescriptor = ruleengine.RuleDescriptor{ ID: R0003ID, Name: R0003Name, Description: "Detecting unexpected system calls that are not whitelisted by application profile.", @@ -58,7 +58,7 @@ func (rule *R0003UnexpectedSystemCall) ID() string { func (rule *R0003UnexpectedSystemCall) DeleteRule() { } -func (rule *R0003UnexpectedSystemCall) ProcessEvent(eventType utils.EventType, event interface{}, objCache objectcache.ObjectCache) ruleengine.RuleFailure { +func (rule *R0003UnexpectedSystemCall) ProcessEvent(eventType utils.EventType, event utils.K8sEvent, objCache objectcache.ObjectCache) ruleengine.RuleFailure { if eventType != utils.SyscallEventType { return nil } diff --git a/pkg/ruleengine/v1/r0004_unexpected_capability_used.go b/pkg/ruleengine/v1/r0004_unexpected_capability_used.go index dd77fca0..5e96899b 100644 --- a/pkg/ruleengine/v1/r0004_unexpected_capability_used.go +++ b/pkg/ruleengine/v1/r0004_unexpected_capability_used.go @@ -17,7 +17,7 @@ const ( R0004Name = "Unexpected capability used" ) -var R0004UnexpectedCapabilityUsedRuleDescriptor = RuleDescriptor{ +var R0004UnexpectedCapabilityUsedRuleDescriptor = ruleengine.RuleDescriptor{ ID: R0004ID, Name: R0004Name, Description: "Detecting unexpected capabilities that are not whitelisted by application profile. Every unexpected capability is identified in context of a syscall and will be alerted only once per container.", @@ -56,7 +56,7 @@ func (rule *R0004UnexpectedCapabilityUsed) generatePatchCommand(event *tracercap event.GetContainer(), event.Syscall, event.CapName) } -func (rule *R0004UnexpectedCapabilityUsed) ProcessEvent(eventType utils.EventType, event interface{}, objCache objectcache.ObjectCache) ruleengine.RuleFailure { +func (rule *R0004UnexpectedCapabilityUsed) ProcessEvent(eventType utils.EventType, event utils.K8sEvent, objCache objectcache.ObjectCache) ruleengine.RuleFailure { if eventType != utils.CapabilitiesEventType { return nil } diff --git a/pkg/ruleengine/v1/r0005_unexpected_domain_request.go b/pkg/ruleengine/v1/r0005_unexpected_domain_request.go index c5ab77b7..bc372fd7 100644 --- a/pkg/ruleengine/v1/r0005_unexpected_domain_request.go +++ b/pkg/ruleengine/v1/r0005_unexpected_domain_request.go @@ -20,7 +20,7 @@ const ( R0005Name = "Unexpected domain request" ) -var R0005UnexpectedDomainRequestRuleDescriptor = RuleDescriptor{ +var R0005UnexpectedDomainRequestRuleDescriptor = ruleengine.RuleDescriptor{ ID: R0005ID, Name: R0005Name, Description: "Detecting unexpected domain requests that are not whitelisted by application profile.", @@ -61,7 +61,7 @@ func (rule *R0005UnexpectedDomainRequest) generatePatchCommand(event *tracerdnst event.GetContainer(), event.DNSName) } -func (rule *R0005UnexpectedDomainRequest) ProcessEvent(eventType utils.EventType, event interface{}, objCache objectcache.ObjectCache) ruleengine.RuleFailure { +func (rule *R0005UnexpectedDomainRequest) ProcessEvent(eventType utils.EventType, event utils.K8sEvent, objCache objectcache.ObjectCache) ruleengine.RuleFailure { if eventType != utils.DnsEventType { return nil } diff --git a/pkg/ruleengine/v1/r0006_unexpected_service_account_token_access.go b/pkg/ruleengine/v1/r0006_unexpected_service_account_token_access.go index 891f8f97..b6b7fe0d 100644 --- a/pkg/ruleengine/v1/r0006_unexpected_service_account_token_access.go +++ b/pkg/ruleengine/v1/r0006_unexpected_service_account_token_access.go @@ -27,7 +27,7 @@ var serviceAccountTokenPathsPrefix = []string{ "/var/run/secrets/eks.amazonaws.com/serviceaccount", } -var R0006UnexpectedServiceAccountTokenAccessRuleDescriptor = RuleDescriptor{ +var R0006UnexpectedServiceAccountTokenAccessRuleDescriptor = ruleengine.RuleDescriptor{ ID: R0006ID, Name: R0006Name, Description: "Detecting unexpected access to service account token.", @@ -76,7 +76,7 @@ func (rule *R0006UnexpectedServiceAccountTokenAccess) generatePatchCommand(event event.GetContainer(), event.FullPath, flagList) } -func (rule *R0006UnexpectedServiceAccountTokenAccess) ProcessEvent(eventType utils.EventType, event interface{}, objCache objectcache.ObjectCache) ruleengine.RuleFailure { +func (rule *R0006UnexpectedServiceAccountTokenAccess) ProcessEvent(eventType utils.EventType, event utils.K8sEvent, objCache objectcache.ObjectCache) ruleengine.RuleFailure { if eventType != utils.OpenEventType { return nil } diff --git a/pkg/ruleengine/v1/r0007_kubernetes_client_executed.go b/pkg/ruleengine/v1/r0007_kubernetes_client_executed.go index 58e3766a..39951124 100644 --- a/pkg/ruleengine/v1/r0007_kubernetes_client_executed.go +++ b/pkg/ruleengine/v1/r0007_kubernetes_client_executed.go @@ -44,7 +44,7 @@ var kubernetesClients = []string{ "containerd-shim-runc", } -var R0007KubernetesClientExecutedDescriptor = RuleDescriptor{ +var R0007KubernetesClientExecutedDescriptor = ruleengine.RuleDescriptor{ ID: R0007ID, Name: R0007Name, Description: "Detecting exececution of kubernetes client", @@ -186,7 +186,7 @@ func (rule *R0007KubernetesClientExecuted) handleExecEvent(event *tracerexectype return nil } -func (rule *R0007KubernetesClientExecuted) ProcessEvent(eventType utils.EventType, event interface{}, objCache objectcache.ObjectCache) ruleengine.RuleFailure { +func (rule *R0007KubernetesClientExecuted) ProcessEvent(eventType utils.EventType, event utils.K8sEvent, objCache objectcache.ObjectCache) ruleengine.RuleFailure { if eventType != utils.ExecveEventType && eventType != utils.NetworkEventType { return nil } diff --git a/pkg/ruleengine/v1/r0008_read_env_variables_procfs.go b/pkg/ruleengine/v1/r0008_read_env_variables_procfs.go index 86c55fb9..ef29e017 100644 --- a/pkg/ruleengine/v1/r0008_read_env_variables_procfs.go +++ b/pkg/ruleengine/v1/r0008_read_env_variables_procfs.go @@ -17,7 +17,7 @@ const ( R0008Name = "Read Environment Variables from procfs" ) -var R0008ReadEnvironmentVariablesProcFSRuleDescriptor = RuleDescriptor{ +var R0008ReadEnvironmentVariablesProcFSRuleDescriptor = ruleengine.RuleDescriptor{ ID: R0008ID, Name: R0008Name, Description: "Detecting reading environment variables from procfs.", @@ -52,7 +52,7 @@ func (rule *R0008ReadEnvironmentVariablesProcFS) ID() string { func (rule *R0008ReadEnvironmentVariablesProcFS) DeleteRule() { } -func (rule *R0008ReadEnvironmentVariablesProcFS) ProcessEvent(eventType utils.EventType, event interface{}, objCache objectcache.ObjectCache) ruleengine.RuleFailure { +func (rule *R0008ReadEnvironmentVariablesProcFS) ProcessEvent(eventType utils.EventType, event utils.K8sEvent, objCache objectcache.ObjectCache) ruleengine.RuleFailure { if eventType != utils.OpenEventType { return nil } diff --git a/pkg/ruleengine/v1/r0009_ebpf_program_load.go b/pkg/ruleengine/v1/r0009_ebpf_program_load.go index af12b834..3c064d38 100644 --- a/pkg/ruleengine/v1/r0009_ebpf_program_load.go +++ b/pkg/ruleengine/v1/r0009_ebpf_program_load.go @@ -18,7 +18,7 @@ const ( R0009Name = "eBPF Program Load" ) -var R0009EbpfProgramLoadRuleDescriptor = RuleDescriptor{ +var R0009EbpfProgramLoadRuleDescriptor = ruleengine.RuleDescriptor{ ID: R0009ID, Name: R0009Name, Description: "Detecting eBPF program load.", @@ -55,7 +55,7 @@ func (rule *R0009EbpfProgramLoad) ID() string { func (rule *R0009EbpfProgramLoad) DeleteRule() { } -func (rule *R0009EbpfProgramLoad) ProcessEvent(eventType utils.EventType, event interface{}, objCache objectcache.ObjectCache) ruleengine.RuleFailure { +func (rule *R0009EbpfProgramLoad) ProcessEvent(eventType utils.EventType, event utils.K8sEvent, objCache objectcache.ObjectCache) ruleengine.RuleFailure { if rule.alreadyNotified { return nil } @@ -105,7 +105,7 @@ func (rule *R0009EbpfProgramLoad) ProcessEvent(eventType utils.EventType, event RuleDescription: fmt.Sprintf("bpf system call executed in %s", syscallEvent.GetContainer()), }, RuntimeAlertK8sDetails: apitypes.RuntimeAlertK8sDetails{ - PodName: syscallEvent.GetPod(), + PodName: syscallEvent.GetPod(), PodLabels: syscallEvent.K8s.PodLabels, }, RuleID: rule.ID(), diff --git a/pkg/ruleengine/v1/r0010_unexpected_sensitive_file_access.go b/pkg/ruleengine/v1/r0010_unexpected_sensitive_file_access.go index 868306e3..54d35e64 100644 --- a/pkg/ruleengine/v1/r0010_unexpected_sensitive_file_access.go +++ b/pkg/ruleengine/v1/r0010_unexpected_sensitive_file_access.go @@ -19,7 +19,7 @@ const ( R0010Name = "Unexpected Sensitive File Access" ) -var R0010UnexpectedSensitiveFileAccessRuleDescriptor = RuleDescriptor{ +var R0010UnexpectedSensitiveFileAccessRuleDescriptor = ruleengine.RuleDescriptor{ ID: R0010ID, Name: R0010Name, Description: "Detecting access to sensitive files.", @@ -76,7 +76,7 @@ func (rule *R0010UnexpectedSensitiveFileAccess) ID() string { func (rule *R0010UnexpectedSensitiveFileAccess) DeleteRule() { } -func (rule *R0010UnexpectedSensitiveFileAccess) ProcessEvent(eventType utils.EventType, event interface{}, objCache objectcache.ObjectCache) ruleengine.RuleFailure { +func (rule *R0010UnexpectedSensitiveFileAccess) ProcessEvent(eventType utils.EventType, event utils.K8sEvent, objCache objectcache.ObjectCache) ruleengine.RuleFailure { if eventType != utils.OpenEventType { return nil } diff --git a/pkg/ruleengine/v1/r0011_unexpected_egress_network_traffic.go b/pkg/ruleengine/v1/r0011_unexpected_egress_network_traffic.go index 78384fa9..3c91f107 100644 --- a/pkg/ruleengine/v1/r0011_unexpected_egress_network_traffic.go +++ b/pkg/ruleengine/v1/r0011_unexpected_egress_network_traffic.go @@ -21,7 +21,7 @@ const ( R0011Name = "Unexpected Egress Network Traffic" ) -var R0011UnexpectedEgressNetworkTrafficRuleDescriptor = RuleDescriptor{ +var R0011UnexpectedEgressNetworkTrafficRuleDescriptor = ruleengine.RuleDescriptor{ ID: R0011ID, Name: R0011Name, Description: "Detecting unexpected egress network traffic that is not whitelisted by application profile.", @@ -133,7 +133,7 @@ func (rule *R0011UnexpectedEgressNetworkTraffic) handleNetworkEvent(networkEvent return nil } -func (rule *R0011UnexpectedEgressNetworkTraffic) ProcessEvent(eventType utils.EventType, event interface{}, objCache objectcache.ObjectCache) ruleengine.RuleFailure { +func (rule *R0011UnexpectedEgressNetworkTraffic) ProcessEvent(eventType utils.EventType, event utils.K8sEvent, objCache objectcache.ObjectCache) ruleengine.RuleFailure { if eventType != utils.NetworkEventType { return nil } diff --git a/pkg/ruleengine/v1/r1000_exec_from_malicious_source.go b/pkg/ruleengine/v1/r1000_exec_from_malicious_source.go index b60eacd6..2be1abf2 100644 --- a/pkg/ruleengine/v1/r1000_exec_from_malicious_source.go +++ b/pkg/ruleengine/v1/r1000_exec_from_malicious_source.go @@ -18,7 +18,7 @@ const ( R1000Name = "Exec from malicious source" ) -var R1000ExecFromMaliciousSourceDescriptor = RuleDescriptor{ +var R1000ExecFromMaliciousSourceDescriptor = ruleengine.RuleDescriptor{ ID: R1000ID, Name: R1000Name, Description: "Detecting exec calls that are from malicious source like: /dev/shm, /proc/self", @@ -49,7 +49,7 @@ func (rule *R1000ExecFromMaliciousSource) ID() string { return R1000ID } -func (rule *R1000ExecFromMaliciousSource) ProcessEvent(eventType utils.EventType, event interface{}, _ objectcache.ObjectCache) ruleengine.RuleFailure { +func (rule *R1000ExecFromMaliciousSource) ProcessEvent(eventType utils.EventType, event utils.K8sEvent, _ objectcache.ObjectCache) ruleengine.RuleFailure { if eventType != utils.ExecveEventType { return nil } diff --git a/pkg/ruleengine/v1/r1001_exec_binary_not_in_base_image.go b/pkg/ruleengine/v1/r1001_exec_binary_not_in_base_image.go index 5f09e550..f9236509 100644 --- a/pkg/ruleengine/v1/r1001_exec_binary_not_in_base_image.go +++ b/pkg/ruleengine/v1/r1001_exec_binary_not_in_base_image.go @@ -18,7 +18,7 @@ const ( R1001Name = "Exec Binary Not In Base Image" ) -var R1001ExecBinaryNotInBaseImageRuleDescriptor = RuleDescriptor{ +var R1001ExecBinaryNotInBaseImageRuleDescriptor = ruleengine.RuleDescriptor{ ID: R1001ID, Name: R1001Name, Description: "Detecting exec calls of binaries that are not included in the base image", @@ -52,7 +52,7 @@ func (rule *R1001ExecBinaryNotInBaseImage) ID() string { func (rule *R1001ExecBinaryNotInBaseImage) DeleteRule() { } -func (rule *R1001ExecBinaryNotInBaseImage) ProcessEvent(eventType utils.EventType, event interface{}, objectCache objectcache.ObjectCache) ruleengine.RuleFailure { +func (rule *R1001ExecBinaryNotInBaseImage) ProcessEvent(eventType utils.EventType, event utils.K8sEvent, objectCache objectcache.ObjectCache) ruleengine.RuleFailure { if eventType != utils.ExecveEventType { return nil } diff --git a/pkg/ruleengine/v1/r1002_load_kernel_module.go b/pkg/ruleengine/v1/r1002_load_kernel_module.go index b50e1a91..b74dfda4 100644 --- a/pkg/ruleengine/v1/r1002_load_kernel_module.go +++ b/pkg/ruleengine/v1/r1002_load_kernel_module.go @@ -17,7 +17,7 @@ const ( R1002Name = "Kernel Module Load" ) -var R1002LoadKernelModuleRuleDescriptor = RuleDescriptor{ +var R1002LoadKernelModuleRuleDescriptor = ruleengine.RuleDescriptor{ ID: R1002ID, Name: R1002Name, Description: "Detecting Kernel Module Load.", @@ -52,7 +52,7 @@ func (rule *R1002LoadKernelModule) ID() string { func (rule *R1002LoadKernelModule) DeleteRule() { } -func (rule *R1002LoadKernelModule) ProcessEvent(eventType utils.EventType, event interface{}, objCache objectcache.ObjectCache) ruleengine.RuleFailure { +func (rule *R1002LoadKernelModule) ProcessEvent(eventType utils.EventType, event utils.K8sEvent, objCache objectcache.ObjectCache) ruleengine.RuleFailure { if rule.alerted { return nil } diff --git a/pkg/ruleengine/v1/r1003_malicious_ssh_connection.go b/pkg/ruleengine/v1/r1003_malicious_ssh_connection.go index c10f9f2a..e8205a4f 100644 --- a/pkg/ruleengine/v1/r1003_malicious_ssh_connection.go +++ b/pkg/ruleengine/v1/r1003_malicious_ssh_connection.go @@ -25,7 +25,7 @@ const ( R1003Name = "Malicious SSH Connection" ) -var R1003MaliciousSSHConnectionRuleDescriptor = RuleDescriptor{ +var R1003MaliciousSSHConnectionRuleDescriptor = ruleengine.RuleDescriptor{ ID: R1003ID, Name: R1003Name, Description: "Detecting ssh connection to disallowed port", @@ -89,7 +89,7 @@ func CreateRuleR1003MaliciousSSHConnection() *R1003MaliciousSSHConnection { logger.L().Error("Failed to read port range, setting to default range:", helpers.Error(err)) } return &R1003MaliciousSSHConnection{ - allowedPorts: []uint16{22}, + allowedPorts: []uint16{22, 2022}, ephemeralPortRange: ephemeralPorts, } } @@ -127,7 +127,7 @@ func (rule *R1003MaliciousSSHConnection) SetParameters(params map[string]interfa func (rule *R1003MaliciousSSHConnection) DeleteRule() { } -func (rule *R1003MaliciousSSHConnection) ProcessEvent(eventType utils.EventType, event interface{}, objectCache objectcache.ObjectCache) ruleengine.RuleFailure { +func (rule *R1003MaliciousSSHConnection) ProcessEvent(eventType utils.EventType, event utils.K8sEvent, objectCache objectcache.ObjectCache) ruleengine.RuleFailure { if eventType != utils.SSHEventType { return nil } diff --git a/pkg/ruleengine/v1/r1004_exec_from_mount.go b/pkg/ruleengine/v1/r1004_exec_from_mount.go index 06b97958..3c06c18a 100644 --- a/pkg/ruleengine/v1/r1004_exec_from_mount.go +++ b/pkg/ruleengine/v1/r1004_exec_from_mount.go @@ -18,7 +18,7 @@ const ( R1004Name = "Exec from mount" ) -var R1004ExecFromMountRuleDescriptor = RuleDescriptor{ +var R1004ExecFromMountRuleDescriptor = ruleengine.RuleDescriptor{ ID: R1004ID, Name: R1004Name, Description: "Detecting exec calls from mounted paths.", @@ -50,7 +50,7 @@ func (rule *R1004ExecFromMount) ID() string { func (rule *R1004ExecFromMount) DeleteRule() { } -func (rule *R1004ExecFromMount) ProcessEvent(eventType utils.EventType, event interface{}, objCache objectcache.ObjectCache) ruleengine.RuleFailure { +func (rule *R1004ExecFromMount) ProcessEvent(eventType utils.EventType, event utils.K8sEvent, objCache objectcache.ObjectCache) ruleengine.RuleFailure { if eventType != utils.ExecveEventType { return nil } diff --git a/pkg/ruleengine/v1/r1005_fileless_execution.go b/pkg/ruleengine/v1/r1005_fileless_execution.go index 8e63a613..d1645caa 100644 --- a/pkg/ruleengine/v1/r1005_fileless_execution.go +++ b/pkg/ruleengine/v1/r1005_fileless_execution.go @@ -18,7 +18,7 @@ const ( R1005Name = "Fileless Execution" ) -var R1005FilelessExecutionRuleDescriptor = RuleDescriptor{ +var R1005FilelessExecutionRuleDescriptor = ruleengine.RuleDescriptor{ ID: R1005ID, Name: R1005Name, Description: "Detecting Fileless Execution", @@ -54,7 +54,7 @@ func (rule *R1005FilelessExecution) ID() string { func (rule *R1005FilelessExecution) DeleteRule() { } -func (rule *R1005FilelessExecution) ProcessEvent(eventType utils.EventType, event interface{}, _ objectcache.ObjectCache) ruleengine.RuleFailure { +func (rule *R1005FilelessExecution) ProcessEvent(eventType utils.EventType, event utils.K8sEvent, _ objectcache.ObjectCache) ruleengine.RuleFailure { if eventType == utils.ExecveEventType { return rule.handleExecveEvent(event.(*tracerexectype.Event)) } diff --git a/pkg/ruleengine/v1/r1006_unshare_system_call.go b/pkg/ruleengine/v1/r1006_unshare_system_call.go index 974557cd..5440d673 100644 --- a/pkg/ruleengine/v1/r1006_unshare_system_call.go +++ b/pkg/ruleengine/v1/r1006_unshare_system_call.go @@ -17,7 +17,7 @@ const ( R1006Name = "Unshare System Call usage" ) -var R1006UnshareSyscallRuleDescriptor = RuleDescriptor{ +var R1006UnshareSyscallRuleDescriptor = ruleengine.RuleDescriptor{ ID: R1006ID, Name: R1006Name, Description: "Detecting Unshare System Call usage, which can be used to escape container.", @@ -54,7 +54,7 @@ func (rule *R1006UnshareSyscall) ID() string { func (rule *R1006UnshareSyscall) DeleteRule() { } -func (rule *R1006UnshareSyscall) ProcessEvent(eventType utils.EventType, event interface{}, objCache objectcache.ObjectCache) ruleengine.RuleFailure { +func (rule *R1006UnshareSyscall) ProcessEvent(eventType utils.EventType, event utils.K8sEvent, objCache objectcache.ObjectCache) ruleengine.RuleFailure { if rule.alreadyNotified { return nil } diff --git a/pkg/ruleengine/v1/r1007_xmr_crypto_mining.go b/pkg/ruleengine/v1/r1007_xmr_crypto_mining.go index 64eb1121..4083afd9 100644 --- a/pkg/ruleengine/v1/r1007_xmr_crypto_mining.go +++ b/pkg/ruleengine/v1/r1007_xmr_crypto_mining.go @@ -17,7 +17,7 @@ const ( R1007Name = "XMR Crypto Mining Detection" ) -var R1007XMRCryptoMiningRuleDescriptor = RuleDescriptor{ +var R1007XMRCryptoMiningRuleDescriptor = ruleengine.RuleDescriptor{ ID: R1007ID, Name: R1007Name, Description: "Detecting XMR Crypto Miners by randomx algorithm usage.", @@ -54,7 +54,7 @@ func (rule *R1007XMRCryptoMining) ID() string { func (rule *R1007XMRCryptoMining) DeleteRule() { } -func (rule *R1007XMRCryptoMining) ProcessEvent(eventType utils.EventType, event interface{}, _ objectcache.ObjectCache) ruleengine.RuleFailure { +func (rule *R1007XMRCryptoMining) ProcessEvent(eventType utils.EventType, event utils.K8sEvent, _ objectcache.ObjectCache) ruleengine.RuleFailure { if eventType != utils.RandomXEventType { return nil } diff --git a/pkg/ruleengine/v1/r1008_crypto_mining_domain.go b/pkg/ruleengine/v1/r1008_crypto_mining_domain.go index 9f9dd2f6..b1a25f4b 100644 --- a/pkg/ruleengine/v1/r1008_crypto_mining_domain.go +++ b/pkg/ruleengine/v1/r1008_crypto_mining_domain.go @@ -128,7 +128,7 @@ var commonlyUsedCryptoMinersDomains = []string{ "us.monero.herominers.com.", } -var R1008CryptoMiningDomainCommunicationRuleDescriptor = RuleDescriptor{ +var R1008CryptoMiningDomainCommunicationRuleDescriptor = ruleengine.RuleDescriptor{ ID: R1008ID, Name: R1008Name, Description: "Detecting Crypto miners communication by domain", @@ -166,7 +166,7 @@ func (rule *R1008CryptoMiningDomainCommunication) ID() string { func (rule *R1008CryptoMiningDomainCommunication) DeleteRule() { } -func (rule *R1008CryptoMiningDomainCommunication) ProcessEvent(eventType utils.EventType, event interface{}, _ objectcache.ObjectCache) ruleengine.RuleFailure { +func (rule *R1008CryptoMiningDomainCommunication) ProcessEvent(eventType utils.EventType, event utils.K8sEvent, _ objectcache.ObjectCache) ruleengine.RuleFailure { if eventType != utils.DnsEventType { return nil } diff --git a/pkg/ruleengine/v1/r1009_crypto_mining_port.go b/pkg/ruleengine/v1/r1009_crypto_mining_port.go index d69f493e..006bbe4e 100644 --- a/pkg/ruleengine/v1/r1009_crypto_mining_port.go +++ b/pkg/ruleengine/v1/r1009_crypto_mining_port.go @@ -22,7 +22,7 @@ var CommonlyUsedCryptoMinersPorts = []uint16{ 45700, // Monero (XMR) - Stratum mining protocol (TCP). (stratum+tcp://xmr.pool.minergate.com) } -var R1009CryptoMiningRelatedPortRuleDescriptor = RuleDescriptor{ +var R1009CryptoMiningRelatedPortRuleDescriptor = ruleengine.RuleDescriptor{ ID: R1009ID, Name: R1009Name, Description: "Detecting Crypto Miners by suspicious port usage.", @@ -59,7 +59,7 @@ func (rule *R1009CryptoMiningRelatedPort) ID() string { func (rule *R1009CryptoMiningRelatedPort) DeleteRule() { } -func (rule *R1009CryptoMiningRelatedPort) ProcessEvent(eventType utils.EventType, event interface{}, objectcache objectcache.ObjectCache) ruleengine.RuleFailure { +func (rule *R1009CryptoMiningRelatedPort) ProcessEvent(eventType utils.EventType, event utils.K8sEvent, objectcache objectcache.ObjectCache) ruleengine.RuleFailure { if eventType != utils.NetworkEventType { return nil } diff --git a/pkg/ruleengine/v1/r1010_symlink_created_over_sensitive_file.go b/pkg/ruleengine/v1/r1010_symlink_created_over_sensitive_file.go index 7b575c90..40537200 100644 --- a/pkg/ruleengine/v1/r1010_symlink_created_over_sensitive_file.go +++ b/pkg/ruleengine/v1/r1010_symlink_created_over_sensitive_file.go @@ -21,7 +21,7 @@ const ( R1010Name = "Symlink Created Over Sensitive File" ) -var R1010SymlinkCreatedOverSensitiveFileRuleDescriptor = RuleDescriptor{ +var R1010SymlinkCreatedOverSensitiveFileRuleDescriptor = ruleengine.RuleDescriptor{ ID: R1010ID, Name: R1010Name, Description: "Detecting symlink creation over sensitive files.", @@ -78,7 +78,7 @@ func (rule *R1010SymlinkCreatedOverSensitiveFile) ID() string { func (rule *R1010SymlinkCreatedOverSensitiveFile) DeleteRule() { } -func (rule *R1010SymlinkCreatedOverSensitiveFile) ProcessEvent(eventType utils.EventType, event interface{}, objCache objectcache.ObjectCache) ruleengine.RuleFailure { +func (rule *R1010SymlinkCreatedOverSensitiveFile) ProcessEvent(eventType utils.EventType, event utils.K8sEvent, objCache objectcache.ObjectCache) ruleengine.RuleFailure { if eventType != utils.SymlinkEventType { return nil } diff --git a/pkg/ruleengine/v1/r1011_ld_preload_hook.go b/pkg/ruleengine/v1/r1011_ld_preload_hook.go index 86a1ccab..ea926fdd 100644 --- a/pkg/ruleengine/v1/r1011_ld_preload_hook.go +++ b/pkg/ruleengine/v1/r1011_ld_preload_hook.go @@ -25,7 +25,7 @@ const ( var LD_PRELOAD_ENV_VARS = []string{"LD_PRELOAD", "LD_AUDIT", "LD_LIBRARY_PATH"} -var R1011LdPreloadHookRuleDescriptor = RuleDescriptor{ +var R1011LdPreloadHookRuleDescriptor = ruleengine.RuleDescriptor{ ID: R1011ID, Name: R1011Name, Description: "Detecting ld_preload hook techniques.", @@ -183,7 +183,7 @@ func (rule *R1011LdPreloadHook) handleOpenEvent(openEvent *traceropentype.Event) return nil } -func (rule *R1011LdPreloadHook) ProcessEvent(eventType utils.EventType, event interface{}, objectCache objectcache.ObjectCache) ruleengine.RuleFailure { +func (rule *R1011LdPreloadHook) ProcessEvent(eventType utils.EventType, event utils.K8sEvent, objectCache objectcache.ObjectCache) ruleengine.RuleFailure { if eventType != utils.ExecveEventType && eventType != utils.OpenEventType { return nil } diff --git a/pkg/ruleengine/v1/r1012_hardlink_created_over_sensitive_file.go b/pkg/ruleengine/v1/r1012_hardlink_created_over_sensitive_file.go index a6adedd1..3d70f34c 100644 --- a/pkg/ruleengine/v1/r1012_hardlink_created_over_sensitive_file.go +++ b/pkg/ruleengine/v1/r1012_hardlink_created_over_sensitive_file.go @@ -21,7 +21,7 @@ const ( R1012Name = "Hardlink Created Over Sensitive File" ) -var R1012HardlinkCreatedOverSensitiveFileRuleDescriptor = RuleDescriptor{ +var R1012HardlinkCreatedOverSensitiveFileRuleDescriptor = ruleengine.RuleDescriptor{ ID: R1012ID, Name: R1012Name, Description: "Detecting hardlink creation over sensitive files.", @@ -78,7 +78,7 @@ func (rule *R1012HardlinkCreatedOverSensitiveFile) ID() string { func (rule *R1012HardlinkCreatedOverSensitiveFile) DeleteRule() { } -func (rule *R1012HardlinkCreatedOverSensitiveFile) ProcessEvent(eventType utils.EventType, event interface{}, objCache objectcache.ObjectCache) ruleengine.RuleFailure { +func (rule *R1012HardlinkCreatedOverSensitiveFile) ProcessEvent(eventType utils.EventType, event utils.K8sEvent, objCache objectcache.ObjectCache) ruleengine.RuleFailure { if eventType != utils.HardlinkEventType { return nil } diff --git a/pkg/ruleengine/v1/rule.go b/pkg/ruleengine/v1/rule.go index 2a084263..8c198ab6 100644 --- a/pkg/ruleengine/v1/rule.go +++ b/pkg/ruleengine/v1/rule.go @@ -16,34 +16,6 @@ const ( RulePrioritySystemIssue = 1000 ) -type RuleDescriptor struct { - // Rule ID - ID string - // Rule Name - Name string - // Rule Description - Description string - // Priority - Priority int - // Tags - Tags []string - // Rule requirements - Requirements ruleengine.RuleSpec - // Create a rule function - RuleCreationFunc func() ruleengine.RuleEvaluator -} - -func (r *RuleDescriptor) HasTags(tags []string) bool { - for _, tag := range tags { - for _, ruleTag := range r.Tags { - if tag == ruleTag { - return true - } - } - } - return false -} - var _ ruleengine.RuleSpec = (*RuleRequirements)(nil) type RuleRequirements struct { diff --git a/pkg/rulemanager/rule_manager_interface.go b/pkg/rulemanager/rule_manager_interface.go index 52ae18c6..76f71de8 100644 --- a/pkg/rulemanager/rule_manager_interface.go +++ b/pkg/rulemanager/rule_manager_interface.go @@ -1,17 +1,9 @@ package rulemanager import ( - tracerhardlinktype "github.com/kubescape/node-agent/pkg/ebpf/gadgets/hardlink/types" - tracerrandomxtype "github.com/kubescape/node-agent/pkg/ebpf/gadgets/randomx/types" - tracersshtype "github.com/kubescape/node-agent/pkg/ebpf/gadgets/ssh/types" - tracersymlinktype "github.com/kubescape/node-agent/pkg/ebpf/gadgets/symlink/types" + "github.com/kubescape/node-agent/pkg/utils" containercollection "github.com/inspektor-gadget/inspektor-gadget/pkg/container-collection" - tracercapabilitiestype "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/trace/capabilities/types" - tracerdnstype "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/trace/dns/types" - tracerexectype "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/trace/exec/types" - tracernetworktype "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/trace/network/types" - traceropentype "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/trace/open/types" v1 "k8s.io/api/core/v1" ) @@ -19,15 +11,7 @@ import ( type RuleManagerClient interface { ContainerCallback(notif containercollection.PubSubEvent) RegisterPeekFunc(peek func(mntns uint64) ([]string, error)) - ReportCapability(event tracercapabilitiestype.Event) - ReportFileExec(event tracerexectype.Event) - ReportFileOpen(event traceropentype.Event) - ReportNetworkEvent(event tracernetworktype.Event) - ReportDNSEvent(event tracerdnstype.Event) - ReportRandomxEvent(event tracerrandomxtype.Event) - ReportSymlinkEvent(event tracersymlinktype.Event) - ReportHardlinkEvent(event tracerhardlinktype.Event) - ReportSSHEvent(event tracersshtype.Event) + ReportEvent(eventType utils.EventType, event utils.K8sEvent) HasApplicableRuleBindings(namespace, name string) bool HasFinalApplicationProfile(pod *v1.Pod) bool IsContainerMonitored(k8sContainerID string) bool diff --git a/pkg/rulemanager/rule_manager_mock.go b/pkg/rulemanager/rule_manager_mock.go index 08d0cbd1..3cf78d59 100644 --- a/pkg/rulemanager/rule_manager_mock.go +++ b/pkg/rulemanager/rule_manager_mock.go @@ -1,17 +1,9 @@ package rulemanager import ( - tracerhardlinktype "github.com/kubescape/node-agent/pkg/ebpf/gadgets/hardlink/types" - tracerrandomxtype "github.com/kubescape/node-agent/pkg/ebpf/gadgets/randomx/types" - tracersshtype "github.com/kubescape/node-agent/pkg/ebpf/gadgets/ssh/types" - tracersymlinktype "github.com/kubescape/node-agent/pkg/ebpf/gadgets/symlink/types" + "github.com/kubescape/node-agent/pkg/utils" containercollection "github.com/inspektor-gadget/inspektor-gadget/pkg/container-collection" - tracercapabilitiestype "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/trace/capabilities/types" - tracerdnstype "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/trace/dns/types" - tracerexectype "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/trace/exec/types" - tracernetworktype "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/trace/network/types" - traceropentype "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/trace/open/types" v1 "k8s.io/api/core/v1" ) @@ -32,39 +24,7 @@ func (r *RuleManagerMock) RegisterPeekFunc(_ func(mntns uint64) ([]string, error // noop } -func (r *RuleManagerMock) ReportCapability(_ tracercapabilitiestype.Event) { - // noop -} - -func (r *RuleManagerMock) ReportFileExec(_ tracerexectype.Event) { - // noop -} - -func (r *RuleManagerMock) ReportFileOpen(_ traceropentype.Event) { - // noop -} - -func (r *RuleManagerMock) ReportNetworkEvent(_ tracernetworktype.Event) { - // noop -} - -func (r *RuleManagerMock) ReportDNSEvent(_ tracerdnstype.Event) { - // noop -} - -func (r *RuleManagerMock) ReportRandomxEvent(_ tracerrandomxtype.Event) { - // noop -} - -func (r *RuleManagerMock) ReportSymlinkEvent(_ tracersymlinktype.Event) { - // noop -} - -func (r *RuleManagerMock) ReportHardlinkEvent(_ tracerhardlinktype.Event) { - // noop -} - -func (r *RuleManagerMock) ReportSSHEvent(_ tracersshtype.Event) { +func (r *RuleManagerMock) ReportEvent(_ utils.EventType, _ utils.K8sEvent) { // noop } diff --git a/pkg/rulemanager/v1/rule_manager.go b/pkg/rulemanager/v1/rule_manager.go index 1e3a2f63..56ce6156 100644 --- a/pkg/rulemanager/v1/rule_manager.go +++ b/pkg/rulemanager/v1/rule_manager.go @@ -26,20 +26,11 @@ import ( "github.com/kubescape/node-agent/pkg/metricsmanager" "github.com/kubescape/node-agent/pkg/objectcache" - tracerhardlinktype "github.com/kubescape/node-agent/pkg/ebpf/gadgets/hardlink/types" - tracerrandomxtype "github.com/kubescape/node-agent/pkg/ebpf/gadgets/randomx/types" - tracersshtype "github.com/kubescape/node-agent/pkg/ebpf/gadgets/ssh/types" - tracersymlinktype "github.com/kubescape/node-agent/pkg/ebpf/gadgets/symlink/types" ruleenginetypes "github.com/kubescape/node-agent/pkg/ruleengine/types" mapset "github.com/deckarep/golang-set/v2" "github.com/goradd/maps" containercollection "github.com/inspektor-gadget/inspektor-gadget/pkg/container-collection" - tracercapabilitiestype "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/trace/capabilities/types" - tracerdnstype "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/trace/dns/types" - tracerexectype "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/trace/exec/types" - tracernetworktype "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/trace/network/types" - traceropentype "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/trace/open/types" eventtypes "github.com/inspektor-gadget/inspektor-gadget/pkg/types" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -330,117 +321,18 @@ func (rm *RuleManager) RegisterPeekFunc(peek func(mntns uint64) ([]string, error rm.syscallPeekFunc = peek } -func (rm *RuleManager) ReportCapability(event tracercapabilitiestype.Event) { +func (rm *RuleManager) ReportEvent(eventType utils.EventType, event utils.K8sEvent) { if event.GetNamespace() == "" || event.GetPod() == "" { - logger.L().Error("RuleManager - failed to get namespace and pod name from ReportCapability event") + logger.L().Error("RuleManager - failed to get namespace and pod name from custom event") return } - // list capability rules + // list custom rules rules := rm.ruleBindingCache.ListRulesForPod(event.GetNamespace(), event.GetPod()) - - rm.processEvent(utils.CapabilitiesEventType, &event, rules) -} - -func (rm *RuleManager) ReportFileExec(event tracerexectype.Event) { - if event.GetNamespace() == "" || event.GetPod() == "" { - logger.L().Error("RuleManager - failed to get namespace and pod name from ReportFileExec event") - return - } - - // list exec rules - rules := rm.ruleBindingCache.ListRulesForPod(event.GetNamespace(), event.GetPod()) - rm.processEvent(utils.ExecveEventType, &event, rules) -} - -func (rm *RuleManager) ReportFileOpen(event traceropentype.Event) { - if event.GetNamespace() == "" || event.GetPod() == "" { - logger.L().Error("RuleManager - failed to get namespace and pod name from ReportFileOpen event") - return - } - - // list open rules - rules := rm.ruleBindingCache.ListRulesForPod(event.GetNamespace(), event.GetPod()) - - rm.processEvent(utils.OpenEventType, &event, rules) - -} - -func (rm *RuleManager) ReportNetworkEvent(event tracernetworktype.Event) { - if event.GetNamespace() == "" || event.GetPod() == "" { - logger.L().Error("RuleManager - failed to get namespace and pod name from ReportNetworkEvent event") - return - } - - // list network rules - rules := rm.ruleBindingCache.ListRulesForPod(event.GetNamespace(), event.GetPod()) - - rm.processEvent(utils.NetworkEventType, &event, rules) -} - -func (rm *RuleManager) ReportDNSEvent(event tracerdnstype.Event) { - // ignore events with empty container name - if event.K8s.ContainerName == "" { - return - } - - if event.GetNamespace() == "" || event.GetPod() == "" { - logger.L().Error("RuleManager - failed to get namespace and pod name from ReportDNSEvent event") - return - } - - // list dns rules - rules := rm.ruleBindingCache.ListRulesForPod(event.GetNamespace(), event.GetPod()) - - rm.processEvent(utils.DnsEventType, &event, rules) -} - -func (rm *RuleManager) ReportRandomxEvent(event tracerrandomxtype.Event) { - if event.GetNamespace() == "" || event.GetPod() == "" { - logger.L().Error("RuleManager - failed to get namespace and pod name from randomx event") - return - } - - // list randomx rules - rules := rm.ruleBindingCache.ListRulesForPod(event.GetNamespace(), event.GetPod()) - - rm.processEvent(utils.RandomXEventType, &event, rules) -} - -func (rm *RuleManager) ReportSymlinkEvent(event tracersymlinktype.Event) { - if event.GetNamespace() == "" || event.GetPod() == "" { - logger.L().Error("RuleManager - failed to get namespace and pod name from ReportSymlinkEvent event") - return - } - - // list symlink rules - rules := rm.ruleBindingCache.ListRulesForPod(event.GetNamespace(), event.GetPod()) - rm.processEvent(utils.SymlinkEventType, &event, rules) -} - -func (rm *RuleManager) ReportHardlinkEvent(event tracerhardlinktype.Event) { - if event.GetNamespace() == "" || event.GetPod() == "" { - logger.L().Error("RuleManager - failed to get namespace and pod name from ReportHardlinkEvent event") - return - } - - // list hardlink rules - rules := rm.ruleBindingCache.ListRulesForPod(event.GetNamespace(), event.GetPod()) - rm.processEvent(utils.HardlinkEventType, &event, rules) -} - -func (rm *RuleManager) ReportSSHEvent(event tracersshtype.Event) { - if event.GetNamespace() == "" || event.GetPod() == "" { - logger.L().Error("RuleManager - failed to get namespace and pod name from ReportSSHEvent event") - return - } - - // list ssh rules - rules := rm.ruleBindingCache.ListRulesForPod(event.GetNamespace(), event.GetPod()) - rm.processEvent(utils.SSHEventType, &event, rules) + rm.processEvent(eventType, event, rules) } -func (rm *RuleManager) processEvent(eventType utils.EventType, event interface{}, rules []ruleengine.RuleEvaluator) { +func (rm *RuleManager) processEvent(eventType utils.EventType, event utils.K8sEvent, rules []ruleengine.RuleEvaluator) { for _, rule := range rules { if rule == nil { continue diff --git a/pkg/rulemanager/v1/rule_manager_test.go b/pkg/rulemanager/v1/rule_manager_test.go index 11ee67ea..cefa8559 100644 --- a/pkg/rulemanager/v1/rule_manager_test.go +++ b/pkg/rulemanager/v1/rule_manager_test.go @@ -1,77 +1,39 @@ package rulemanager -// func TestApplicationProfileManager(t *testing.T) { -// cfg := config.Config{ -// InitialDelay: 1 * time.Second, -// MaxSniffingTime: 5 * time.Minute, -// UpdateDataPeriod: 1 * time.Second, -// } -// ctx := context.TODO() -// k8sClient := &k8sclient.K8sClientMock{} -// storageClient := &storage.StorageHttpClientMock{} -// am, err := CreateApplicationProfileManager(ctx, cfg, "cluster", k8sClient, storageClient) -// assert.NoError(t, err) -// // prepare container -// container := &containercollection.Container{ -// K8s: containercollection.K8sMetadata{ -// BasicK8sMetadata: types.BasicK8sMetadata{ -// Namespace: "ns", -// PodName: "pod", -// ContainerName: "cont", -// }, -// }, -// Runtime: containercollection.RuntimeMetadata{ -// BasicRuntimeMetadata: types.BasicRuntimeMetadata{ -// ContainerID: "5fff6a395ce4e6984a9447cc6cfb09f473eaf278498243963fcc944889bc8400", -// }, -// }, -// } -// // register peek function for syscall tracer -// go am.RegisterPeekFunc(func(_ uint64) ([]string, error) { -// return []string{"dup", "listen"}, nil -// }) -// // report capability -// go am.ReportCapability("ns/pod/cont", "NET_BIND_SERVICE") -// // report file exec -// go am.ReportFileExec("ns/pod/cont", "", []string{"ls"}) // will not be reported -// go am.ReportFileExec("ns/pod/cont", "/bin/bash", []string{"-c", "ls"}) -// // report file open -// go am.ReportFileOpen("ns/pod/cont", "/etc/passwd", []string{"O_RDONLY"}) -// // report container started (race condition with reports) -// am.ContainerCallback(containercollection.PubSubEvent{ -// Type: containercollection.EventTypeAddContainer, -// Container: container, -// }) -// // let it run for a while -// time.Sleep(15 * time.Second) // need to sleep longer because of AddRandomDuration in startApplicationProfiling -// // report another file open -// go am.ReportFileOpen("ns/pod/cont", "/etc/hosts", []string{"O_RDONLY"}) -// // sleep more -// time.Sleep(2 * time.Second) -// // report container stopped -// am.ContainerCallback(containercollection.PubSubEvent{ -// Type: containercollection.EventTypeRemoveContainer, -// Container: container, -// }) -// // let it stop -// time.Sleep(2 * time.Second) -// // verify generated CRDs -// assert.Equal(t, 1, len(storageClient.ApplicationActivities)) -// sort.Strings(storageClient.ApplicationActivities[0].Spec.Syscalls) -// assert.Equal(t, []string{"dup", "listen"}, storageClient.ApplicationActivities[0].Spec.Syscalls) -// assert.Equal(t, 2, len(storageClient.ApplicationProfiles)) -// assert.Equal(t, 2, len(storageClient.ApplicationProfileSummaries)) -// // check the first profile -// sort.Strings(storageClient.ApplicationProfiles[0].Spec.Containers[0].Capabilities) -// assert.Equal(t, []string{"NET_BIND_SERVICE"}, storageClient.ApplicationProfiles[0].Spec.Containers[1].Capabilities) -// assert.Equal(t, []v1beta1.ExecCalls{{Path: "/bin/bash", Args: []string{"-c", "ls"}, Envs: []string(nil)}}, storageClient.ApplicationProfiles[0].Spec.Containers[1].Execs) -// assert.Equal(t, []v1beta1.OpenCalls{{Path: "/etc/passwd", Flags: []string{"O_RDONLY"}}}, storageClient.ApplicationProfiles[0].Spec.Containers[1].Opens) -// // check the second profile - this is a patch for execs and opens -// sort.Strings(storageClient.ApplicationProfiles[1].Spec.Containers[0].Capabilities) -// assert.Equal(t, []string{"NET_BIND_SERVICE"}, storageClient.ApplicationProfiles[1].Spec.Containers[1].Capabilities) -// assert.Equal(t, []v1beta1.ExecCalls{{Path: "/bin/bash", Args: []string{"-c", "ls"}, Envs: []string(nil)}}, storageClient.ApplicationProfiles[1].Spec.Containers[1].Execs) -// assert.Equal(t, []v1beta1.OpenCalls{ -// {Path: "/etc/passwd", Flags: []string{"O_RDONLY"}}, -// {Path: "/etc/hosts", Flags: []string{"O_RDONLY"}}, -// }, storageClient.ApplicationProfiles[1].Spec.Containers[1].Opens) -// } +import ( + "testing" + + eventtypes "github.com/inspektor-gadget/inspektor-gadget/pkg/types" + "github.com/kubescape/go-logger" + tracerhardlinktype "github.com/kubescape/node-agent/pkg/ebpf/gadgets/hardlink/types" + "github.com/kubescape/node-agent/pkg/utils" +) + +func TestReportEvent(t *testing.T) { + // Create a hardlink event + e := &tracerhardlinktype.Event{ + Event: eventtypes.Event{ + CommonData: eventtypes.CommonData{ + K8s: eventtypes.K8sMetadata{ + BasicK8sMetadata: eventtypes.BasicK8sMetadata{ + ContainerName: "test", + }, + }, + }, + }, + Comm: "test", + OldPath: "test", + NewPath: "test", + } + + // Create a new rule + reportEvent(utils.HardlinkEventType, e) +} + +func reportEvent(eventType utils.EventType, event utils.K8sEvent) { + k8sEvent := event.(*tracerhardlinktype.Event) + if k8sEvent.GetNamespace() == "" || k8sEvent.GetPod() == "" { + logger.L().Error("RuleManager - failed to get namespace and pod name from custom event") + return + } +} diff --git a/pkg/utils/events.go b/pkg/utils/events.go index 90c19876..bebf057a 100644 --- a/pkg/utils/events.go +++ b/pkg/utils/events.go @@ -1,18 +1,23 @@ package utils -type EventType int +type K8sEvent interface { + GetPod() string + GetNamespace() string +} + +type EventType string const ( - ExecveEventType EventType = iota - OpenEventType - CapabilitiesEventType - DnsEventType - NetworkEventType - SyscallEventType - RandomXEventType - SymlinkEventType - HardlinkEventType - SSHEventType - HTTPEventType - AllEventType + ExecveEventType EventType = "exec" + OpenEventType EventType = "open" + CapabilitiesEventType EventType = "capabilities" + DnsEventType EventType = "dns" + NetworkEventType EventType = "network" + SyscallEventType EventType = "syscall" + RandomXEventType EventType = "randomx" + SymlinkEventType EventType = "symlink" + HardlinkEventType EventType = "hardlink" + SSHEventType EventType = "ssh" + HTTPEventType EventType = "http" + AllEventType EventType = "all" ) diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index 3e70ba6f..e78f99f8 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -428,7 +428,7 @@ func GetProcessEnv(pid int) (map[string]string, error) { } // Get the path of the file on the node. -func GetHostFilePathFromEvent(event interface{}, containerPid uint32) (string, error) { +func GetHostFilePathFromEvent(event K8sEvent, containerPid uint32) (string, error) { if execEvent, ok := event.(*tracerexectype.Event); ok { realPath := filepath.Join("/proc", fmt.Sprintf("/%d/root/%s", containerPid, GetExecPathFromEvent(execEvent))) return realPath, nil diff --git a/internal/validator/ebpf/verifier.go b/pkg/validator/ebpf/verifier.go similarity index 100% rename from internal/validator/ebpf/verifier.go rename to pkg/validator/ebpf/verifier.go diff --git a/internal/validator/validator.go b/pkg/validator/validator.go similarity index 98% rename from internal/validator/validator.go rename to pkg/validator/validator.go index fa655242..0db9834d 100644 --- a/internal/validator/validator.go +++ b/pkg/validator/validator.go @@ -5,8 +5,8 @@ import ( "os" "syscall" - "github.com/kubescape/node-agent/internal/validator/ebpf" "github.com/kubescape/node-agent/pkg/config" + "github.com/kubescape/node-agent/pkg/validator/ebpf" "github.com/cilium/ebpf/rlimit" "github.com/facette/natsort" diff --git a/internal/validator/validator_test.go b/pkg/validator/validator_test.go similarity index 100% rename from internal/validator/validator_test.go rename to pkg/validator/validator_test.go diff --git a/pkg/watcher/dynamicwatcher/watch.go b/pkg/watcher/dynamicwatcher/watch.go index eba63152..d67dbca9 100644 --- a/pkg/watcher/dynamicwatcher/watch.go +++ b/pkg/watcher/dynamicwatcher/watch.go @@ -18,7 +18,6 @@ import ( "github.com/kubescape/go-logger" "github.com/kubescape/go-logger/helpers" - errors2 "k8s.io/apimachinery/pkg/api/errors" k8sErrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" @@ -144,8 +143,14 @@ func (wh *WatchHandler) chooseWatcher(res schema.GroupVersionResource, opts meta return wh.k8sClient.GetDynamicClient().Resource(res).Namespace("").Watch(context.Background(), opts) case "seccompprofiles": return wh.storageClient.SeccompProfiles("").Watch(context.Background(), opts) + default: + // Make sure the resource version is not our storage, if so we panic. + if res.Group == kubescapeCustomResourceGroup { + return nil, fmt.Errorf("resource must use the storage client %s: %w", res.Resource, errNotImplemented) + } + + return wh.k8sClient.GetDynamicClient().Resource(res).Watch(context.Background(), opts) } - return nil, fmt.Errorf("cannot watch for resource %s: %w", res.Resource, errNotImplemented) } func (wh *WatchHandler) watchRetry(ctx context.Context, res schema.GroupVersionResource, watchOpts metav1.ListOptions, eventQueue *cooldownqueue.CooldownQueue) { @@ -213,8 +218,14 @@ func (wh *WatchHandler) chooseLister(res schema.GroupVersionResource, opts metav return wh.k8sClient.GetDynamicClient().Resource(res).Namespace("").List(context.Background(), opts) case "seccompprofiles": return wh.storageClient.SeccompProfiles("").List(context.Background(), opts) + default: + // Make sure the resource version is not our storage, if so we panic. + if res.Group == kubescapeCustomResourceGroup { + return nil, fmt.Errorf("resource must use the storage client %s: %w", res.Resource, errNotImplemented) + } + + return wh.k8sClient.GetDynamicClient().Resource(res).List(context.Background(), opts) } - return nil, errors2.NewNotFound(res.GroupResource(), "not implemented") } func (wh *WatchHandler) getExistingStorageObjects(ctx context.Context, res schema.GroupVersionResource, watchOpts metav1.ListOptions) (string, error) { diff --git a/tests/chart/templates/node-agent/configmap.yaml b/tests/chart/templates/node-agent/configmap.yaml index e1166887..ee1890c8 100644 --- a/tests/chart/templates/node-agent/configmap.yaml +++ b/tests/chart/templates/node-agent/configmap.yaml @@ -15,6 +15,7 @@ data: "runtimeDetectionEnabled": {{ eq .Values.capabilities.runtimeDetection "enable" }}, "networkServiceEnabled": {{ eq .Values.capabilities.networkPolicyService "enable" }}, "malwareDetectionEnabled": {{ eq .Values.capabilities.malwareDetection "enable" }}, + "httpDetectionEnabled": {{ eq .Values.capabilities.httpDetection "enable" }}, "initialDelay": "{{ .Values.nodeAgent.config.learningPeriod }}", "updateDataPeriod": "{{ .Values.nodeAgent.config.updatePeriod }}", "maxDelaySeconds": "{{ .Values.nodeAgent.config.maxDelaySeconds }}", diff --git a/tests/chart/values.yaml b/tests/chart/values.yaml index 534de197..1df339d5 100644 --- a/tests/chart/values.yaml +++ b/tests/chart/values.yaml @@ -11,6 +11,7 @@ capabilities: networkPolicyService: enable runtimeDetection: enable malwareDetection: enable + httpDetection: enable configurations: persistence: enable