Skip to content

Commit

Permalink
[features] migrate CSPM feature (#492)
Browse files Browse the repository at this point in the history
* [features] migrate CSPM feature
  • Loading branch information
celenechang committed May 19, 2022
1 parent 827d687 commit 263b407
Show file tree
Hide file tree
Showing 21 changed files with 779 additions and 15 deletions.
1 change: 1 addition & 0 deletions LICENSE-3rdparty.csv
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ core,"github.com/onsi/gomega/matchers/support/goraph/edge",MIT
core,"github.com/onsi/gomega/matchers/support/goraph/node",MIT
core,"github.com/onsi/gomega/matchers/support/goraph/util",MIT
core,"github.com/onsi/gomega/types",MIT
core,"github.com/openshift/api/security/v1",Apache-2.0
core,"github.com/peterbourgon/diskv",MIT
core,"github.com/pierrec/lz4/v4",NewBSD
core,"github.com/pierrec/lz4/v4/internal/lz4block",NewBSD
Expand Down
14 changes: 12 additions & 2 deletions apis/datadoghq/common/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,14 @@ const (
HostRootHostPath = "/"
HostRootMountPath = "/host/root"

GroupVolumeName = "group"
GroupHostPath = "/etc/group"
GroupMountPath = "/etc/group"

PasswdVolumeName = "passwd"
PasswdHostPath = "/etc/passwd"
PasswdMountPath = "/etc/passwd"

ProcdirVolumeName = "procdir"
ProcdirHostPath = "/proc"
ProcdirMountPath = "/host/proc"
Expand All @@ -126,9 +134,11 @@ const (
ModulesVolumeName = "modules"
// same path on host and container
ModulesVolumePath = "/lib/modules"
SrcVolumeName = "src"

SrcVolumeName = "src"
// same path on host and container
SrcVolumePath = "/usr/src"
SrcVolumePath = "/usr/src"

LogDatadogVolumeName = "logdatadog"
LogDatadogVolumePath = "/var/log/datadog"
TmpVolumeName = "tmp"
Expand Down
6 changes: 4 additions & 2 deletions apis/datadoghq/common/envvar.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ const (
DDIgnoreAutoConf = "DD_IGNORE_AUTOCONF"
DDKubeStateMetricsCoreEnabled = "DD_KUBE_STATE_METRICS_CORE_ENABLED"
DDKubeStateMetricsCoreConfigMap = "DD_KUBE_STATE_METRICS_CORE_CONFIGMAP_NAME"
DDHostRootEnvVar = "HOST_ROOT"
DDProcessAgentEnabledEnvVar = "DD_PROCESS_AGENT_ENABLED"
DDSystemProbeNPMEnabledEnvVar = "DD_SYSTEM_PROBE_NETWORK_ENABLED"
DDSystemProbeEnabledEnvVar = "DD_SYSTEM_PROBE_ENABLED"
DDProcessAgentEnabledEnvVar = "DD_PROCESS_AGENT_ENABLED"
DDSystemProbeExternal = "DD_SYSTEM_PROBE_EXTERNAL"
DDSystemProbeServiceMonitoringEnabled = "DD_SYSTEM_PROBE_SERVICE_MONITORING_ENABLED"
DDSystemProbeSocket = "DD_SYSPROBE_SOCKET"
DDComplianceEnabled = "DD_COMPLIANCE_CONFIG_ENABLED"
DDComplianceCheckInterval = "DD_COMPLIANCE_CONFIG_CHECK_INTERVAL"
DDHostRootEnvVar = "HOST_ROOT"
DDEnableOOMKillEnvVar = "DD_SYSTEM_PROBE_CONFIG_ENABLE_OOM_KILL"
DDEnableTCPQueueLengthEnvVar = "DD_SYSTEM_PROBE_CONFIG_ENABLE_TCP_QUEUE_LENGTH"
DDLeaderElection = "DD_LEADER_ELECTION"
Expand Down
3 changes: 2 additions & 1 deletion apis/datadoghq/v1alpha1/datadogagent_conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,8 @@ func convertConfigMapConfig(src *CustomConfigSpec) *v2alpha1.CustomConfig {
return dstConfig
}

func convertConfigDirSpec(src *ConfigDirSpec) *v2alpha1.CustomConfig {
// ConvertConfigDirSpec converts v1alpha1.ConfigDirSpec to v2alpha1.CustomConfig
func ConvertConfigDirSpec(src *ConfigDirSpec) *v2alpha1.CustomConfig {
if src == nil {
return nil
}
Expand Down
4 changes: 2 additions & 2 deletions apis/datadoghq/v1alpha1/datadogagent_conversion_agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,11 @@ func convertDatadogAgentSpec(src *DatadogAgentSpecAgentSpec, dst *v2alpha1.Datad
// src.Config.LogLevel not forwarded as setting is not at the same level (NodeAgent POD in v1, Container in v2)

if src.Config.Confd != nil {
getV2TemplateOverride(&dst.Spec, v2alpha1.NodeAgentComponentName).ExtraConfd = convertConfigDirSpec(src.Config.Confd)
getV2TemplateOverride(&dst.Spec, v2alpha1.NodeAgentComponentName).ExtraConfd = ConvertConfigDirSpec(src.Config.Confd)
}

if src.Config.Checksd != nil {
getV2TemplateOverride(&dst.Spec, v2alpha1.NodeAgentComponentName).ExtraChecksd = convertConfigDirSpec(src.Config.Checksd)
getV2TemplateOverride(&dst.Spec, v2alpha1.NodeAgentComponentName).ExtraChecksd = ConvertConfigDirSpec(src.Config.Checksd)
}

if src.Config.Tags != nil {
Expand Down
2 changes: 1 addition & 1 deletion apis/datadoghq/v1alpha1/datadogagent_conversion_dca.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func convertClusterAgentSpec(src *DatadogAgentSpecClusterAgentSpec, dst *v2alpha
}

if src.Config.Confd != nil {
getV2TemplateOverride(&dst.Spec, v2alpha1.ClusterAgentComponentName).ExtraConfd = convertConfigDirSpec(src.Config.Confd)
getV2TemplateOverride(&dst.Spec, v2alpha1.ClusterAgentComponentName).ExtraConfd = ConvertConfigDirSpec(src.Config.Confd)
}

if src.Config.Env != nil {
Expand Down
2 changes: 1 addition & 1 deletion apis/datadoghq/v2alpha1/datadogagent_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ type TCPQueueLengthFeatureConfig struct {
}

// CSPMFeatureConfig contains CSPM (Cloud Security Posture Management) configuration.
// CSPM runs in the Security Agent.
// CSPM runs in the Security Agent and Cluster Agent.
type CSPMFeatureConfig struct {
// Enabled enables Cloud Security Posture Management.
// Default: false
Expand Down
1 change: 1 addition & 0 deletions controllers/datadogagent/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
"github.com/DataDog/datadog-operator/controllers/datadogagent/feature"

// Use to register features
_ "github.com/DataDog/datadog-operator/controllers/datadogagent/feature/cspm"
_ "github.com/DataDog/datadog-operator/controllers/datadogagent/feature/dummy"
_ "github.com/DataDog/datadog-operator/controllers/datadogagent/feature/enabledefault"
_ "github.com/DataDog/datadog-operator/controllers/datadogagent/feature/kubernetesstatecore"
Expand Down
31 changes: 31 additions & 0 deletions controllers/datadogagent/feature/cspm/const.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// 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 2016-present Datadog, Inc.

package cspm

import (
"fmt"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

const (
cspmRBACPrefix = "cspm"
cspmConfigVolumeName = "complianceconfigdir"
cspmConfigVolumePath = "/etc/datadog-agent/compliance.d"
)

func getSCCName(owner metav1.Object) string {
return fmt.Sprintf("%s-%s", owner.GetNamespace(), owner.GetName())
}

func getPSPName(owner metav1.Object) string {
return fmt.Sprintf("%s-%s", owner.GetNamespace(), owner.GetName())
}

// getRBACResourceName return the RBAC resources name
func getRBACResourceName(owner metav1.Object) string {
return fmt.Sprintf("%s-%s-%s-%s", owner.GetNamespace(), owner.GetName(), cspmRBACPrefix, "cluster-agent")
}
243 changes: 243 additions & 0 deletions controllers/datadogagent/feature/cspm/feature.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
// 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 2016-present Datadog, Inc.

package cspm

import (
"strconv"

corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"github.com/DataDog/datadog-operator/apis/datadoghq/v1alpha1"
"github.com/DataDog/datadog-operator/apis/datadoghq/v2alpha1"
apiutils "github.com/DataDog/datadog-operator/apis/utils"

apicommon "github.com/DataDog/datadog-operator/apis/datadoghq/common"
apicommonv1 "github.com/DataDog/datadog-operator/apis/datadoghq/common/v1"
"github.com/DataDog/datadog-operator/controllers/datadogagent/feature"
"github.com/DataDog/datadog-operator/controllers/datadogagent/object/volume"
)

func init() {
err := feature.Register(feature.CSPMIDType, buildCSPMFeature)
if err != nil {
panic(err)
}
}

func buildCSPMFeature(options *feature.Options) feature.Feature {
cspmFeat := &cspmFeature{}

return cspmFeat
}

type cspmFeature struct {
enable bool
serviceAccountName string
checkInterval string
configMapConfig *apicommonv1.ConfigMapConfig
configMapName string
createSCC bool
createPSP bool

owner metav1.Object
}

// Configure is used to configure the feature from a v2alpha1.DatadogAgent instance.
func (f *cspmFeature) Configure(dda *v2alpha1.DatadogAgent) (reqComp feature.RequiredComponents) {
f.owner = dda

if dda.Spec.Features.CSPM != nil && apiutils.BoolValue(dda.Spec.Features.CSPM.Enabled) {
f.enable = true
f.serviceAccountName = v2alpha1.GetClusterAgentServiceAccount(dda)

if dda.Spec.Features.CSPM.CheckInterval != nil {
f.checkInterval = strconv.FormatInt(dda.Spec.Features.CSPM.CheckInterval.Nanoseconds(), 10)
}

if dda.Spec.Features.CSPM.CustomBenchmarks != nil {
f.configMapName = dda.Spec.Features.CSPM.CustomBenchmarks.Name
f.configMapConfig = dda.Spec.Features.CSPM.CustomBenchmarks
}

// TODO add settings to configure f.createSCC and f.createPSP

reqComp = feature.RequiredComponents{
ClusterAgent: feature.RequiredComponent{IsRequired: apiutils.NewBoolPointer(true)},
Agent: feature.RequiredComponent{
IsRequired: apiutils.NewBoolPointer(true),
Containers: []apicommonv1.AgentContainerName{
apicommonv1.SecurityAgentContainerName,
},
},
}
}

return reqComp
}

// ConfigureV1 use to configure the feature from a v1alpha1.DatadogAgent instance.
func (f *cspmFeature) ConfigureV1(dda *v1alpha1.DatadogAgent) (reqComp feature.RequiredComponents) {
f.owner = dda

if dda.Spec.Agent.Security != nil && *dda.Spec.Agent.Security.Compliance.Enabled {
f.enable = true
f.serviceAccountName = v1alpha1.GetClusterAgentServiceAccount(dda)

if dda.Spec.Agent.Security.Compliance.CheckInterval != nil {
f.checkInterval = strconv.FormatInt(dda.Spec.Agent.Security.Compliance.CheckInterval.Duration.Nanoseconds(), 10)
}

if dda.Spec.Agent.Security.Compliance.ConfigDir != nil {
f.configMapName = dda.Spec.Agent.Security.Compliance.ConfigDir.ConfigMapName
f.configMapConfig = v1alpha1.ConvertConfigDirSpec(dda.Spec.Agent.Security.Compliance.ConfigDir).ConfigMap
}

reqComp = feature.RequiredComponents{
ClusterAgent: feature.RequiredComponent{IsRequired: apiutils.NewBoolPointer(true)},
Agent: feature.RequiredComponent{
IsRequired: apiutils.NewBoolPointer(true),
Containers: []apicommonv1.AgentContainerName{
apicommonv1.SecurityAgentContainerName,
},
},
}
}

return reqComp
}

// ManageDependencies allows a feature to manage its dependencies.
// Feature's dependencies should be added in the store.
func (f *cspmFeature) ManageDependencies(managers feature.ResourceManagers) error {
if f.createSCC {
// Manage SecurityContextConstraints
sccName := getSCCName(f.owner)
scc, err := managers.PodSecurityManager().GetSecurityContextConstraints(f.owner.GetNamespace(), sccName)
if err != nil {
return err
}
scc.AllowHostPID = true
managers.PodSecurityManager().UpdateSecurityContextConstraints(scc)
}

if f.createPSP {
// Manage PodSecurityPolicy
pspName := getPSPName(f.owner)
psp, err := managers.PodSecurityManager().GetPodSecurityPolicy(f.owner.GetNamespace(), pspName)
if err != nil {
return err
}
psp.Spec.HostPID = true
managers.PodSecurityManager().UpdatePodSecurityPolicy(psp)
}

// Manage RBAC
rbacName := getRBACResourceName(f.owner)

return managers.RBACManager().AddClusterPolicyRules("", rbacName, f.serviceAccountName, getRBACPolicyRules())
}

// ManageClusterAgent allows a feature to configure the ClusterAgent's corev1.PodTemplateSpec
// It should do nothing if the feature doesn't need to configure it.
func (f *cspmFeature) ManageClusterAgent(managers feature.PodTemplateManagers) error {
if f.configMapConfig != nil && f.configMapName != "" {
cmVol, cmVolMount := volume.GetConfigMapVolumes(
f.configMapConfig,
f.configMapName,
cspmConfigVolumeName,
cspmConfigVolumePath,
)
managers.Volume().AddVolumeToContainer(&cmVol, &cmVolMount, apicommonv1.ClusterAgentContainerName)
}

enabledEnvVar := &corev1.EnvVar{
Name: apicommon.DDComplianceEnabled,
Value: "true",
}
managers.EnvVar().AddEnvVarToContainer(apicommonv1.ClusterAgentContainerName, enabledEnvVar)

if f.checkInterval != "" {
intervalEnvVar := &corev1.EnvVar{
Name: apicommon.DDComplianceCheckInterval,
Value: f.checkInterval,
}
managers.EnvVar().AddEnvVarToContainer(apicommonv1.ClusterAgentContainerName, intervalEnvVar)
}

return nil
}

// ManageNodeAgent allows a feature to configure the Node Agent's corev1.PodTemplateSpec
// It should do nothing if the feature doesn't need to configure it.
func (f *cspmFeature) ManageNodeAgent(managers feature.PodTemplateManagers) error {
// security context capabilities
capabilities := []corev1.Capability{
"AUDIT_CONTROL",
"AUDIT_READ",
}
managers.SecurityContext().AddCapabilitiesToContainer(capabilities, apicommonv1.SecurityAgentContainerName)

// configmap volume mount
if f.configMapConfig != nil && f.configMapName != "" {
cmVol, cmVolMount := volume.GetConfigMapVolumes(
f.configMapConfig,
f.configMapName,
cspmConfigVolumeName,
cspmConfigVolumePath,
)
managers.Volume().AddVolumeToContainer(&cmVol, &cmVolMount, apicommonv1.SecurityAgentContainerName)
}

// cgroups volume mount
cgroupsVol, cgroupsVolMount := volume.GetVolumes(apicommon.CgroupsVolumeName, apicommon.CgroupsHostPath, apicommon.CgroupsMountPath, true)
managers.Volume().AddVolumeToContainer(&cgroupsVol, &cgroupsVolMount, apicommonv1.SecurityAgentContainerName)

// passwd volume mount
passwdVol, passwdVolMount := volume.GetVolumes(apicommon.PasswdVolumeName, apicommon.PasswdHostPath, apicommon.PasswdMountPath, true)
managers.Volume().AddVolumeToContainer(&passwdVol, &passwdVolMount, apicommonv1.SecurityAgentContainerName)

// procdir volume mount
procdirVol, procdirVolMount := volume.GetVolumes(apicommon.ProcdirVolumeName, apicommon.ProcdirHostPath, apicommon.ProcdirMountPath, true)
managers.Volume().AddVolumeToContainer(&procdirVol, &procdirVolMount, apicommonv1.SecurityAgentContainerName)

// host root volume mount
hostRootVol, hostRootVolMount := volume.GetVolumes(apicommon.HostRootVolumeName, apicommon.HostRootHostPath, apicommon.HostRootMountPath, true)
managers.Volume().AddVolumeToContainer(&hostRootVol, &hostRootVolMount, apicommonv1.SecurityAgentContainerName)

// group volume mount
groupVol, groupVolMount := volume.GetVolumes(apicommon.GroupVolumeName, apicommon.GroupHostPath, apicommon.GroupMountPath, true)
managers.Volume().AddVolumeToContainer(&groupVol, &groupVolMount, apicommonv1.SecurityAgentContainerName)

// env vars
enabledEnvVar := &corev1.EnvVar{
Name: apicommon.DDComplianceEnabled,
Value: "true",
}
managers.EnvVar().AddEnvVarToContainer(apicommonv1.SecurityAgentContainerName, enabledEnvVar)

hostRootEnvVar := &corev1.EnvVar{
Name: apicommon.DDHostRootEnvVar,
Value: apicommon.HostRootMountPath,
}
managers.EnvVar().AddEnvVarToContainer(apicommonv1.SecurityAgentContainerName, hostRootEnvVar)

if f.checkInterval != "" {
intervalEnvVar := &corev1.EnvVar{
Name: apicommon.DDComplianceCheckInterval,
Value: f.checkInterval,
}
managers.EnvVar().AddEnvVarToContainer(apicommonv1.SecurityAgentContainerName, intervalEnvVar)
}

return nil
}

// ManageClusterChecksRunner allows a feature to configure the ClusterChecksRunner's corev1.PodTemplateSpec
// It should do nothing if the feature doesn't need to configure it.
func (f *cspmFeature) ManageClusterChecksRunner(managers feature.PodTemplateManagers) error {
return nil
}
Loading

0 comments on commit 263b407

Please sign in to comment.