diff --git a/api/datadoghq/v2alpha1/datadogagent_types.go b/api/datadoghq/v2alpha1/datadogagent_types.go index 9606de3b56..1f5bce689c 100644 --- a/api/datadoghq/v2alpha1/datadogagent_types.go +++ b/api/datadoghq/v2alpha1/datadogagent_types.go @@ -1909,8 +1909,43 @@ type GlobalConfig struct { // UseVSock allows the use of VSock communication between the Agent and containerized workloads. // Default: 'false' + // Deprecated: Use VSock.Enabled instead. When VSock is set, this field is ignored. // +optional UseVSock *bool `json:"useVSock,omitempty"` + + // VSock configures VSock communication for the Agent. + // +optional + VSock *VSockConfig `json:"vsock,omitempty"` +} + +// VSockMode controls which Agent components communicate over VSock. +// +kubebuilder:validation:Enum=full;system-probe +type VSockMode string + +const ( + // VSockModeFull enables VSock communication between the Agent and containerized workloads + // for all Agent components. This is the default and matches the legacy UseVSock behavior. + VSockModeFull VSockMode = "full" + // VSockModeSystemProbe scopes VSock communication to the CWS runtime-security event gRPC + // server only, allowing the system-probe running inside a micro VM to forward events to the + // host system-probe over VSock. All other Agent communications use the regular TCP/unix + // socket transport. This mode requires features.cws.directSendFromSystemProbe to be enabled, + // since the host system-probe no longer exposes the unix socket the security-agent connects to. + VSockModeSystemProbe VSockMode = "system-probe" +) + +// VSockConfig configures VSock communication for the Agent. +type VSockConfig struct { + // Enabled enables VSock communication. + // Default: 'false' + // +optional + Enabled *bool `json:"enabled,omitempty"` + + // Mode controls which Agent components communicate over VSock. + // "full" (default): all Agent components communicate over VSock. + // "system-probe": only the CWS system-probe <=> micro VM system-probe communication uses VSock. + // +optional + Mode *VSockMode `json:"mode,omitempty"` } // DatadogCredentials is a generic structure that holds credentials to access Datadog. diff --git a/api/datadoghq/v2alpha1/datadogagent_validation.go b/api/datadoghq/v2alpha1/datadogagent_validation.go index 4a942ecc5a..7e890f539b 100644 --- a/api/datadoghq/v2alpha1/datadogagent_validation.go +++ b/api/datadoghq/v2alpha1/datadogagent_validation.go @@ -5,7 +5,11 @@ package v2alpha1 -import "fmt" +import ( + "fmt" + + apiutils "github.com/DataDog/datadog-operator/api/utils" +) // ValidateDatadogAgent is used to check if a DatadogAgent is valid func ValidateDatadogAgent(dda *DatadogAgent) error { @@ -14,5 +18,30 @@ func ValidateDatadogAgent(dda *DatadogAgent) error { if dda.Spec.Global == nil || dda.Spec.Global.Credentials == nil { return fmt.Errorf("credentials not configured in the DatadogAgent, can't reconcile") } + + if err := validateVSock(&dda.Spec); err != nil { + return err + } + + return nil +} + +// validateVSock ensures the VSock configuration is consistent with the features that rely on it. +func validateVSock(spec *DatadogAgentSpec) error { + vsockEnabled, vsockMode := spec.Global.GetVSockConfig() + if !vsockEnabled || vsockMode != VSockModeSystemProbe { + return nil + } + + // In SystemProbe mode the host system-probe hosts the runtime-security event server over + // VSock and no longer exposes the unix socket the security-agent connects to, so CWS must + // send payloads directly from the system-probe. + if spec.Features == nil || spec.Features.CWS == nil || !apiutils.BoolValue(spec.Features.CWS.Enabled) { + return nil + } + if !apiutils.BoolValue(spec.Features.CWS.DirectSendFromSystemProbe) { + return fmt.Errorf("global.vsock.mode %q requires features.cws.directSendFromSystemProbe to be enabled", VSockModeSystemProbe) + } + return nil } diff --git a/api/datadoghq/v2alpha1/datadogagent_validation_test.go b/api/datadoghq/v2alpha1/datadogagent_validation_test.go new file mode 100644 index 0000000000..b6066c4ca7 --- /dev/null +++ b/api/datadoghq/v2alpha1/datadogagent_validation_test.go @@ -0,0 +1,102 @@ +// 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 v2alpha1 + +import ( + "testing" + + "k8s.io/utils/ptr" +) + +func Test_validateVSock(t *testing.T) { + tests := []struct { + name string + spec *DatadogAgentSpec + wantErr bool + }{ + { + name: "no global - no error", + spec: &DatadogAgentSpec{}, + wantErr: false, + }, + { + name: "vsock disabled - no error", + spec: &DatadogAgentSpec{ + Global: &GlobalConfig{}, + }, + wantErr: false, + }, + { + name: "vsock Full mode + CWS without directSend - no error", + spec: &DatadogAgentSpec{ + Global: &GlobalConfig{ + VSock: &VSockConfig{Enabled: ptr.To(true), Mode: ptr.To(VSockModeFull)}, + }, + Features: &DatadogFeatures{ + CWS: &CWSFeatureConfig{Enabled: ptr.To(true)}, + }, + }, + wantErr: false, + }, + { + name: "deprecated useVSock (maps to Full) + CWS without directSend - no error", + spec: &DatadogAgentSpec{ + Global: &GlobalConfig{ + UseVSock: ptr.To(true), + }, + Features: &DatadogFeatures{ + CWS: &CWSFeatureConfig{Enabled: ptr.To(true)}, + }, + }, + wantErr: false, + }, + { + name: "vsock SystemProbe mode + CWS disabled - no error", + spec: &DatadogAgentSpec{ + Global: &GlobalConfig{ + VSock: &VSockConfig{Enabled: ptr.To(true), Mode: ptr.To(VSockModeSystemProbe)}, + }, + Features: &DatadogFeatures{ + CWS: &CWSFeatureConfig{Enabled: ptr.To(false)}, + }, + }, + wantErr: false, + }, + { + name: "vsock SystemProbe mode + CWS enabled + directSend - no error", + spec: &DatadogAgentSpec{ + Global: &GlobalConfig{ + VSock: &VSockConfig{Enabled: ptr.To(true), Mode: ptr.To(VSockModeSystemProbe)}, + }, + Features: &DatadogFeatures{ + CWS: &CWSFeatureConfig{Enabled: ptr.To(true), DirectSendFromSystemProbe: ptr.To(true)}, + }, + }, + wantErr: false, + }, + { + name: "vsock SystemProbe mode + CWS enabled without directSend - error", + spec: &DatadogAgentSpec{ + Global: &GlobalConfig{ + VSock: &VSockConfig{Enabled: ptr.To(true), Mode: ptr.To(VSockModeSystemProbe)}, + }, + Features: &DatadogFeatures{ + CWS: &CWSFeatureConfig{Enabled: ptr.To(true)}, + }, + }, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := validateVSock(tt.spec) + if (err != nil) != tt.wantErr { + t.Errorf("validateVSock() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/api/datadoghq/v2alpha1/vsock.go b/api/datadoghq/v2alpha1/vsock.go new file mode 100644 index 0000000000..d85d34b4ae --- /dev/null +++ b/api/datadoghq/v2alpha1/vsock.go @@ -0,0 +1,38 @@ +// 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 v2alpha1 + +// GetVSockConfig resolves the effective VSock configuration from the GlobalConfig, +// taking into account the deprecated UseVSock field. +// +// The new VSock section takes precedence: when the VSock section is set, the +// deprecated UseVSock field is ignored. When the VSock section is absent, UseVSock +// is honored for backward compatibility and maps to the "full" mode. +// +// It returns whether VSock communication is enabled and the mode that controls which +// Agent components communicate over VSock (defaulting to VSockModeFull). +func (g *GlobalConfig) GetVSockConfig() (enabled bool, mode VSockMode) { + mode = VSockModeFull + if g == nil { + return false, mode + } + + if g.VSock != nil { + if g.VSock.Enabled != nil { + enabled = *g.VSock.Enabled + } + if g.VSock.Mode != nil { + mode = *g.VSock.Mode + } + return enabled, mode + } + + // Backward compatibility with the deprecated UseVSock field. + if g.UseVSock != nil { + enabled = *g.UseVSock + } + return enabled, mode +} diff --git a/api/datadoghq/v2alpha1/zz_generated.deepcopy.go b/api/datadoghq/v2alpha1/zz_generated.deepcopy.go index 3ce3956403..2afdfeba90 100644 --- a/api/datadoghq/v2alpha1/zz_generated.deepcopy.go +++ b/api/datadoghq/v2alpha1/zz_generated.deepcopy.go @@ -2196,6 +2196,11 @@ func (in *GlobalConfig) DeepCopyInto(out *GlobalConfig) { *out = new(bool) **out = **in } + if in.VSock != nil { + in, out := &in.VSock, &out.VSock + *out = new(VSockConfig) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GlobalConfig. @@ -3748,6 +3753,31 @@ func (in *UnixDomainSocketConfig) DeepCopy() *UnixDomainSocketConfig { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VSockConfig) DeepCopyInto(out *VSockConfig) { + *out = *in + if in.Enabled != nil { + in, out := &in.Enabled, &out.Enabled + *out = new(bool) + **out = **in + } + if in.Mode != nil { + in, out := &in.Mode, &out.Mode + *out = new(VSockMode) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VSockConfig. +func (in *VSockConfig) DeepCopy() *VSockConfig { + if in == nil { + return nil + } + out := new(VSockConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *WorkloadAutoscalingFeatureConfig) DeepCopyInto(out *WorkloadAutoscalingFeatureConfig) { *out = *in diff --git a/config/crd/bases/v1/datadoghq.com_datadogagentinternals.yaml b/config/crd/bases/v1/datadoghq.com_datadogagentinternals.yaml index 0fd41618bc..2dc6ec61c9 100644 --- a/config/crd/bases/v1/datadoghq.com_datadogagentinternals.yaml +++ b/config/crd/bases/v1/datadoghq.com_datadogagentinternals.yaml @@ -3689,7 +3689,26 @@ spec: description: |- UseVSock allows the use of VSock communication between the Agent and containerized workloads. Default: 'false' + Deprecated: Use VSock.Enabled instead. When VSock is set, this field is ignored. type: boolean + vsock: + description: VSock configures VSock communication for the Agent. + properties: + enabled: + description: |- + Enabled enables VSock communication. + Default: 'false' + type: boolean + mode: + description: |- + Mode controls which Agent components communicate over VSock. + "full" (default): all Agent components communicate over VSock. + "system-probe": only the CWS system-probe <=> micro VM system-probe communication uses VSock. + enum: + - full + - system-probe + type: string + type: object type: object override: additionalProperties: diff --git a/config/crd/bases/v1/datadoghq.com_datadogagentinternals_v1alpha1.json b/config/crd/bases/v1/datadoghq.com_datadogagentinternals_v1alpha1.json index 3c61a4a54a..353f3cef46 100644 --- a/config/crd/bases/v1/datadoghq.com_datadogagentinternals_v1alpha1.json +++ b/config/crd/bases/v1/datadoghq.com_datadogagentinternals_v1alpha1.json @@ -3823,8 +3823,27 @@ "type": "boolean" }, "useVSock": { - "description": "UseVSock allows the use of VSock communication between the Agent and containerized workloads.\nDefault: 'false'", + "description": "UseVSock allows the use of VSock communication between the Agent and containerized workloads.\nDefault: 'false'\nDeprecated: Use VSock.Enabled instead. When VSock is set, this field is ignored.", "type": "boolean" + }, + "vsock": { + "additionalProperties": false, + "description": "VSock configures VSock communication for the Agent.", + "properties": { + "enabled": { + "description": "Enabled enables VSock communication.\nDefault: 'false'", + "type": "boolean" + }, + "mode": { + "description": "Mode controls which Agent components communicate over VSock.\n\"full\" (default): all Agent components communicate over VSock.\n\"system-probe\": only the CWS system-probe \u003c=\u003e micro VM system-probe communication uses VSock.", + "enum": [ + "full", + "system-probe" + ], + "type": "string" + } + }, + "type": "object" } }, "type": "object" diff --git a/config/crd/bases/v1/datadoghq.com_datadogagentprofiles.yaml b/config/crd/bases/v1/datadoghq.com_datadogagentprofiles.yaml index 5bc20fa1e9..0d437de819 100644 --- a/config/crd/bases/v1/datadoghq.com_datadogagentprofiles.yaml +++ b/config/crd/bases/v1/datadoghq.com_datadogagentprofiles.yaml @@ -3689,7 +3689,26 @@ spec: description: |- UseVSock allows the use of VSock communication between the Agent and containerized workloads. Default: 'false' + Deprecated: Use VSock.Enabled instead. When VSock is set, this field is ignored. type: boolean + vsock: + description: VSock configures VSock communication for the Agent. + properties: + enabled: + description: |- + Enabled enables VSock communication. + Default: 'false' + type: boolean + mode: + description: |- + Mode controls which Agent components communicate over VSock. + "full" (default): all Agent components communicate over VSock. + "system-probe": only the CWS system-probe <=> micro VM system-probe communication uses VSock. + enum: + - full + - system-probe + type: string + type: object type: object override: additionalProperties: diff --git a/config/crd/bases/v1/datadoghq.com_datadogagentprofiles_v1alpha1.json b/config/crd/bases/v1/datadoghq.com_datadogagentprofiles_v1alpha1.json index a7488a18ca..f393859378 100644 --- a/config/crd/bases/v1/datadoghq.com_datadogagentprofiles_v1alpha1.json +++ b/config/crd/bases/v1/datadoghq.com_datadogagentprofiles_v1alpha1.json @@ -3827,8 +3827,27 @@ "type": "boolean" }, "useVSock": { - "description": "UseVSock allows the use of VSock communication between the Agent and containerized workloads.\nDefault: 'false'", + "description": "UseVSock allows the use of VSock communication between the Agent and containerized workloads.\nDefault: 'false'\nDeprecated: Use VSock.Enabled instead. When VSock is set, this field is ignored.", "type": "boolean" + }, + "vsock": { + "additionalProperties": false, + "description": "VSock configures VSock communication for the Agent.", + "properties": { + "enabled": { + "description": "Enabled enables VSock communication.\nDefault: 'false'", + "type": "boolean" + }, + "mode": { + "description": "Mode controls which Agent components communicate over VSock.\n\"full\" (default): all Agent components communicate over VSock.\n\"system-probe\": only the CWS system-probe \u003c=\u003e micro VM system-probe communication uses VSock.", + "enum": [ + "full", + "system-probe" + ], + "type": "string" + } + }, + "type": "object" } }, "type": "object" diff --git a/config/crd/bases/v1/datadoghq.com_datadogagents.yaml b/config/crd/bases/v1/datadoghq.com_datadogagents.yaml index 61b96259f8..7973f6c135 100644 --- a/config/crd/bases/v1/datadoghq.com_datadogagents.yaml +++ b/config/crd/bases/v1/datadoghq.com_datadogagents.yaml @@ -3693,7 +3693,26 @@ spec: description: |- UseVSock allows the use of VSock communication between the Agent and containerized workloads. Default: 'false' + Deprecated: Use VSock.Enabled instead. When VSock is set, this field is ignored. type: boolean + vsock: + description: VSock configures VSock communication for the Agent. + properties: + enabled: + description: |- + Enabled enables VSock communication. + Default: 'false' + type: boolean + mode: + description: |- + Mode controls which Agent components communicate over VSock. + "full" (default): all Agent components communicate over VSock. + "system-probe": only the CWS system-probe <=> micro VM system-probe communication uses VSock. + enum: + - full + - system-probe + type: string + type: object type: object override: additionalProperties: diff --git a/config/crd/bases/v1/datadoghq.com_datadogagents_v2alpha1.json b/config/crd/bases/v1/datadoghq.com_datadogagents_v2alpha1.json index 0f72aabd93..2c6fe56d22 100644 --- a/config/crd/bases/v1/datadoghq.com_datadogagents_v2alpha1.json +++ b/config/crd/bases/v1/datadoghq.com_datadogagents_v2alpha1.json @@ -3823,8 +3823,27 @@ "type": "boolean" }, "useVSock": { - "description": "UseVSock allows the use of VSock communication between the Agent and containerized workloads.\nDefault: 'false'", + "description": "UseVSock allows the use of VSock communication between the Agent and containerized workloads.\nDefault: 'false'\nDeprecated: Use VSock.Enabled instead. When VSock is set, this field is ignored.", "type": "boolean" + }, + "vsock": { + "additionalProperties": false, + "description": "VSock configures VSock communication for the Agent.", + "properties": { + "enabled": { + "description": "Enabled enables VSock communication.\nDefault: 'false'", + "type": "boolean" + }, + "mode": { + "description": "Mode controls which Agent components communicate over VSock.\n\"full\" (default): all Agent components communicate over VSock.\n\"system-probe\": only the CWS system-probe \u003c=\u003e micro VM system-probe communication uses VSock.", + "enum": [ + "full", + "system-probe" + ], + "type": "string" + } + }, + "type": "object" } }, "type": "object" diff --git a/docs/configuration.v2alpha1.md b/docs/configuration.v2alpha1.md index 2bc0e20346..9f0872cc7a 100644 --- a/docs/configuration.v2alpha1.md +++ b/docs/configuration.v2alpha1.md @@ -296,7 +296,9 @@ spec: | global.site | Is the Datadog intake site Agent data is sent to. Set this to your Datadog site ({{< region-param key="dd_site" code="true" >}}). Default: 'datadoghq.com' | | global.tags | Contains a list of tags to attach to every metric, event and service check collected. Learn more about tagging: https://docs.datadoghq.com/tagging/ | | global.useFIPSAgent | UseFIPSAgent enables the FIPS flavor of the Agent. If 'true', the FIPS proxy will always be disabled. Default: 'false' | -| global.useVSock | UseVSock allows the use of VSock communication between the Agent and containerized workloads. Default: 'false' | +| global.useVSock | UseVSock allows the use of VSock communication between the Agent and containerized workloads. Default: 'false' Deprecated: Use VSock.Enabled instead. When VSock is set, this field is ignored. | +| global.vsock.enabled | Enables VSock communication. Default: 'false' | +| global.vsock.mode | Controls which Agent components communicate over VSock. "full" (default): all Agent components communicate over VSock. "system-probe": only the CWS system-probe <=> micro VM system-probe communication uses VSock. | | override | The default configurations of the agents |
diff --git a/docs/configuration_public.md b/docs/configuration_public.md index c141925584..7305395ba0 100644 --- a/docs/configuration_public.md +++ b/docs/configuration_public.md @@ -577,7 +577,13 @@ spec: : UseFIPSAgent enables the FIPS flavor of the Agent. If 'true', the FIPS proxy will always be disabled. Default: 'false' `global.useVSock` -: UseVSock allows the use of VSock communication between the Agent and containerized workloads. Default: 'false' +: UseVSock allows the use of VSock communication between the Agent and containerized workloads. Default: 'false' Deprecated: Use VSock.Enabled instead. When VSock is set, this field is ignored. + +`global.vsock.enabled` +: Enables VSock communication. Default: 'false' + +`global.vsock.mode` +: Controls which Agent components communicate over VSock. "full" (default): all Agent components communicate over VSock. "system-probe": only the CWS system-probe <=> micro VM system-probe communication uses VSock. `override` : The default configurations of the agents diff --git a/internal/controller/datadogagent/feature/cws/feature.go b/internal/controller/datadogagent/feature/cws/feature.go index be61445ab6..9f8343232a 100644 --- a/internal/controller/datadogagent/feature/cws/feature.go +++ b/internal/controller/datadogagent/feature/cws/feature.go @@ -51,7 +51,8 @@ type cwsFeature struct { remoteConfigurationEnabled bool directSendFromSystemProbe bool enforcementEnabled bool - useVSock bool + vsockEnabled bool + vsockMode v2alpha1.VSockMode owner metav1.Object logger logr.Logger @@ -97,7 +98,7 @@ func (f *cwsFeature) Configure(dda metav1.Object, ddaSpec *v2alpha1.DatadogAgent f.enforcementEnabled = apiutils.BoolValue(cwsConfig.Enforcement.Enabled) } if ddaSpec.Global != nil { - f.useVSock = apiutils.BoolValue(ddaSpec.Global.UseVSock) + f.vsockEnabled, f.vsockMode = ddaSpec.Global.GetVSockConfig() } if cwsConfig.Network != nil { f.networkEnabled = apiutils.BoolValue(cwsConfig.Network.Enabled) @@ -222,21 +223,52 @@ func (f *cwsFeature) ManageNodeAgent(managers feature.PodTemplateManagers) error } managers.EnvVar().AddEnvVarToContainers(containersForEnvVars, enabledEnvVar) + // The runtime-security socket (DD_RUNTIME_SECURITY_CONFIG_SOCKET) carries the address + // used to communicate runtime-security events, and the event gRPC server selector + // (DD_RUNTIME_SECURITY_CONFIG_EVENT_GRPC_SERVER) selects which process hosts the event + // server. VSock is wired differently depending on the mode. socketPath := filepath.Join(common.SystemProbeSocketVolumePath, "runtime-security.sock") - if f.useVSock { - socketPath = "vsock:5020" - + switch { + case f.vsockEnabled && f.vsockMode == v2alpha1.VSockModeFull: + // Full mode: all CWS containers communicate over VSock and the security-agent hosts + // the runtime-security event gRPC server. managers.EnvVar().AddEnvVarToContainers(containersForEnvVars, &corev1.EnvVar{ Name: DDRuntimeSecurityConfigEventGRPCServer, Value: "security-agent", }) - } + managers.EnvVar().AddEnvVarToContainers(containersForEnvVars, &corev1.EnvVar{ + Name: DDRuntimeSecurityConfigSocket, + Value: "vsock:5020", + }) + + case f.vsockEnabled && f.vsockMode == v2alpha1.VSockModeSystemProbe: + // SystemProbe mode: VSock is scoped to the host system-probe only. It hosts a remote + // event server over VSock so the system-probe running inside the micro VM can forward + // its events. The core and security agents keep the regular unix socket. + managers.EnvVar().AddEnvVarToContainer(apicommon.SystemProbeContainerName, &corev1.EnvVar{ + Name: DDRuntimeSecurityConfigEventGRPCServer, + Value: "system-probe", + }) + managers.EnvVar().AddEnvVarToContainer(apicommon.SystemProbeContainerName, &corev1.EnvVar{ + Name: DDRuntimeSecurityConfigSocket, + Value: "vsock:5020", + }) + for _, container := range containersForEnvVars { + if container == apicommon.SystemProbeContainerName { + continue + } + managers.EnvVar().AddEnvVarToContainer(container, &corev1.EnvVar{ + Name: DDRuntimeSecurityConfigSocket, + Value: socketPath, + }) + } - runtimeSocketEnvVar := &corev1.EnvVar{ - Name: DDRuntimeSecurityConfigSocket, - Value: socketPath, + default: + managers.EnvVar().AddEnvVarToContainers(containersForEnvVars, &corev1.EnvVar{ + Name: DDRuntimeSecurityConfigSocket, + Value: socketPath, + }) } - managers.EnvVar().AddEnvVarToContainers(containersForEnvVars, runtimeSocketEnvVar) if f.syscallMonitorEnabled { monitorEnvVar := &corev1.EnvVar{ diff --git a/internal/controller/datadogagent/feature/cws/feature_test.go b/internal/controller/datadogagent/feature/cws/feature_test.go index d42cbb7dcf..c69ff9fa63 100644 --- a/internal/controller/datadogagent/feature/cws/feature_test.go +++ b/internal/controller/datadogagent/feature/cws/feature_test.go @@ -93,6 +93,24 @@ func Test_cwsFeature_Configure(t *testing.T) { ddaCWSLiteEnforcementEnabled.Spec.Features.CWS.Enforcement.Enabled = ptr.To(true) } + // Deprecated UseVSock maps to the "Full" VSock mode. + ddaCWSLiteVSockFull := ddaCWSLiteEnabled.DeepCopy() + { + ddaCWSLiteVSockFull.Spec.Global = &v2alpha1.GlobalConfig{ + UseVSock: ptr.To(true), + } + } + + ddaCWSLiteVSockSystemProbe := ddaCWSLiteEnabled.DeepCopy() + { + ddaCWSLiteVSockSystemProbe.Spec.Global = &v2alpha1.GlobalConfig{ + VSock: &v2alpha1.VSockConfig{ + Enabled: ptr.To(true), + Mode: ptr.To(v2alpha1.VSockModeSystemProbe), + }, + } + } + tests := test.FeatureTestSuite{ { Name: "v2alpha1 CWS not enabled", @@ -103,32 +121,44 @@ func Test_cwsFeature_Configure(t *testing.T) { Name: "v2alpha1 CWS enabled", DDA: ddaCWSLiteEnabled, WantConfigure: true, - Agent: cwsAgentNodeWantFunc(false, false, false), + Agent: cwsAgentNodeWantFunc(false, false, false, ""), }, { Name: "v2alpha1 CWS enabled (with network, security profiles and remote configuration)", DDA: ddaCWSFullEnabled, WantConfigure: true, - Agent: cwsAgentNodeWantFunc(true, false, false), + Agent: cwsAgentNodeWantFunc(true, false, false, ""), }, { Name: "v2alpha1 CWS enabled in direct sender mode", DDA: ddaCWSLiteDirectSendEnabled, WantConfigure: true, - Agent: cwsAgentNodeWantFunc(false, true, false), + Agent: cwsAgentNodeWantFunc(false, true, false, ""), }, { Name: "v2alpha1 CWS enabled with enforcement", DDA: ddaCWSLiteEnforcementEnabled, WantConfigure: true, - Agent: cwsAgentNodeWantFunc(false, false, true), + Agent: cwsAgentNodeWantFunc(false, false, true, ""), + }, + { + Name: "v2alpha1 CWS enabled with VSock (Full mode, deprecated useVSock)", + DDA: ddaCWSLiteVSockFull, + WantConfigure: true, + Agent: cwsAgentNodeWantFunc(false, false, false, v2alpha1.VSockModeFull), + }, + { + Name: "v2alpha1 CWS enabled with VSock (SystemProbe mode)", + DDA: ddaCWSLiteVSockSystemProbe, + WantConfigure: true, + Agent: cwsAgentNodeWantFunc(false, false, false, v2alpha1.VSockModeSystemProbe), }, } tests.Run(t, buildCWSFeature) } -func cwsAgentNodeWantFunc(withSubFeatures bool, directSendFromSysProbe bool, enforcementEnabled bool) *test.ComponentTest { +func cwsAgentNodeWantFunc(withSubFeatures bool, directSendFromSysProbe bool, enforcementEnabled bool, vsockMode v2alpha1.VSockMode) *test.ComponentTest { return test.NewDefaultComponentTest().WithWantFunc( func(t testing.TB, mgrInterface feature.PodTemplateManagers) { mgr := mgrInterface.(*fake.PodTemplateManagers) @@ -142,34 +172,79 @@ func cwsAgentNodeWantFunc(withSubFeatures bool, directSendFromSysProbe bool, enf } assert.True(t, apiutils.IsEqualStruct(sysProbeCapabilities, capabilitiesWant), "System Probe security context capabilities \ndiff = %s", cmp.Diff(sysProbeCapabilities, capabilitiesWant)) + // VSock changes the runtime-security socket and event gRPC server depending on the mode: + // - Full: all CWS containers use vsock; security-agent hosts the event server. + // - SystemProbe: only the system-probe uses vsock and hosts a remote event server + // for the micro VM system-probe; the other containers keep the unix socket. + unixSocket := "/var/run/sysprobe/runtime-security.sock" + securityWant := []*corev1.EnvVar{ { Name: DDRuntimeSecurityConfigEnabled, Value: "true", }, - { + } + if vsockMode == v2alpha1.VSockModeFull { + securityWant = append(securityWant, &corev1.EnvVar{ + Name: DDRuntimeSecurityConfigEventGRPCServer, + Value: "security-agent", + }) + } + securitySocket := unixSocket + if vsockMode == v2alpha1.VSockModeFull { + securitySocket = "vsock:5020" + } + securityWant = append(securityWant, + &corev1.EnvVar{ Name: DDRuntimeSecurityConfigSocket, - Value: "/var/run/sysprobe/runtime-security.sock", + Value: securitySocket, }, - { + &corev1.EnvVar{ Name: DDRuntimeSecurityConfigSyscallMonitorEnabled, Value: "true", }, - } + ) sysProbeWant := []*corev1.EnvVar{ { Name: DDRuntimeSecurityConfigEnabled, Value: "true", }, - { + } + switch vsockMode { + case v2alpha1.VSockModeFull: + sysProbeWant = append(sysProbeWant, + &corev1.EnvVar{ + Name: DDRuntimeSecurityConfigEventGRPCServer, + Value: "security-agent", + }, + &corev1.EnvVar{ + Name: DDRuntimeSecurityConfigSocket, + Value: "vsock:5020", + }, + ) + case v2alpha1.VSockModeSystemProbe: + sysProbeWant = append(sysProbeWant, + &corev1.EnvVar{ + Name: DDRuntimeSecurityConfigEventGRPCServer, + Value: "system-probe", + }, + &corev1.EnvVar{ + Name: DDRuntimeSecurityConfigSocket, + Value: "vsock:5020", + }, + ) + default: + sysProbeWant = append(sysProbeWant, &corev1.EnvVar{ Name: DDRuntimeSecurityConfigSocket, - Value: "/var/run/sysprobe/runtime-security.sock", - }, - { + Value: unixSocket, + }) + } + sysProbeWant = append(sysProbeWant, + &corev1.EnvVar{ Name: DDRuntimeSecurityConfigSyscallMonitorEnabled, Value: "true", }, - } + ) if withSubFeatures { sysProbeWant = append( sysProbeWant, diff --git a/internal/controller/datadogagent/global/agent.go b/internal/controller/datadogagent/global/agent.go index 27285fbcbe..3c994b0935 100644 --- a/internal/controller/datadogagent/global/agent.go +++ b/internal/controller/datadogagent/global/agent.go @@ -21,7 +21,11 @@ import ( func applyNodeAgentResources(manager feature.PodTemplateManagers, ddaSpec *v2alpha1.DatadogAgentSpec, singleContainerStrategyEnabled bool) { config := ddaSpec.Global - if apiutils.BoolValue(config.UseVSock) { + // In "full" mode, VSock communication is enabled for all Agent components. In + // "system-probe" mode, VSock is scoped to the CWS system-probe <=> micro VM + // communication only (handled by the CWS feature) and the node agent keeps using + // the regular TCP/unix socket transport. + if vsockEnabled, vsockMode := config.GetVSockConfig(); vsockEnabled && vsockMode == v2alpha1.VSockModeFull { manager.EnvVar().AddEnvVar(&corev1.EnvVar{ Name: DDVSockAddr, Value: "host", diff --git a/internal/controller/datadogagent/global/global_test.go b/internal/controller/datadogagent/global/global_test.go index cf15f1d6ef..39932e7773 100644 --- a/internal/controller/datadogagent/global/global_test.go +++ b/internal/controller/datadogagent/global/global_test.go @@ -135,7 +135,8 @@ func TestNodeAgentComponenGlobalSettings(t *testing.T) { want: assertAll, }, { - name: "VSock enabled", + // Deprecated UseVSock maps to the "Full" VSock mode: all node agents use VSock. + name: "VSock enabled (deprecated useVSock)", singleContainerStrategyEnabled: false, dda: func() *v2alpha1.DatadogAgent { dda := testutils.NewDatadogAgentBuilder(). @@ -145,54 +146,72 @@ func TestNodeAgentComponenGlobalSettings(t *testing.T) { return dda }(), wantCoreAgentEnvVars: nil, - wantEnvVars: getExpectedEnvVars([]*corev1.EnvVar{ - { - Name: constants.DDAPIKey, - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "-secret", - }, - Key: v2alpha1.DefaultAPIKeyKey, - }, - }, - }, - { - Name: constants.DDAppKey, - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "-secret", - }, - Key: v2alpha1.DefaultAPPKeyKey, - }, - }, + wantEnvVars: getExpectedEnvVars(append(credentialEnvVars(), + &corev1.EnvVar{ + Name: DDVSockAddr, + Value: "host", }, - { - Name: DDClusterAgentAuthToken, - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: "-token", - }, - Key: common.DefaultTokenKey, - }, - }, + &corev1.EnvVar{ + Name: DDRemoteAgentRegistryEnabled, + Value: "false", }, - { + )...), + wantCoreAgentVolumeMounts: nil, + wantVolumeMounts: nil, + wantVolumes: getExpectedVolumes(authVolume), + want: assertAll, + }, + { + // vsock.enabled without an explicit mode defaults to "Full": all node agents use VSock. + name: "VSock enabled (vsock.enabled, Full mode default)", + singleContainerStrategyEnabled: false, + dda: func() *v2alpha1.DatadogAgent { + dda := testutils.NewDatadogAgentBuilder(). + WithCredentials("apiKey", "appKey"). + BuildWithDefaults() + dda.Spec.Global.VSock = &v2alpha1.VSockConfig{ + Enabled: ptr.To(true), + } + return dda + }(), + wantCoreAgentEnvVars: nil, + wantEnvVars: getExpectedEnvVars(append(credentialEnvVars(), + &corev1.EnvVar{ Name: DDVSockAddr, Value: "host", }, - { + &corev1.EnvVar{ Name: DDRemoteAgentRegistryEnabled, Value: "false", }, - }...), + )...), wantCoreAgentVolumeMounts: nil, wantVolumeMounts: nil, wantVolumes: getExpectedVolumes(authVolume), want: assertAll, }, + { + // In "SystemProbe" mode, VSock is scoped to the CWS system-probe <=> micro VM + // communication only, so the node agent resources are not affected globally. + name: "VSock enabled (SystemProbe mode)", + singleContainerStrategyEnabled: false, + dda: func() *v2alpha1.DatadogAgent { + dda := testutils.NewDatadogAgentBuilder(). + WithCredentials("apiKey", "appKey"). + BuildWithDefaults() + dda.Spec.Global.VSock = &v2alpha1.VSockConfig{ + Enabled: ptr.To(true), + Mode: ptr.To(v2alpha1.VSockModeSystemProbe), + } + return dda + }(), + wantCoreAgentEnvVars: nil, + wantEnvVars: getExpectedEnvVars(credentialEnvVars()...), + wantCoreAgentVolumeMounts: nil, + wantVolumeMounts: nil, + wantVolumes: getExpectedVolumes(), + want: assertAll, + }, { name: "Kubelet volume configured", singleContainerStrategyEnabled: true, @@ -1073,6 +1092,44 @@ func assertAllAgentSingleContainer(t testing.TB, mgrInterface feature.PodTemplat assert.True(t, apiutils.IsEqualStruct(agentEnvVars, expectedEnvVars), "Agent envvars \ndiff = %s", cmp.Diff(agentEnvVars, expectedEnvVars)) } +func credentialEnvVars() []*corev1.EnvVar { + return []*corev1.EnvVar{ + { + Name: constants.DDAPIKey, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "-secret", + }, + Key: v2alpha1.DefaultAPIKeyKey, + }, + }, + }, + { + Name: constants.DDAppKey, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "-secret", + }, + Key: v2alpha1.DefaultAPPKeyKey, + }, + }, + }, + { + Name: DDClusterAgentAuthToken, + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "-token", + }, + Key: common.DefaultTokenKey, + }, + }, + }, + } +} + func getExpectedEnvVars(addedEnvVars ...*corev1.EnvVar) []*corev1.EnvVar { defaultEnvVars := []*corev1.EnvVar{ {