Skip to content

Commit 8dd5c88

Browse files
committed
add guard time when scanning naked pods
Signed-off-by: Matthias Bertschy <[email protected]>
1 parent 4617c94 commit 8dd5c88

File tree

3 files changed

+49
-6
lines changed

3 files changed

+49
-6
lines changed

config/config.go

+9
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ type Config struct {
102102
HTTPExporterConfig *exporters.HTTPExporterConfig `mapstructure:"httpExporterConfig"`
103103
ExcludeNamespaces []string `mapstructure:"excludeNamespaces"`
104104
IncludeNamespaces []string `mapstructure:"includeNamespaces"`
105+
// PodScanGuardTime is the time to wait before scanning a pod without a parent
106+
PodScanGuardTime time.Duration `mapstructure:"podScanGuardTime"`
105107
}
106108

107109
// IConfig is an interface for all config types used in the operator
@@ -121,6 +123,7 @@ type IConfig interface {
121123
KubescapeURL() string
122124
KubevulnURL() string
123125
SkipNamespace(ns string) bool
126+
GuardTime() time.Duration
124127
}
125128

126129
// OperatorConfig implements IConfig
@@ -185,6 +188,7 @@ func (c *OperatorConfig) MatchingRulesFilename() string {
185188
func (c *OperatorConfig) GatewayWebsocketURL() string {
186189
return c.clusterConfig.GatewayWebsocketURL
187190
}
191+
188192
func (c *OperatorConfig) ConcurrencyWorkers() int {
189193
return c.serviceConfig.ConcurrencyWorkers
190194
}
@@ -224,6 +228,10 @@ func (c *OperatorConfig) EventReceiverURL() string {
224228
return c.eventReceiverRestURL
225229
}
226230

231+
func (c *OperatorConfig) GuardTime() time.Duration {
232+
return c.serviceConfig.PodScanGuardTime
233+
}
234+
227235
func LoadConfig(path string) (Config, error) {
228236
viper.AddConfigPath(path)
229237
viper.SetConfigName("config")
@@ -236,6 +244,7 @@ func LoadConfig(path string) (Config, error) {
236244
viper.SetDefault("triggerSecurityFramework", false)
237245
viper.SetDefault("matchingRulesFilename", "/etc/config/matchingRules.json")
238246
viper.SetDefault("eventDeduplicationInterval", 2*time.Minute)
247+
viper.SetDefault("podScanGuardTime", 3*time.Hour)
239248

240249
viper.AutomaticEnv()
241250

mainhandler/handlerequests.go

+15-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import (
88
"time"
99

1010
"github.com/kubescape/backend/pkg/versioncheck"
11+
"github.com/kubescape/k8s-interface/workloadinterface"
12+
"k8s.io/apimachinery/pkg/runtime"
1113

1214
core1 "k8s.io/api/core/v1"
1315
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -384,12 +386,24 @@ func (mainHandler *MainHandler) HandleImageScanningScopedRequest(ctx context.Con
384386
pod.Kind = "Pod"
385387

386388
// get pod instanceIDs
387-
instanceIDs, err := instanceidhandlerv1.GenerateInstanceIDFromPod(&pod)
389+
unstructuredObj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(pod)
390+
if err != nil {
391+
logger.L().Ctx(ctx).Error("failed to convert pod to unstructured", helpers.String("pod", pod.GetName()), helpers.String("namespace", pod.GetNamespace()), helpers.Error(err))
392+
continue
393+
}
394+
wl := workloadinterface.NewWorkloadObj(unstructuredObj)
395+
instanceIDs, err := instanceidhandlerv1.GenerateInstanceID(wl)
388396
if err != nil {
389397
logger.L().Ctx(ctx).Error("failed to generate instance ID for pod", helpers.String("pod", pod.GetName()), helpers.String("namespace", pod.GetNamespace()), helpers.Error(err))
390398
continue
391399
}
392400

401+
// for naked pods, only handle if pod is older than guard time
402+
if !k8sinterface.WorkloadHasParent(wl) && time.Now().Before(pod.CreationTimestamp.Add(mainHandler.config.GuardTime())) {
403+
logger.L().Debug("naked pod younger than guard time detected, skipping scan", helpers.String("pod", pod.GetName()), helpers.String("namespace", pod.GetNamespace()))
404+
continue
405+
}
406+
393407
for _, instanceID := range instanceIDs {
394408
s, _ := instanceID.GetSlug(false)
395409
if ok := slugs[s]; ok {

watcher/podwatcher.go

+25-5
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ package watcher
33
import (
44
"context"
55
"errors"
6+
"time"
67

78
mapset "github.com/deckarep/golang-set/v2"
9+
"github.com/kubescape/k8s-interface/workloadinterface"
810
"k8s.io/apimachinery/pkg/runtime"
911
"k8s.io/client-go/tools/pager"
1012

@@ -68,26 +70,44 @@ func (wh *WatchHandler) PodWatch(ctx context.Context, workerPool *ants.PoolWithF
6870
if !ok {
6971
continue
7072
}
73+
// handle pod events
7174
switch event.Type {
7275
case watch.Modified, watch.Added:
73-
wh.handlePodWatcher(ctx, pod, workerPool)
74-
case watch.Deleted:
76+
unstructuredObj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(pod)
77+
if err != nil {
78+
logger.L().Ctx(ctx).Error("failed to convert pod to unstructured", helpers.String("pod", pod.GetName()), helpers.String("namespace", pod.GetNamespace()), helpers.Error(err))
79+
continue
80+
}
81+
wl := workloadinterface.NewWorkloadObj(unstructuredObj)
82+
if k8sinterface.WorkloadHasParent(wl) {
83+
wh.handlePodWatcher(ctx, pod, wl, workerPool)
84+
} else {
85+
// for naked pods, only handle if pod still exists after guard time
86+
logger.L().Debug("naked pod detected, delaying scan", helpers.String("pod", pod.GetName()), helpers.String("namespace", pod.GetNamespace()))
87+
time.AfterFunc(wh.cfg.GuardTime(), func() {
88+
// use get to check if pod still exists, and refresh the pod object
89+
pod, err := wh.k8sAPI.KubernetesClient.CoreV1().Pods(pod.Namespace).Get(context.Background(), pod.Name, v1.GetOptions{})
90+
if err == nil {
91+
wh.handlePodWatcher(ctx, pod, wl, workerPool)
92+
}
93+
})
94+
}
95+
default:
7596
continue
7697
}
77-
7898
}
7999
return nil
80100
}
81101

82102
// handlePodWatcher handles the pod watch events
83-
func (wh *WatchHandler) handlePodWatcher(ctx context.Context, pod *core1.Pod, workerPool *ants.PoolWithFunc) {
103+
func (wh *WatchHandler) handlePodWatcher(ctx context.Context, pod *core1.Pod, wl *workloadinterface.Workload, workerPool *ants.PoolWithFunc) {
84104

85105
// check if we need to add
86106
pod.APIVersion = "v1"
87107
pod.Kind = "Pod"
88108

89109
// get pod instanceIDs
90-
instanceIDs, err := instanceidhandlerv1.GenerateInstanceIDFromPod(pod)
110+
instanceIDs, err := instanceidhandlerv1.GenerateInstanceID(wl)
91111
if err != nil {
92112
logger.L().Ctx(ctx).Error("failed to generate instance ID for pod", helpers.String("pod", pod.GetName()), helpers.String("namespace", pod.GetNamespace()), helpers.Error(err))
93113
return

0 commit comments

Comments
 (0)