Skip to content

Commit

Permalink
Add SBOM core check
Browse files Browse the repository at this point in the history
  • Loading branch information
L3n41c committed Jan 15, 2023
1 parent 2dd3c42 commit f3fa66c
Show file tree
Hide file tree
Showing 15 changed files with 1,611 additions and 0 deletions.
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@
/pkg/collector/corechecks/embed/apm*.go @Datadog/agent-platform @DataDog/agent-apm
/pkg/collector/corechecks/embed/process_agent*.go @Datadog/agent-platform @DataDog/processes
/pkg/collector/corechecks/net/ @DataDog/agent-platform
/pkg/collector/corechecks/sbom/ @DataDog/container-integrations
/pkg/collector/corechecks/snmp/ @DataDog/network-device-monitoring
/pkg/collector/corechecks/system/ @DataDog/agent-platform
/pkg/collector/corechecks/system/**/*_windows*.go @DataDog/agent-platform @DataDog/windows-agent
Expand Down
1 change: 1 addition & 0 deletions LICENSE-3rdparty.csv
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ core,code.cloudfoundry.org/lager,Apache-2.0,"Copyright (c) 2016-Present CloudFou
core,code.cloudfoundry.org/tlsconfig,Apache-2.0,"Copyright (c) 2016-Present CloudFoundry.org Foundation, Inc. All Rights Reserved."
core,contrib.go.opencensus.io/exporter/prometheus,Apache-2.0,"Copyright 2020, OpenCensus Authors"
core,github.com/AlekSi/pointer,MIT,Copyright (c) 2015 Alexey Palazhchenko
core,github.com/CycloneDX/cyclonedx-go,Apache-2.0,Copyright & License | Copyright (c) OWASP Foundation | Copyright (c) OWASP Foundation. All Rights Reserved | Copyright OWASP Foundation
core,github.com/DataDog/agent-payload/v5/contimage,BSD-3-Clause,"Copyright (c) 2017, Datadog, Inc"
core,github.com/DataDog/agent-payload/v5/contlcycle,BSD-3-Clause,"Copyright (c) 2017, Datadog, Inc"
core,github.com/DataDog/agent-payload/v5/cyclonedx_v1_4,BSD-3-Clause,"Copyright (c) 2017, Datadog, Inc"
Expand Down
2 changes: 2 additions & 0 deletions cmd/agent/subcommands/run/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ import (
_ "github.com/DataDog/datadog-agent/pkg/collector/corechecks/embed"
_ "github.com/DataDog/datadog-agent/pkg/collector/corechecks/net"
_ "github.com/DataDog/datadog-agent/pkg/collector/corechecks/nvidia/jetson"
_ "github.com/DataDog/datadog-agent/pkg/collector/corechecks/sbom"
_ "github.com/DataDog/datadog-agent/pkg/collector/corechecks/snmp"
_ "github.com/DataDog/datadog-agent/pkg/collector/corechecks/system/cpu"
_ "github.com/DataDog/datadog-agent/pkg/collector/corechecks/system/disk"
Expand Down Expand Up @@ -376,6 +377,7 @@ func startAgent(cliParams *cliParams, flare flare.Component) error {
opts.EnableNoAggregationPipeline = pkgconfig.Datadog.GetBool("dogstatsd_no_aggregation_pipeline")
opts.UseContainerLifecycleForwarder = pkgconfig.Datadog.GetBool("container_lifecycle.enabled")
opts.UseContainerImageForwarder = pkgconfig.Datadog.GetBool("container_image.enabled")
opts.UseSBOMForwarder = pkgconfig.Datadog.GetBool("sbom.enabled")
demux = aggregator.InitAndStartAgentDemultiplexer(opts, hostnameDetected)

// Setup stats telemetry handler
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,7 @@ require (
)

require (
github.com/CycloneDX/cyclonedx-go v0.6.0
github.com/DataDog/go-libddwaf v0.0.0-20221118110754-0372d7c76b8a
github.com/go-redis/redis/v9 v9.0.0-rc.2
github.com/safchain/baloum v0.0.0-20221229104256-b1fc8f70a86b
Expand Down
4 changes: 4 additions & 0 deletions go.sum

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

166 changes: 166 additions & 0 deletions pkg/collector/corechecks/sbom/check.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2022-present Datadog, Inc.

package sbom

import (
"errors"
"time"

yaml "gopkg.in/yaml.v2"

"github.com/DataDog/datadog-agent/pkg/autodiscovery/integration"
"github.com/DataDog/datadog-agent/pkg/collector/check"
core "github.com/DataDog/datadog-agent/pkg/collector/corechecks"
ddConfig "github.com/DataDog/datadog-agent/pkg/config"
"github.com/DataDog/datadog-agent/pkg/util/log"
"github.com/DataDog/datadog-agent/pkg/workloadmeta"
)

const (
checkName = "sbom"
)

func init() {
core.RegisterCheck(checkName, CheckFactory)
}

// Config holds the container_image check configuration
type Config struct {
chunkSize int `yaml:"chunk_size"`
newSBOMMaxLatencySeconds int `yaml:"new_images_max_latency_seconds"`
periodicRefreshSeconds int `yaml:"periodic_refresh_seconds"`
}

type configValueRange struct {
min int
max int
default_ int
}

var /* const */ (
chunkSizeValueRange = &configValueRange{
min: 1,
max: 100,
default_: 1,
}

newSBOMMaxLatencySecondsValueRange = &configValueRange{
min: 1, // 1 s
max: 300, // 5 min
default_: 30, // 30 s
}

periodicRefreshSecondsValueRange = &configValueRange{
min: 60, // 1 min
max: 604800, // 1 week
default_: 3600, // 1h
}
)

func validateValue(val *int, range_ *configValueRange) {
if *val == 0 {
*val = range_.default_
} else if *val < range_.min {
*val = range_.min
} else if *val > range_.max {
*val = range_.max
}
}

func (c *Config) Parse(data []byte) error {
if err := yaml.Unmarshal(data, c); err != nil {
return err
}

validateValue(&c.chunkSize, chunkSizeValueRange)
validateValue(&c.newSBOMMaxLatencySeconds, newSBOMMaxLatencySecondsValueRange)
validateValue(&c.periodicRefreshSeconds, periodicRefreshSecondsValueRange)

return nil
}

// Check reports SBOM
type Check struct {
core.CheckBase
workloadmetaStore workloadmeta.Store
instance *Config
processor *processor
stopCh chan struct{}
}

// CheckFactory registers the sbom check
func CheckFactory() check.Check {
return &Check{
CheckBase: core.NewCheckBase(checkName),
workloadmetaStore: workloadmeta.GetGlobalStore(),
instance: &Config{},
stopCh: make(chan struct{}),
}
}

// Configure parses the check configuration and initializes the sbom check
func (c *Check) Configure(integrationConfigDigest uint64, config, initConfig integration.Data, source string) error {
if !ddConfig.Datadog.GetBool("sbom.enabled") {
return errors.New("collection of SBOM is disabled")
}

if err := c.CommonConfigure(integrationConfigDigest, initConfig, config, source); err != nil {
return err
}

if err := c.instance.Parse(config); err != nil {
return err
}

sender, err := c.GetSender()
if err != nil {
return err
}

c.processor = newProcessor(sender, c.instance.chunkSize, time.Duration(c.instance.newSBOMMaxLatencySeconds)*time.Second)

return nil
}

// Run starts the sbom check
func (c *Check) Run() error {
log.Infof("Starting long-running check %q", c.ID())
defer log.Infof("Shutting down long-running check %q", c.ID())

imgEventsCh := c.workloadmetaStore.Subscribe(
checkName,
workloadmeta.NormalPriority,
workloadmeta.NewFilter(
[]workloadmeta.Kind{workloadmeta.KindContainerImageMetadata},
workloadmeta.SourceAll,
workloadmeta.EventTypeSet, // We don’t care about SBOM removal because we just have to wait for them to expire on BE side once we stopped refreshing them periodically.
),
)

imgRefreshTicker := time.NewTicker(time.Duration(c.instance.periodicRefreshSeconds) * time.Second)

for {
select {
case eventBundle := <-imgEventsCh:
c.processor.processEvents(eventBundle)
case <-imgRefreshTicker.C:
c.processor.processRefresh(c.workloadmetaStore.ListImages())
case <-c.stopCh:
c.processor.stop()
return nil
}
}
}

// Stop stops the sbom check
func (c *Check) Stop() {
close(c.stopCh)
}

// Interval returns 0. It makes sbom a long-running check
func (c *Check) Interval() time.Duration {
return 0
}
Loading

0 comments on commit f3fa66c

Please sign in to comment.