diff --git a/build/Dockerfile b/build/Dockerfile index 93d23be58f..33bd448168 100644 --- a/build/Dockerfile +++ b/build/Dockerfile @@ -1,4 +1,4 @@ -FROM --platform=$BUILDPLATFORM golang:1.22-bullseye as builder +FROM --platform=$BUILDPLATFORM golang:1.22-bullseye AS builder ENV GO111MODULE=on CGO_ENABLED=0 WORKDIR /work diff --git a/main.go b/main.go index c71225fd6a..a43dd46147 100644 --- a/main.go +++ b/main.go @@ -121,7 +121,7 @@ func main() { nodeName := os.Getenv(config.NodeNameEnvVar) // Create watchers - dWatcher := dynamicwatcher.NewWatchHandler(k8sClient) + dWatcher := dynamicwatcher.NewWatchHandler(k8sClient, cfg) // create k8sObject cache k8sObjectCache, err := k8scache.NewK8sObjectCache(nodeName, k8sClient) if err != nil { diff --git a/pkg/config/config.go b/pkg/config/config.go index 9b3bc7e718..14ea77526c 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -1,6 +1,7 @@ package config import ( + "slices" "time" "github.com/kubescape/node-agent/pkg/exporters" @@ -26,6 +27,8 @@ type Config struct { EnableNodeProfile bool `mapstructure:"nodeProfileServiceEnabled"` NodeProfileInterval time.Duration `mapstructure:"nodeProfileInterval"` EnableSeccomp bool `mapstructure:"seccompServiceEnabled"` + ExcludeNamespaces []string `mapstructure:"excludeNamespaces"` + IncludeNamespaces []string `mapstructure:"includeNamespaces"` } // LoadConfig reads configuration from file or environment variables. @@ -49,3 +52,18 @@ func LoadConfig(path string) (Config, error) { err = viper.Unmarshal(&config) return config, err } + +func (c *Config) SkipNamespace(ns string) bool { + if includeNamespaces := c.IncludeNamespaces; len(includeNamespaces) > 0 { + if !slices.Contains(includeNamespaces, ns) { + // skip ns not in IncludeNamespaces + return true + } + } else if excludeNamespaces := c.ExcludeNamespaces; len(excludeNamespaces) > 0 { + if slices.Contains(excludeNamespaces, ns) { + // skip ns in ExcludeNamespaces + return true + } + } + return false +} diff --git a/pkg/containerwatcher/v1/container_watcher_private.go b/pkg/containerwatcher/v1/container_watcher_private.go index 74e7921d99..cacd83207e 100644 --- a/pkg/containerwatcher/v1/container_watcher_private.go +++ b/pkg/containerwatcher/v1/container_watcher_private.go @@ -22,7 +22,7 @@ const ( func (ch *IGContainerWatcher) containerCallback(notif containercollection.PubSubEvent) { - // do not trace the node-agent pod + // check if the container should be ignored if ch.ignoreContainer(notif.Container.K8s.Namespace, notif.Container.K8s.PodName) { // avoid loops when the container is being removed if notif.Type == containercollection.EventTypeAddContainer { @@ -363,5 +363,10 @@ func (ch *IGContainerWatcher) unregisterContainer(container *containercollection } func (ch *IGContainerWatcher) ignoreContainer(namespace, name string) bool { - return name == ch.podName && namespace == ch.namespace + // do not trace the node-agent pod + if name == ch.podName && namespace == ch.namespace { + return true + } + // check if config excludes the namespace + return ch.cfg.SkipNamespace(namespace) } diff --git a/pkg/objectcache/applicationactivitiescache_interface.go b/pkg/objectcache/applicationactivitiescache_interface.go deleted file mode 100644 index 18e63a0589..0000000000 --- a/pkg/objectcache/applicationactivitiescache_interface.go +++ /dev/null @@ -1,16 +0,0 @@ -package objectcache - -import "github.com/kubescape/storage/pkg/apis/softwarecomposition/v1beta1" - -type ApplicationActivityCache interface { - GetApplicationActivity(namespace, name string) *v1beta1.ApplicationActivity -} - -var _ ApplicationActivityCache = (*ApplicationActivityCacheMock)(nil) - -type ApplicationActivityCacheMock struct { -} - -func (ap *ApplicationActivityCacheMock) GetApplicationActivity(_, _ string) *v1beta1.ApplicationActivity { - return nil -} diff --git a/pkg/storage/storage_interface.go b/pkg/storage/storage_interface.go index ae99bf29ed..29fd5d2b4e 100644 --- a/pkg/storage/storage_interface.go +++ b/pkg/storage/storage_interface.go @@ -6,7 +6,6 @@ import ( type StorageClient interface { CreateApplicationActivity(activity *v1beta1.ApplicationActivity, namespace string) error - GetApplicationActivity(namespace, name string) (*v1beta1.ApplicationActivity, error) CreateApplicationProfile(profile *v1beta1.ApplicationProfile, namespace string) error PatchApplicationProfile(name, namespace string, patch []byte, channel chan error) error GetApplicationProfile(namespace, name string) (*v1beta1.ApplicationProfile, error) diff --git a/pkg/storage/storage_mock.go b/pkg/storage/storage_mock.go index ea354c69cb..2bf7147c5d 100644 --- a/pkg/storage/storage_mock.go +++ b/pkg/storage/storage_mock.go @@ -24,14 +24,6 @@ type StorageHttpClientMock struct { failedOnce bool } -func (sc *StorageHttpClientMock) GetApplicationActivity(_, _ string) (*spdxv1beta1.ApplicationActivity, error) { - return &spdxv1beta1.ApplicationActivity{ - Spec: spdxv1beta1.ApplicationActivitySpec{ - Syscalls: []string{"open"}, - }, - }, nil -} - var _ StorageClient = (*StorageHttpClientMock)(nil) func CreateSyftSBOMStorageHttpClientMock(sbom spdxv1beta1.SBOMSyft) *StorageHttpClientMock { diff --git a/pkg/storage/v1/storage.go b/pkg/storage/v1/storage.go index f162962b87..98d47947d8 100644 --- a/pkg/storage/v1/storage.go +++ b/pkg/storage/v1/storage.go @@ -130,10 +130,6 @@ func (sc Storage) CreateApplicationActivity(activity *v1beta1.ApplicationActivit return nil } -func (sc Storage) GetApplicationActivity(namespace, name string) (*v1beta1.ApplicationActivity, error) { - return sc.StorageClient.ApplicationActivities(namespace).Get(context.Background(), name, metav1.GetOptions{}) -} - func (sc Storage) CreateFilteredSBOM(SBOM *v1beta1.SBOMSyftFiltered) error { _, err := sc.StorageClient.SBOMSyftFiltereds(sc.namespace).Create(context.Background(), SBOM, metav1.CreateOptions{}) if err != nil { diff --git a/pkg/watcher/dynamicwatcher/watch.go b/pkg/watcher/dynamicwatcher/watch.go index 17060fb168..44ce71e4b6 100644 --- a/pkg/watcher/dynamicwatcher/watch.go +++ b/pkg/watcher/dynamicwatcher/watch.go @@ -7,9 +7,12 @@ import ( "os" "time" + "github.com/kubescape/node-agent/pkg/config" "github.com/kubescape/node-agent/pkg/k8sclient" "github.com/kubescape/node-agent/pkg/watcher" "github.com/kubescape/node-agent/pkg/watcher/cooldownqueue" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/tools/pager" "github.com/cenkalti/backoff/v4" @@ -36,15 +39,17 @@ type WatchHandler struct { resources map[string]watcher.WatchResource eventQueues map[string]*cooldownqueue.CooldownQueue handlers []watcher.Watcher + cfg config.Config } var errWatchClosed = errors.New("watch channel closed") -func NewWatchHandler(k8sClient k8sclient.K8sClientInterface) *WatchHandler { +func NewWatchHandler(k8sClient k8sclient.K8sClientInterface, cfg config.Config) *WatchHandler { return &WatchHandler{ k8sClient: k8sClient, resources: make(map[string]watcher.WatchResource), eventQueues: make(map[string]*cooldownqueue.CooldownQueue), + cfg: cfg, } } @@ -152,7 +157,10 @@ func (wh *WatchHandler) watchRetry(ctx context.Context, res schema.GroupVersionR if event.Type == watch.Error { return fmt.Errorf("watch error: %s", event.Object) } - + pod := event.Object.(*unstructured.Unstructured) + if wh.cfg.SkipNamespace(pod.GetNamespace()) { + continue + } eventQueue.Enqueue(event) } }, newBackOff(), func(err error, d time.Duration) { @@ -172,20 +180,25 @@ func (wh *WatchHandler) watchRetry(ctx context.Context, res schema.GroupVersionR func (wh *WatchHandler) getExistingStorageObjects(ctx context.Context, res schema.GroupVersionResource, watchOpts metav1.ListOptions) (string, error) { logger.L().Debug("WatchHandler - getting existing objects from storage", helpers.String("resource", res.Resource)) - list, err := wh.k8sClient.GetDynamicClient().Resource(res).Namespace("").List(context.Background(), watchOpts) - if err != nil { - return "", fmt.Errorf("list resources: %w", err) - } - logger.L().Debug("WatchHandler - got existing objects from storage", helpers.String("resource", res.Resource), helpers.Int("count", len(list.Items))) - for i := range list.Items { + list := pager.New(func(ctx context.Context, opts metav1.ListOptions) (runtime.Object, error) { + return wh.k8sClient.GetDynamicClient().Resource(res).Namespace("").List(ctx, opts) + }) + var resourceVersion string + if err := list.EachListItem(context.Background(), watchOpts, func(obj runtime.Object) error { + pod := obj.(*unstructured.Unstructured) + resourceVersion = pod.GetResourceVersion() + if wh.cfg.SkipNamespace(pod.GetNamespace()) { + return nil + } for _, handler := range wh.handlers { - var l unstructured.Unstructured - l = list.Items[i] - handler.AddHandler(ctx, &l) + handler.AddHandler(ctx, pod) } + return nil + }); err != nil { + return "", fmt.Errorf("list resources: %w", err) } // set resource version to watch from - return list.GetResourceVersion(), nil + return resourceVersion, nil } func newBackOff() backoff.BackOff { diff --git a/pkg/watcher/dynamicwatcher/watch_test.go b/pkg/watcher/dynamicwatcher/watch_test.go index 6769233767..42f338f05b 100644 --- a/pkg/watcher/dynamicwatcher/watch_test.go +++ b/pkg/watcher/dynamicwatcher/watch_test.go @@ -7,6 +7,7 @@ import ( "time" "github.com/kubescape/node-agent/mocks" + "github.com/kubescape/node-agent/pkg/config" "github.com/kubescape/node-agent/pkg/watcher" "github.com/kubescape/storage/pkg/apis/softwarecomposition/v1beta1" @@ -74,7 +75,7 @@ func startTest(t *testing.T, tc testObj) { k8sClient := k8sinterface.NewKubernetesApiMock() k8sClient.DynamicClient = dynamicfake.NewSimpleDynamicClient(scheme.Scheme, tc.preCreatedObjects...) - wh := NewWatchHandler(k8sClient) + wh := NewWatchHandler(k8sClient, config.Config{}) wh.AddAdaptor(a)