Skip to content

Commit

Permalink
generate SBOM from node-local files
Browse files Browse the repository at this point in the history
Signed-off-by: Matthias Bertschy <[email protected]>
  • Loading branch information
matthyx committed Oct 7, 2024
1 parent 8a37973 commit a189d6b
Show file tree
Hide file tree
Showing 13 changed files with 727 additions and 115 deletions.
2 changes: 1 addition & 1 deletion build/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ RUN --mount=target=. \
--mount=type=cache,target=/go/pkg \
GOOS=$TARGETOS GOARCH=$TARGETARCH go build -o /out/node-agent .

FROM gcr.io/distroless/static-debian12:latest
FROM gcr.io/distroless/static-debian12:debug

COPY --from=builder /out/node-agent /usr/bin/node-agent

Expand Down
133 changes: 106 additions & 27 deletions go.mod

Large diffs are not rendered by default.

369 changes: 311 additions & 58 deletions go.sum

Large diffs are not rendered by default.

46 changes: 33 additions & 13 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"strings"
"syscall"

containercollection "github.com/inspektor-gadget/inspektor-gadget/pkg/container-collection"
"github.com/kubescape/node-agent/pkg/applicationprofilemanager"
applicationprofilemanagerv1 "github.com/kubescape/node-agent/pkg/applicationprofilemanager/v1"
"github.com/kubescape/node-agent/pkg/config"
Expand Down Expand Up @@ -39,6 +40,8 @@ import (
"github.com/kubescape/node-agent/pkg/rulemanager"
rulemanagerv1 "github.com/kubescape/node-agent/pkg/rulemanager/v1"
"github.com/kubescape/node-agent/pkg/sbomhandler/syfthandler"
"github.com/kubescape/node-agent/pkg/sbommanager"
sbommanagerv1 "github.com/kubescape/node-agent/pkg/sbommanager/v1"
"github.com/kubescape/node-agent/pkg/seccompmanager"
seccompmanagerv1 "github.com/kubescape/node-agent/pkg/seccompmanager/v1"
"github.com/kubescape/node-agent/pkg/storage/v1"
Expand Down Expand Up @@ -126,10 +129,8 @@ func main() {
prometheusExporter = metricsmanager.NewMetricsMock()
}

nodeName := os.Getenv(config.NodeNameEnvVar)

// Detect the container containerRuntime of the node
containerRuntime, err := utils.DetectContainerRuntimeViaK8sAPI(ctx, k8sClient, nodeName)
containerRuntime, err := utils.DetectContainerRuntimeViaK8sAPI(ctx, k8sClient, cfg.NodeName)
if err != nil {
logger.L().Ctx(ctx).Fatal("error detecting the container runtime", helpers.Error(err))
}
Expand All @@ -139,7 +140,7 @@ func main() {
// Create watchers
dWatcher := dynamicwatcher.NewWatchHandler(k8sClient, storageClient.StorageClient, cfg.SkipNamespace)
// create k8sObject cache
k8sObjectCache, err := k8scache.NewK8sObjectCache(nodeName, k8sClient)
k8sObjectCache, err := k8scache.NewK8sObjectCache(cfg.NodeName, k8sClient)
if err != nil {
logger.L().Ctx(ctx).Fatal("error creating K8sObjectCache", helpers.Error(err))
}
Expand Down Expand Up @@ -214,16 +215,16 @@ func main() {

if cfg.EnableRuntimeDetection {
// create ruleBinding cache
ruleBindingCache := rulebindingcachev1.NewCache(nodeName, k8sClient)
ruleBindingCache := rulebindingcachev1.NewCache(cfg.NodeName, k8sClient)
dWatcher.AddAdaptor(ruleBindingCache)

ruleBindingNotify = make(chan rulebinding.RuleBindingNotify, 100)
ruleBindingCache.AddNotifier(&ruleBindingNotify)

apc := applicationprofilecache.NewApplicationProfileCache(nodeName, storageClient.StorageClient, cfg.MaxDelaySeconds)
apc := applicationprofilecache.NewApplicationProfileCache(cfg.NodeName, storageClient.StorageClient, cfg.MaxDelaySeconds)
dWatcher.AddAdaptor(apc)

nnc := networkneighborhoodcache.NewNetworkNeighborhoodCache(nodeName, storageClient.StorageClient, cfg.MaxDelaySeconds)
nnc := networkneighborhoodcache.NewNetworkNeighborhoodCache(cfg.NodeName, storageClient.StorageClient, cfg.MaxDelaySeconds)
dWatcher.AddAdaptor(nnc)

dc := dnscache.NewDnsCache(dnsResolver)
Expand All @@ -232,10 +233,10 @@ func main() {
objCache = objectcachev1.NewObjectCache(k8sObjectCache, apc, nnc, dc)

// create exporter
exporter := exporters.InitExporters(cfg.Exporters, clusterData.ClusterName, nodeName)
exporter := exporters.InitExporters(cfg.Exporters, clusterData.ClusterName, cfg.NodeName)

// create runtimeDetection managers
ruleManager, err = rulemanagerv1.CreateRuleManager(ctx, cfg, k8sClient, ruleBindingCache, objCache, exporter, prometheusExporter, nodeName, clusterData.ClusterName)
ruleManager, err = rulemanagerv1.CreateRuleManager(ctx, cfg, k8sClient, ruleBindingCache, objCache, exporter, prometheusExporter, cfg.NodeName, clusterData.ClusterName)
if err != nil {
logger.L().Ctx(ctx).Fatal("error creating RuleManager", helpers.Error(err))
}
Expand All @@ -250,7 +251,7 @@ func main() {
var profileManager nodeprofilemanager.NodeProfileManagerClient
if cfg.EnableNodeProfile {
// FIXME validate the HTTPExporterConfig before we use it ?
profileManager = nodeprofilemanagerv1.NewNodeProfileManager(cfg, *clusterData, nodeName, k8sObjectCache, relevancyManager, ruleManager)
profileManager = nodeprofilemanagerv1.NewNodeProfileManager(cfg, *clusterData, cfg.NodeName, k8sObjectCache, relevancyManager, ruleManager)
} else {
profileManager = nodeprofilemanager.NewNodeProfileManagerMock()
}
Expand All @@ -259,17 +260,36 @@ func main() {
var malwareManager malwaremanager.MalwareManagerClient
if cfg.EnableMalwareDetection {
// create exporter
exporter := exporters.InitExporters(cfg.Exporters, clusterData.ClusterName, nodeName)
malwareManager, err = malwaremanagerv1.CreateMalwareManager(cfg, k8sClient, nodeName, clusterData.ClusterName, exporter, prometheusExporter)
exporter := exporters.InitExporters(cfg.Exporters, clusterData.ClusterName, cfg.NodeName)
malwareManager, err = malwaremanagerv1.CreateMalwareManager(cfg, k8sClient, cfg.NodeName, clusterData.ClusterName, exporter, prometheusExporter)
if err != nil {
logger.L().Ctx(ctx).Fatal("error creating MalwareManager", helpers.Error(err))
}
} else {
malwareManager = malwaremanager.CreateMalwareManagerMock()
}

// Create the IG k8sClient
igK8sClient, err := containercollection.NewK8sClient(cfg.NodeName)
if err != nil {
logger.L().Fatal("error creating IG Kubernetes client", helpers.Error(err))
}
defer igK8sClient.Close()
logger.L().Info("IG Kubernetes client created", helpers.Interface("client", igK8sClient))

// Create the SBOM manager
var sbomManager sbommanager.SbomManagerClient
if cfg.EnableSbomGeneration {
sbomManager, err = sbommanagerv1.CreateSbomManager(ctx, cfg, igK8sClient.SocketPath)
if err != nil {
logger.L().Ctx(ctx).Fatal("error creating SbomManager", helpers.Error(err))
}
} else {
sbomManager = sbommanager.CreateSbomManagerMock()
}

// Create the container handler
mainHandler, err := containerwatcher.CreateIGContainerWatcher(cfg, applicationProfileManager, k8sClient, relevancyManager, networkManagerClient, dnsManagerClient, prometheusExporter, ruleManager, malwareManager, preRunningContainersIDs, &ruleBindingNotify, containerRuntime, nil)
mainHandler, err := containerwatcher.CreateIGContainerWatcher(cfg, applicationProfileManager, k8sClient, igK8sClient, relevancyManager, networkManagerClient, dnsManagerClient, prometheusExporter, ruleManager, malwareManager, sbomManager, preRunningContainersIDs, &ruleBindingNotify, containerRuntime, nil)
if err != nil {
logger.L().Ctx(ctx).Fatal("error creating the container watcher", helpers.Error(err))
}
Expand Down
8 changes: 8 additions & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package config

import (
"os"
"slices"
"time"

Expand Down Expand Up @@ -32,6 +33,10 @@ type Config struct {
EnableSeccomp bool `mapstructure:"seccompServiceEnabled"`
ExcludeNamespaces []string `mapstructure:"excludeNamespaces"`
IncludeNamespaces []string `mapstructure:"includeNamespaces"`
EnableSbomGeneration bool `mapstructure:"sbomGenerationEnabled"`
NamespaceName string `mapstructure:"namespaceName"`
NodeName string `mapstructure:"nodeName"`
PodName string `mapstructure:"podName"`
}

// LoadConfig reads configuration from file or environment variables.
Expand All @@ -45,6 +50,9 @@ func LoadConfig(path string) (Config, error) {
viper.SetDefault("nodeProfileInterval", 10*time.Minute)
viper.SetDefault("maxDelaySeconds", 30)
viper.SetDefault("maxJitterPercentage", 5)
viper.SetDefault("namespaceName", os.Getenv(NamespaceEnvVar))
viper.SetDefault("nodeName", os.Getenv(NodeNameEnvVar))
viper.SetDefault("podName", os.Getenv(PodNameEnvVar))

viper.AutomaticEnv()

Expand Down
12 changes: 6 additions & 6 deletions pkg/containerwatcher/v1/container_watcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package containerwatcher
import (
"context"
"fmt"
"os"

mapset "github.com/deckarep/golang-set/v2"
"github.com/goradd/maps"
Expand Down Expand Up @@ -46,6 +45,7 @@ import (
"github.com/kubescape/node-agent/pkg/relevancymanager"
rulebinding "github.com/kubescape/node-agent/pkg/rulebindingmanager"
"github.com/kubescape/node-agent/pkg/rulemanager"
"github.com/kubescape/node-agent/pkg/sbommanager"
"github.com/kubescape/node-agent/pkg/utils"
"github.com/panjf2000/ants/v2"
)
Expand Down Expand Up @@ -79,18 +79,19 @@ type IGContainerWatcher struct {
cfg config.Config
containerSelector containercollection.ContainerSelector
ctx context.Context
nodeName string
podName string
namespace string

// Clients
applicationProfileManager applicationprofilemanager.ApplicationProfileManagerClient
igK8sClient *containercollection.K8sClient
k8sClient *k8sinterface.KubernetesApi
relevancyManager relevancymanager.RelevancyManagerClient
networkManager networkmanager.NetworkManagerClient
dnsManager dnsmanager.DNSManagerClient
ruleManager rulemanager.RuleManagerClient
malwareManager malwaremanager.MalwareManagerClient
sbomManager sbommanager.SbomManagerClient
// IG Collections
containerCollection *containercollection.ContainerCollection
tracerCollection *tracercollection.TracerCollection
Expand Down Expand Up @@ -149,7 +150,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, thirdPartyEventReceivers *maps.SafeMap[utils.EventType, mapset.Set[containerwatcher.EventReceiver]]) (*IGContainerWatcher, error) {
func CreateIGContainerWatcher(cfg config.Config, applicationProfileManager applicationprofilemanager.ApplicationProfileManagerClient, k8sClient *k8sinterface.KubernetesApi, igK8sClient *containercollection.K8sClient, relevancyManager relevancymanager.RelevancyManagerClient, networkManagerClient networkmanager.NetworkManagerClient, dnsManagerClient dnsmanager.DNSManagerClient, metrics metricsmanager.MetricsManager, ruleManager rulemanager.RuleManagerClient, malwareManager malwaremanager.MalwareManagerClient, sbomManager sbommanager.SbomManagerClient, 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
Expand Down Expand Up @@ -378,18 +379,17 @@ func CreateIGContainerWatcher(cfg config.Config, applicationProfileManager appli
// Configuration
cfg: cfg,
containerSelector: containercollection.ContainerSelector{}, // Empty selector to get all containers
nodeName: os.Getenv(config.NodeNameEnvVar),
podName: os.Getenv(config.PodNameEnvVar),
namespace: os.Getenv(config.NamespaceEnvVar),

// Clients
applicationProfileManager: applicationProfileManager,
igK8sClient: igK8sClient,
k8sClient: k8sClient,
relevancyManager: relevancyManager,
networkManager: networkManagerClient,
dnsManager: dnsManagerClient,
ruleManager: ruleManager,
malwareManager: malwareManager,
sbomManager: sbomManager,
// IG Collections
containerCollection: containerCollection,
tracerCollection: tracerCollection,
Expand Down
10 changes: 3 additions & 7 deletions pkg/containerwatcher/v1/container_watcher_private.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ func (ch *IGContainerWatcher) startContainerCollection(ctx context.Context) erro
ch.networkManager.ContainerCallback,
ch.malwareManager.ContainerCallback,
ch.ruleManager.ContainerCallback,
ch.sbomManager.ContainerCallback,
}

for receiver := range ch.thirdPartyContainerReceivers.Iter() {
Expand Down Expand Up @@ -116,7 +117,7 @@ func (ch *IGContainerWatcher) startContainerCollection(ctx context.Context) erro
containercollection.WithTracerCollection(ch.tracerCollection),

// Enrich those containers with data from the Kubernetes API
containercollection.WithKubernetesEnrichment(ch.nodeName, ch.k8sClient.K8SConfig),
containercollection.WithKubernetesEnrichment(ch.cfg.NodeName, ch.k8sClient.K8SConfig),
}

// Initialize the container collection
Expand All @@ -131,13 +132,8 @@ func (ch *IGContainerWatcher) startContainerCollection(ctx context.Context) erro
}

func (ch *IGContainerWatcher) startRunningContainers() error {
k8sClient, err := containercollection.NewK8sClient(ch.nodeName)
if err != nil {
logger.L().Fatal("creating IG Kubernetes client", helpers.Error(err))
}
defer k8sClient.Close()
for n := range *ch.ruleBindingPodNotify {
ch.addRunningContainers(k8sClient, &n)
ch.addRunningContainers(ch.igK8sClient, &n)
}
return nil
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/containerwatcher/v1/dns.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func (ch *IGContainerWatcher) startDNSTracing() error {
return fmt.Errorf("adding tracer: %w", err)
}

tracerDns, err := tracerdns.NewTracer()
tracerDns, err := tracerdns.NewTracer(&tracerdns.Config{})
if err != nil {
return fmt.Errorf("creating tracer: %w", err)
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/containerwatcher/v1/open_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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, nil)
mainHandler, err := CreateIGContainerWatcher(cfg, nil, nil, nil, relevancyManager, nil, nil, mockExporter, nil, nil, nil, nil, nil, nil, nil)
assert.NoError(b, err)
event := &traceropentype.Event{
Event: types.Event{
Expand Down
7 changes: 7 additions & 0 deletions pkg/sbommanager/sbom_manager_interface.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package sbommanager

import containercollection "github.com/inspektor-gadget/inspektor-gadget/pkg/container-collection"

type SbomManagerClient interface {
ContainerCallback(notif containercollection.PubSubEvent)
}
15 changes: 15 additions & 0 deletions pkg/sbommanager/sbom_manager_mock.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package sbommanager

import containercollection "github.com/inspektor-gadget/inspektor-gadget/pkg/container-collection"

type SbomManagerMock struct{}

var _ SbomManagerClient = (*SbomManagerMock)(nil)

func CreateSbomManagerMock() *SbomManagerMock {
return &SbomManagerMock{}
}

func (s SbomManagerMock) ContainerCallback(_ containercollection.PubSubEvent) {
// noop
}
Loading

0 comments on commit a189d6b

Please sign in to comment.