diff --git a/apis/datadoghq/common/const.go b/apis/datadoghq/common/const.go index 4be59b37c..b9353b897 100644 --- a/apis/datadoghq/common/const.go +++ b/apis/datadoghq/common/const.go @@ -135,6 +135,9 @@ const ( const ( SystemProbeAppArmorAnnotationKey = "container.apparmor.security.beta.kubernetes.io/system-probe" SystemProbeAppArmorAnnotationValue = "unconfined" + + AgentAppArmorAnnotationKey = "container.apparmor.security.beta.kubernetes.io/agent" + AgentAppArmorAnnotationValue = "unconfined" ) // Datadog volume names and mount paths @@ -172,6 +175,10 @@ const ( SystemProbeOSReleaseDirVolumePath = "/etc/os-release" SystemProbeOSReleaseDirMountPath = "/host/etc/os-release" + ContainerdDirVolumeName = "host-containerd-dir" + ContainerdDirVolumePath = "/var/lib/containerd" + ContainerdDirMountPath = "/host/var/lib/containerd" + ApkDirVolumeName = "host-apk-dir" ApkDirVolumePath = "/var/lib/apk" ApkDirMountPath = "/host/var/lib/apk" diff --git a/apis/datadoghq/common/envvar.go b/apis/datadoghq/common/envvar.go index d6e8e1136..58abc0e0b 100644 --- a/apis/datadoghq/common/envvar.go +++ b/apis/datadoghq/common/envvar.go @@ -141,6 +141,8 @@ const ( DDSBOMEnabled = "DD_SBOM_ENABLED" DDSBOMContainerImageEnabled = "DD_SBOM_CONTAINER_IMAGE_ENABLED" DDSBOMContainerImageAnalyzers = "DD_SBOM_CONTAINER_IMAGE_ANALYZERS" + DDSBOMContainerUseMount = "DD_SBOM_CONTAINER_IMAGE_USE_MOUNT" + DDSBOMContainerOverlayFSDirectScan = "DD_SBOM_CONTAINER_IMAGE_OVERLAYFS_DIRECT_SCAN" DDSBOMHostEnabled = "DD_SBOM_HOST_ENABLED" DDSBOMHostAnalyzers = "DD_SBOM_HOST_ANALYZERS" DDSecretBackendCommand = "DD_SECRET_BACKEND_COMMAND" diff --git a/apis/datadoghq/v2alpha1/datadogagent_types.go b/apis/datadoghq/v2alpha1/datadogagent_types.go index 6f126f16b..2eae36867 100644 --- a/apis/datadoghq/v2alpha1/datadogagent_types.go +++ b/apis/datadoghq/v2alpha1/datadogagent_types.go @@ -416,12 +416,12 @@ type SBOMFeatureConfig struct { // +optional Enabled *bool `json:"enabled,omitempty"` - ContainerImage *SBOMTypeConfig `json:"containerImage,omitempty"` - Host *SBOMTypeConfig `json:"host,omitempty"` + ContainerImage *SBOMContainerImageConfig `json:"containerImage,omitempty"` + Host *SBOMHostConfig `json:"host,omitempty"` } // SBOMTypeConfig contains configuration for a SBOM collection type. -type SBOMTypeConfig struct { +type SBOMHostConfig struct { // Enable this option to activate SBOM collection. // Default: false // +optional @@ -433,6 +433,29 @@ type SBOMTypeConfig struct { Analyzers []string `json:"analyzers,omitempty"` } +// SBOMTypeConfig contains configuration for a SBOM collection type. +type SBOMContainerImageConfig struct { + // Enable this option to activate SBOM collection. + // Default: false + // +optional + Enabled *bool `json:"enabled,omitempty"` + + // Analyzers to use for SBOM collection. + // +optional + // +listType=set + Analyzers []string `json:"analyzers,omitempty"` + + // Enable this option to enable support for uncompressed layers. + // Default: false + // +optional + UncompressedLayersSupport bool `json:"uncompressedLayersSupport,omitempty"` + + // Enable this option to enable experimental overlayFS direct scan. + // Default: false + // +optional + OverlayFSDirectScan bool `json:"overlayFSDirectScan,omitempty"` +} + // NPMFeatureConfig contains NPM (Network Performance Monitoring) feature configuration. // Network Performance Monitoring runs in the System Probe and Process Agent. type NPMFeatureConfig struct { diff --git a/apis/datadoghq/v2alpha1/zz_generated.deepcopy.go b/apis/datadoghq/v2alpha1/zz_generated.deepcopy.go index bf5a3ca9b..278b80cc3 100644 --- a/apis/datadoghq/v2alpha1/zz_generated.deepcopy.go +++ b/apis/datadoghq/v2alpha1/zz_generated.deepcopy.go @@ -2071,6 +2071,31 @@ func (in *RollingUpdate) DeepCopy() *RollingUpdate { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SBOMContainerImageConfig) DeepCopyInto(out *SBOMContainerImageConfig) { + *out = *in + if in.Enabled != nil { + in, out := &in.Enabled, &out.Enabled + *out = new(bool) + **out = **in + } + if in.Analyzers != nil { + in, out := &in.Analyzers, &out.Analyzers + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SBOMContainerImageConfig. +func (in *SBOMContainerImageConfig) DeepCopy() *SBOMContainerImageConfig { + if in == nil { + return nil + } + out := new(SBOMContainerImageConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SBOMFeatureConfig) DeepCopyInto(out *SBOMFeatureConfig) { *out = *in @@ -2081,12 +2106,12 @@ func (in *SBOMFeatureConfig) DeepCopyInto(out *SBOMFeatureConfig) { } if in.ContainerImage != nil { in, out := &in.ContainerImage, &out.ContainerImage - *out = new(SBOMTypeConfig) + *out = new(SBOMContainerImageConfig) (*in).DeepCopyInto(*out) } if in.Host != nil { in, out := &in.Host, &out.Host - *out = new(SBOMTypeConfig) + *out = new(SBOMHostConfig) (*in).DeepCopyInto(*out) } } @@ -2102,7 +2127,7 @@ func (in *SBOMFeatureConfig) DeepCopy() *SBOMFeatureConfig { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *SBOMTypeConfig) DeepCopyInto(out *SBOMTypeConfig) { +func (in *SBOMHostConfig) DeepCopyInto(out *SBOMHostConfig) { *out = *in if in.Enabled != nil { in, out := &in.Enabled, &out.Enabled @@ -2116,12 +2141,12 @@ func (in *SBOMTypeConfig) DeepCopyInto(out *SBOMTypeConfig) { } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SBOMTypeConfig. -func (in *SBOMTypeConfig) DeepCopy() *SBOMTypeConfig { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SBOMHostConfig. +func (in *SBOMHostConfig) DeepCopy() *SBOMHostConfig { if in == nil { return nil } - out := new(SBOMTypeConfig) + out := new(SBOMHostConfig) in.DeepCopyInto(out) return out } diff --git a/config/crd/bases/v1/datadoghq.com_datadogagents.yaml b/config/crd/bases/v1/datadoghq.com_datadogagents.yaml index 95b7c762d..85d62d46f 100644 --- a/config/crd/bases/v1/datadoghq.com_datadogagents.yaml +++ b/config/crd/bases/v1/datadoghq.com_datadogagents.yaml @@ -1307,6 +1307,16 @@ spec: Enable this option to activate SBOM collection. Default: false type: boolean + overlayFSDirectScan: + description: |- + Enable this option to enable experimental overlayFS direct scan. + Default: false + type: boolean + uncompressedLayersSupport: + description: |- + Enable this option to enable support for uncompressed layers. + Default: false + type: boolean type: object enabled: description: |- @@ -7105,6 +7115,16 @@ spec: Enable this option to activate SBOM collection. Default: false type: boolean + overlayFSDirectScan: + description: |- + Enable this option to enable experimental overlayFS direct scan. + Default: false + type: boolean + uncompressedLayersSupport: + description: |- + Enable this option to enable support for uncompressed layers. + Default: false + type: boolean type: object enabled: description: |- diff --git a/config/crd/bases/v1beta1/datadoghq.com_datadogagents.yaml b/config/crd/bases/v1beta1/datadoghq.com_datadogagents.yaml index b9737112b..4da799024 100644 --- a/config/crd/bases/v1beta1/datadoghq.com_datadogagents.yaml +++ b/config/crd/bases/v1beta1/datadoghq.com_datadogagents.yaml @@ -4820,6 +4820,12 @@ spec: enabled: description: 'Enable this option to activate SBOM collection. Default: false' type: boolean + overlayFSDirectScan: + description: 'Enable this option to enable experimental overlayFS direct scan. Default: false' + type: boolean + uncompressedLayersSupport: + description: 'Enable this option to enable support for uncompressed layers. Default: false' + type: boolean type: object enabled: description: 'Enable this option to activate SBOM collection. Default: false' diff --git a/controllers/datadogagent/feature/sbom/feature.go b/controllers/datadogagent/feature/sbom/feature.go index 7e62db24a..fe601dda6 100644 --- a/controllers/datadogagent/feature/sbom/feature.go +++ b/controllers/datadogagent/feature/sbom/feature.go @@ -44,11 +44,13 @@ type sbomFeature struct { owner metav1.Object logger logr.Logger - enabled bool - containerImageEnabled bool - containerImageAnalyzers []string - hostEnabled bool - hostAnalyzers []string + enabled bool + containerImageEnabled bool + containerImageAnalyzers []string + containerImageUncompressedLayersSupport bool + containerImageOverlayFSDirectScan bool + hostEnabled bool + hostAnalyzers []string } // ID returns the ID of the Feature @@ -70,6 +72,8 @@ func (f *sbomFeature) Configure(dda *v2alpha1.DatadogAgent) (reqComp feature.Req if sbomConfig.ContainerImage != nil && apiutils.BoolValue(sbomConfig.ContainerImage.Enabled) { f.containerImageEnabled = true f.containerImageAnalyzers = sbomConfig.ContainerImage.Analyzers + f.containerImageUncompressedLayersSupport = sbomConfig.ContainerImage.UncompressedLayersSupport + f.containerImageOverlayFSDirectScan = sbomConfig.ContainerImage.OverlayFSDirectScan } if sbomConfig.Host != nil && apiutils.BoolValue(sbomConfig.Host.Enabled) { f.hostEnabled = true @@ -112,14 +116,14 @@ func mergeConfigs(ddaSpec *v2alpha1.DatadogAgentSpec, ddaStatus *v2alpha1.Datado if ddaStatus.RemoteConfigConfiguration.Features.SBOM.Host != nil && ddaStatus.RemoteConfigConfiguration.Features.SBOM.Host.Enabled != nil { if ddaSpec.Features.SBOM.Host == nil { - ddaSpec.Features.SBOM.Host = &v2alpha1.SBOMTypeConfig{} + ddaSpec.Features.SBOM.Host = &v2alpha1.SBOMHostConfig{} } ddaSpec.Features.SBOM.Host.Enabled = ddaStatus.RemoteConfigConfiguration.Features.SBOM.Host.Enabled } if ddaStatus.RemoteConfigConfiguration.Features.SBOM.ContainerImage != nil && ddaStatus.RemoteConfigConfiguration.Features.SBOM.ContainerImage.Enabled != nil { if ddaSpec.Features.SBOM.ContainerImage == nil { - ddaSpec.Features.SBOM.ContainerImage = &v2alpha1.SBOMTypeConfig{} + ddaSpec.Features.SBOM.ContainerImage = &v2alpha1.SBOMContainerImageConfig{} } ddaSpec.Features.SBOM.ContainerImage.Enabled = ddaStatus.RemoteConfigConfiguration.Features.SBOM.ContainerImage.Enabled } @@ -161,6 +165,33 @@ func (f *sbomFeature) ManageNodeAgent(managers feature.PodTemplateManagers, prov Value: strings.Join(f.containerImageAnalyzers, " "), }) } + if f.containerImageUncompressedLayersSupport { + if f.containerImageOverlayFSDirectScan { + managers.EnvVar().AddEnvVarToContainer(apicommonv1.CoreAgentContainerName, &corev1.EnvVar{ + Name: apicommon.DDSBOMContainerOverlayFSDirectScan, + Value: "true", + }) + } else { + managers.EnvVar().AddEnvVarToContainer(apicommonv1.CoreAgentContainerName, &corev1.EnvVar{ + Name: apicommon.DDSBOMContainerUseMount, + Value: "true", + }) + + managers.SecurityContext().AddCapabilitiesToContainer( + []corev1.Capability{"SYS_ADMIN"}, + apicommonv1.CoreAgentContainerName, + ) + + managers.Annotation().AddAnnotation(apicommon.AgentAppArmorAnnotationKey, apicommon.AgentAppArmorAnnotationValue) + } + + volMgr := managers.Volume() + volMountMgr := managers.VolumeMount() + + containerdLibVol, containerdLibVolMount := volume.GetVolumes(apicommon.ContainerdDirVolumeName, apicommon.ContainerdDirVolumePath, apicommon.ContainerdDirMountPath, true) + volMountMgr.AddVolumeMountToContainer(&containerdLibVolMount, apicommonv1.CoreAgentContainerName) + volMgr.AddVolume(&containerdLibVol) + } managers.EnvVar().AddEnvVar(&corev1.EnvVar{ Name: apicommon.DDSBOMHostEnabled, diff --git a/controllers/datadogagent/feature/sbom/feature_test.go b/controllers/datadogagent/feature/sbom/feature_test.go index 37b8a191c..d977db4d4 100644 --- a/controllers/datadogagent/feature/sbom/feature_test.go +++ b/controllers/datadogagent/feature/sbom/feature_test.go @@ -38,12 +38,17 @@ func Test_sbomFeature_Configure(t *testing.T) { sbomEnabledContainerImageEnabled := sbomEnabled.DeepCopy() { - sbomEnabledContainerImageEnabled.Spec.Features.SBOM.ContainerImage = &v2alpha1.SBOMTypeConfig{Enabled: apiutils.NewBoolPointer(true)} + sbomEnabledContainerImageEnabled.Spec.Features.SBOM.ContainerImage = &v2alpha1.SBOMContainerImageConfig{Enabled: apiutils.NewBoolPointer(true)} + } + + sbomEnabledContainerImageOverlayFSEnabled := sbomEnabled.DeepCopy() + { + sbomEnabledContainerImageOverlayFSEnabled.Spec.Features.SBOM.ContainerImage = &v2alpha1.SBOMContainerImageConfig{Enabled: apiutils.NewBoolPointer(true), UncompressedLayersSupport: true, OverlayFSDirectScan: true} } sbomEnabledHostEnabled := sbomEnabled.DeepCopy() { - sbomEnabledHostEnabled.Spec.Features.SBOM.Host = &v2alpha1.SBOMTypeConfig{Enabled: apiutils.NewBoolPointer(true)} + sbomEnabledHostEnabled.Spec.Features.SBOM.Host = &v2alpha1.SBOMHostConfig{Enabled: apiutils.NewBoolPointer(true)} } sbomNodeAgentWantFunc := func(t testing.TB, mgrInterface feature.PodTemplateManagers) { @@ -90,6 +95,35 @@ func Test_sbomFeature_Configure(t *testing.T) { assert.True(t, apiutils.IsEqualStruct(nodeAgentEnvVars, wantEnvVars), "Node agent envvars \ndiff = %s", cmp.Diff(nodeAgentEnvVars, wantEnvVars)) } + sbomWithContainerImageOverlayFSWantFunc := func(t testing.TB, mgrInterface feature.PodTemplateManagers) { + mgr := mgrInterface.(*fake.PodTemplateManagers) + + wantEnvVars := []*corev1.EnvVar{ + { + Name: apicommon.DDSBOMEnabled, + Value: "true", + }, + { + Name: apicommon.DDSBOMContainerImageEnabled, + Value: "true", + }, + { + Name: apicommon.DDSBOMHostEnabled, + Value: "false", + }, + } + + nodeAgentEnvVars := mgr.EnvVarMgr.EnvVarsByC[apicommonv1.AllContainers] + nodeCoreAgentEnvVars := mgr.EnvVarMgr.EnvVarsByC[apicommonv1.CoreAgentContainerName] + assert.True(t, apiutils.IsEqualStruct(nodeAgentEnvVars, wantEnvVars), "Node agent envvars \ndiff = %s", cmp.Diff(nodeAgentEnvVars, wantEnvVars)) + + wantEnvVars = []*corev1.EnvVar{{ + Name: apicommon.DDSBOMContainerOverlayFSDirectScan, + Value: "true", + }} + assert.True(t, apiutils.IsEqualStruct(nodeCoreAgentEnvVars, wantEnvVars), "Core agent envvars \ndiff = %s", cmp.Diff(nodeCoreAgentEnvVars, wantEnvVars)) + } + sbomWithHostWantFunc := func(t testing.TB, mgrInterface feature.PodTemplateManagers) { mgr := mgrInterface.(*fake.PodTemplateManagers) @@ -255,6 +289,12 @@ func Test_sbomFeature_Configure(t *testing.T) { WantConfigure: true, Agent: test.NewDefaultComponentTest().WithWantFunc(sbomWithContainerImageWantFunc), }, + { + Name: "SBOM enabled, ContainerImage enabled, overlayFS direct scan", + DDAv2: sbomEnabledContainerImageOverlayFSEnabled, + WantConfigure: true, + Agent: test.NewDefaultComponentTest().WithWantFunc(sbomWithContainerImageOverlayFSWantFunc), + }, { Name: "SBOM enabled, Host enabled", DDAv2: sbomEnabledHostEnabled, diff --git a/docs/configuration.v2alpha1.md b/docs/configuration.v2alpha1.md index fb3a7e1ab..35ad08ff7 100644 --- a/docs/configuration.v2alpha1.md +++ b/docs/configuration.v2alpha1.md @@ -148,6 +148,8 @@ spec: | features.remoteConfiguration.enabled | Enable this option to activate Remote Configuration. Default: true | | features.sbom.containerImage.analyzers | Analyzers to use for SBOM collection. | | features.sbom.containerImage.enabled | Enable this option to activate SBOM collection. Default: false | +| features.sbom.containerImage.overlayFSDirectScan | Enable this option to enable experimental overlayFS direct scan. Default: false | +| features.sbom.containerImage.uncompressedLayersSupport | Enable this option to enable support for uncompressed layers. Default: false | | features.sbom.enabled | Enable this option to activate SBOM collection. Default: false | | features.sbom.host.analyzers | Analyzers to use for SBOM collection. | | features.sbom.host.enabled | Enable this option to activate SBOM collection. Default: false | diff --git a/pkg/remoteconfig/updater.go b/pkg/remoteconfig/updater.go index e38d3c1d5..ab420900c 100644 --- a/pkg/remoteconfig/updater.go +++ b/pkg/remoteconfig/updater.go @@ -444,7 +444,7 @@ func (r *RemoteConfigUpdater) updateInstanceStatus(dda v2alpha1.DatadogAgent, cf // SBOM HOST if cfg.CoreAgent.SBOM.Host != nil { if newddaStatus.RemoteConfigConfiguration.Features.SBOM.Host == nil { - newddaStatus.RemoteConfigConfiguration.Features.SBOM.Host = &v2alpha1.SBOMTypeConfig{} + newddaStatus.RemoteConfigConfiguration.Features.SBOM.Host = &v2alpha1.SBOMHostConfig{} } if newddaStatus.RemoteConfigConfiguration.Features.SBOM.Host.Enabled == nil { newddaStatus.RemoteConfigConfiguration.Features.SBOM.Host.Enabled = new(bool) @@ -457,7 +457,7 @@ func (r *RemoteConfigUpdater) updateInstanceStatus(dda v2alpha1.DatadogAgent, cf // SBOM CONTAINER IMAGE if cfg.CoreAgent.SBOM.ContainerImage != nil { if newddaStatus.RemoteConfigConfiguration.Features.SBOM.ContainerImage == nil { - newddaStatus.RemoteConfigConfiguration.Features.SBOM.ContainerImage = &v2alpha1.SBOMTypeConfig{} + newddaStatus.RemoteConfigConfiguration.Features.SBOM.ContainerImage = &v2alpha1.SBOMContainerImageConfig{} } if newddaStatus.RemoteConfigConfiguration.Features.SBOM.ContainerImage.Enabled == nil { newddaStatus.RemoteConfigConfiguration.Features.SBOM.ContainerImage.Enabled = new(bool)