Skip to content

Commit 9405170

Browse files
authored
Merge pull request #253 from kubescape/sbom
add support for node-agent generated SBOMs
2 parents 9803bd6 + f2b69a4 commit 9405170

11 files changed

+460
-101
lines changed

config/config.go

+7
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ type Capabilities struct {
2929
Otel string `json:"otel"`
3030
Relevancy string `json:"relevancy"`
3131
RuntimeObservability string `json:"runtimeObservability"`
32+
NodeSbomGeneration string `json:"nodeSbomGeneration"`
3233
Seccomp string `json:"seccomp"`
3334
VulnerabilityScan string `json:"vulnerabilityScan"`
3435
AdmissionController string `json:"admissionController"`
@@ -116,7 +117,9 @@ type IConfig interface {
116117
GatewayWebsocketURL() string
117118
ConcurrencyWorkers() int
118119
Components() Components
120+
AdmissionControllerEnabled() bool
119121
ContinuousScanEnabled() bool
122+
NodeSbomGenerationEnabled() bool
120123
CleanUpRoutineInterval() time.Duration
121124
MatchingRulesFilename() string
122125
TriggerSecurityFramework() bool
@@ -157,6 +160,10 @@ func (c *OperatorConfig) AdmissionControllerEnabled() bool {
157160
return c.components.Capabilities.AdmissionController == "enable"
158161
}
159162

163+
func (c *OperatorConfig) NodeSbomGenerationEnabled() bool {
164+
return c.components.Capabilities.NodeSbomGeneration == "enable"
165+
}
166+
160167
func (c *OperatorConfig) KubevulnURL() string {
161168
return c.clusterConfig.KubevulnURL
162169
}

mainhandler/handlerequests.go

+10-6
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ func (mainHandler *MainHandler) HandleWatchers(ctx context.Context) {
156156
logger.L().Ctx(ctx).Fatal(fmt.Sprintf("Unable to initialize the storage client: %v", err))
157157
}
158158
eventQueue := watcher.NewCooldownQueue()
159-
watchHandler := watcher.NewWatchHandler(ctx, mainHandler.config, mainHandler.k8sAPI, ksStorageClient, eventQueue)
159+
watchHandler := watcher.NewWatchHandler(mainHandler.config, mainHandler.k8sAPI, ksStorageClient, eventQueue)
160160

161161
commandWatchHandler := watcher.NewCommandWatchHandler(mainHandler.k8sAPI, mainHandler.config)
162162
registryCommandsHandler := watcher.NewRegistryCommandsHandler(ctx, mainHandler.k8sAPI, commandWatchHandler, mainHandler.config)
@@ -168,7 +168,11 @@ func (mainHandler *MainHandler) HandleWatchers(ctx context.Context) {
168168
waitFunc(mainHandler.config)
169169

170170
// start watching
171-
go watchHandler.PodWatch(ctx, mainHandler.eventWorkerPool)
171+
if mainHandler.config.NodeSbomGenerationEnabled() {
172+
go watchHandler.SBOMWatch(ctx, mainHandler.eventWorkerPool)
173+
} else {
174+
go watchHandler.PodWatch(ctx, mainHandler.eventWorkerPool)
175+
}
172176
go watchHandler.SBOMFilteredWatch(ctx, mainHandler.eventWorkerPool)
173177
go commandWatchHandler.CommandWatch(ctx)
174178
}
@@ -240,7 +244,7 @@ func (actionHandler *ActionHandler) runCommand(ctx context.Context, sessionObj *
240244
case apis.TypeScanImages:
241245
return actionHandler.scanImage(ctx, sessionObj)
242246
case utils.CommandScanFilteredSBOM:
243-
actionHandler.scanFilteredSBOM(ctx, sessionObj)
247+
return actionHandler.scanFilteredSBOM(ctx, sessionObj)
244248
case apis.TypeRunKubescape, apis.TypeRunKubescapeJob:
245249
return actionHandler.kubescapeScan(ctx)
246250
case apis.TypeSetKubescapeCronJob:
@@ -368,7 +372,7 @@ func (mainHandler *MainHandler) HandleImageScanningScopedRequest(ctx context.Con
368372
return
369373
}
370374

371-
labels := sessionObj.Command.GetLabels()
375+
lbls := sessionObj.Command.GetLabels()
372376
fields := sessionObj.Command.GetFieldSelector()
373377
namespaces, err := mainHandler.getNamespaces(ctx, sessionObj)
374378
if err != nil {
@@ -377,12 +381,12 @@ func (mainHandler *MainHandler) HandleImageScanningScopedRequest(ctx context.Con
377381
return
378382
}
379383

380-
info := fmt.Sprintf("%s: id: '%s', namespaces: '%v', labels: '%v', fieldSelector: '%v'", sessionObj.Command.CommandName, sessionObj.Command.GetID(), namespaces, labels, fields)
384+
info := fmt.Sprintf("%s: id: '%s', namespaces: '%v', labels: '%v', fieldSelector: '%v'", sessionObj.Command.CommandName, sessionObj.Command.GetID(), namespaces, lbls, fields)
381385
logger.L().Info(info)
382386
sessionObj.Reporter.SendDetails(info, mainHandler.sendReport)
383387

384388
listOptions := metav1.ListOptions{
385-
LabelSelector: k8sinterface.SelectorToString(labels),
389+
LabelSelector: k8sinterface.SelectorToString(lbls),
386390
FieldSelector: k8sinterface.SelectorToString(fields),
387391
}
388392

mainhandler/vulnscan.go

+4-5
Original file line numberDiff line numberDiff line change
@@ -438,10 +438,7 @@ func (actionHandler *ActionHandler) scanImage(ctx context.Context, sessionObj *u
438438
return errors.New("kubevuln is not enabled")
439439
}
440440

441-
pod, ok := actionHandler.command.Args[utils.ArgsPod].(*corev1.Pod)
442-
if !ok || pod == nil {
443-
return fmt.Errorf("failed to get pod for image %s", actionHandler.command.Args[utils.ArgsPod])
444-
}
441+
pod, _ := actionHandler.command.Args[utils.ArgsPod].(*corev1.Pod)
445442

446443
containerData, ok := actionHandler.command.Args[utils.ArgsContainerData].(*utils.ContainerData)
447444
if !ok {
@@ -607,7 +604,9 @@ func sendWorkloadWithCredentials(ctx context.Context, scanUrl *url.URL, command
607604
if !ok {
608605
logger.L().Debug("Not an image scan command")
609606
} else {
610-
instanceID = *imageScanCommand.InstanceID
607+
if imageScanCommand.InstanceID != nil {
608+
instanceID = *imageScanCommand.InstanceID
609+
}
611610
}
612611

613612
if err != nil {

watcher/filteredsbomwatcher.go

+1-11
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,6 @@ import (
1919
"k8s.io/apimachinery/pkg/watch"
2020
)
2121

22-
const retryInterval = 1 * time.Second
23-
24-
var (
25-
ErrMissingWLID = fmt.Errorf("missing WLID")
26-
ErrMissingSlug = fmt.Errorf("missing slug")
27-
ErrMissingImageTag = fmt.Errorf("missing image ID")
28-
ErrMissingImageID = fmt.Errorf("missing image tag")
29-
ErrMissingContainerName = fmt.Errorf("missing container name")
30-
)
31-
3222
// SBOMFilteredWatch watches and processes changes on Filtered SBOMs
3323
func (wh *WatchHandler) SBOMFilteredWatch(ctx context.Context, workerPool *ants.PoolWithFunc) {
3424
inputEvents := make(chan watch.Event)
@@ -64,7 +54,7 @@ func (wh *WatchHandler) SBOMFilteredWatch(ctx context.Context, workerPool *ants.
6454
}
6555
case cmd, ok := <-cmdCh:
6656
if ok {
67-
utils.AddCommandToChannel(ctx, wh.cfg, cmd, workerPool)
57+
_ = utils.AddCommandToChannel(ctx, wh.cfg, cmd, workerPool)
6858
} else {
6959
notifyWatcherDown(sbomWatcherUnavailable)
7060
}

watcher/filteredsbomwatcher_test.go

+1-47
Original file line numberDiff line numberDiff line change
@@ -20,52 +20,6 @@ import (
2020
k8sfake "k8s.io/client-go/kubernetes/fake"
2121
)
2222

23-
func TestNewWatchHandlerProducesValidResult(t *testing.T) {
24-
tt := []struct {
25-
imageIDsToWLIDSsMap map[string][]string
26-
expectedIWMap map[string][]string
27-
name string
28-
}{
29-
{
30-
name: "Creating with provided empty map returns matching empty map",
31-
imageIDsToWLIDSsMap: map[string][]string{},
32-
expectedIWMap: map[string][]string{},
33-
},
34-
{
35-
name: "Creating with provided nil map returns matching empty map",
36-
imageIDsToWLIDSsMap: nil,
37-
expectedIWMap: map[string][]string{},
38-
},
39-
{
40-
name: "Creating with provided non-empty map returns matching map",
41-
imageIDsToWLIDSsMap: map[string][]string{
42-
"imageid-01": {"wlid-01"},
43-
},
44-
expectedIWMap: map[string][]string{
45-
"imageid-01": {"wlid-01"},
46-
},
47-
},
48-
}
49-
50-
for _, tc := range tt {
51-
t.Run(tc.name, func(t *testing.T) {
52-
ctx := context.TODO()
53-
clusterConfig := utilsmetadata.ClusterConfig{}
54-
cfg, err := config.LoadConfig("../configuration")
55-
assert.NoError(t, err)
56-
operatorConfig := config.NewOperatorConfig(config.CapabilitiesConfig{}, clusterConfig, &beUtils.Credentials{}, "", cfg)
57-
58-
k8sClient := k8sfake.NewSimpleClientset()
59-
k8sAPI := utils.NewK8sInterfaceFake(k8sClient)
60-
storageClient := kssfake.NewSimpleClientset()
61-
62-
wh := NewWatchHandler(ctx, operatorConfig, k8sAPI, storageClient, nil)
63-
64-
assert.NotNilf(t, wh, "Constructing should create a non-nil object")
65-
})
66-
}
67-
}
68-
6923
func TestHandleSBOMFilteredEvents(t *testing.T) {
7024
tt := []struct {
7125
name string
@@ -218,7 +172,7 @@ func TestHandleSBOMFilteredEvents(t *testing.T) {
218172
cmdCh := make(chan *apis.Command)
219173
errorCh := make(chan error)
220174

221-
wh := NewWatchHandler(ctx, operatorConfig, k8sAPI, storageClient, nil)
175+
wh := NewWatchHandler(operatorConfig, k8sAPI, storageClient, nil)
222176

223177
go wh.HandleSBOMFilteredEvents(inputEvents, cmdCh, errorCh)
224178

watcher/podwatcher.go

-29
Original file line numberDiff line numberDiff line change
@@ -2,55 +2,26 @@ package watcher
22

33
import (
44
"context"
5-
"errors"
65
"time"
76

8-
mapset "github.com/deckarep/golang-set/v2"
97
"github.com/kubescape/k8s-interface/workloadinterface"
108
"k8s.io/apimachinery/pkg/runtime"
119
"k8s.io/client-go/tools/pager"
1210

1311
"github.com/armosec/armoapi-go/apis"
14-
"github.com/goradd/maps"
1512
"github.com/kubescape/go-logger"
1613
"github.com/kubescape/go-logger/helpers"
1714
"github.com/kubescape/k8s-interface/instanceidhandler"
1815
instanceidhandlerv1 "github.com/kubescape/k8s-interface/instanceidhandler/v1"
1916
"github.com/kubescape/k8s-interface/k8sinterface"
20-
"github.com/kubescape/operator/config"
2117
"github.com/kubescape/operator/utils"
22-
kssc "github.com/kubescape/storage/pkg/generated/clientset/versioned"
2318
"github.com/panjf2000/ants/v2"
2419

2520
core1 "k8s.io/api/core/v1"
2621
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2722
"k8s.io/apimachinery/pkg/watch"
2823
)
2924

30-
var (
31-
ErrUnsupportedObject = errors.New("unsupported object type")
32-
ErrUnknownImageHash = errors.New("unknown image hash")
33-
)
34-
35-
type WatchHandler struct {
36-
SlugToImageID maps.SafeMap[string, string] // map of <Slug> : string <image ID>
37-
WlidAndImageID mapset.Set[string] // set of <wlid+imageID>
38-
storageClient kssc.Interface
39-
cfg config.IConfig
40-
k8sAPI *k8sinterface.KubernetesApi
41-
eventQueue *CooldownQueue
42-
}
43-
44-
// NewWatchHandler creates a new WatchHandler, initializes the maps and returns it
45-
func NewWatchHandler(ctx context.Context, cfg config.IConfig, k8sAPI *k8sinterface.KubernetesApi, storageClient kssc.Interface, eventQueue *CooldownQueue) *WatchHandler {
46-
return &WatchHandler{
47-
storageClient: storageClient,
48-
k8sAPI: k8sAPI,
49-
cfg: cfg,
50-
WlidAndImageID: mapset.NewSet[string](),
51-
eventQueue: eventQueue,
52-
}
53-
}
5425
func (wh *WatchHandler) PodWatch(ctx context.Context, workerPool *ants.PoolWithFunc) error {
5526
watchOpts := v1.ListOptions{
5627
Watch: true,

watcher/podwatcher_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ func TestPodWatch(t *testing.T) {
113113
k8sAPI.DynamicClient = dynClient
114114

115115
eventQueue := NewCooldownQueue()
116-
wh := NewWatchHandler(ctx, operatorConfig, k8sAPI, storageClient, eventQueue)
116+
wh := NewWatchHandler(operatorConfig, k8sAPI, storageClient, eventQueue)
117117

118118
resourcesCreatedWg := &sync.WaitGroup{}
119119
resourcesCreatedWg.Add(tc.numberOfExpectedCommands)
@@ -371,7 +371,7 @@ func Test_handlePodWatcher(t *testing.T) {
371371
k8sAPI.DynamicClient = dynClient
372372

373373
eventQueue := NewCooldownQueue()
374-
wh := NewWatchHandler(ctx, operatorConfig, k8sAPI, storageClient, eventQueue)
374+
wh := NewWatchHandler(operatorConfig, k8sAPI, storageClient, eventQueue)
375375

376376
resourcesCreatedWg := &sync.WaitGroup{}
377377
resourcesCreatedWg.Add(len(tc.expectedCommands))
@@ -453,7 +453,7 @@ func Test_listPods(t *testing.T) {
453453
storageClient := kssfake.NewSimpleClientset()
454454
eventQueue := NewCooldownQueue()
455455

456-
wh := NewWatchHandler(ctx, operatorConfig, k8sAPI, storageClient, eventQueue)
456+
wh := NewWatchHandler(operatorConfig, k8sAPI, storageClient, eventQueue)
457457
resourcesCreatedWg := &sync.WaitGroup{}
458458

459459
for i := range tc.pods {

0 commit comments

Comments
 (0)