Skip to content
62 changes: 62 additions & 0 deletions go.work.sum

Large diffs are not rendered by default.

78 changes: 76 additions & 2 deletions internal/controller/datadogagent/feature/appsec/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import (
"fmt"
"slices"
"strconv"

"k8s.io/apimachinery/pkg/api/resource"
"k8s.io/apimachinery/pkg/util/validation"
)

type Config struct {
Expand All @@ -20,6 +23,17 @@ type Config struct {
ProcessorPort int
ProcessorServiceName string
ProcessorServiceNamespace string
// Sidecar injection mode fields
Mode string
SidecarImage string
SidecarImageTag string
SidecarPort string
SidecarHealthPort string
SidecarResourcesRequestsCPU string
SidecarResourcesRequestsMemory string
SidecarResourcesLimitsCPU string
SidecarResourcesLimitsMemory string
SidecarBodyParsingSizeLimit string
}

// FromAnnotations creates an appsec.Config from an annotation map and validates it.
Expand Down Expand Up @@ -64,6 +78,17 @@ func FromAnnotations(annotations map[string]string) (config Config, err error) {
}
}

config.Mode = annotations[AnnotationInjectorMode]
config.SidecarImage = annotations[AnnotationSidecarImage]
config.SidecarImageTag = annotations[AnnotationSidecarImageTag]
config.SidecarPort = annotations[AnnotationSidecarPort]
config.SidecarHealthPort = annotations[AnnotationSidecarHealthPort]
config.SidecarResourcesRequestsCPU = annotations[AnnotationSidecarResourcesRequestsCPU]
config.SidecarResourcesRequestsMemory = annotations[AnnotationSidecarResourcesRequestsMemory]
config.SidecarResourcesLimitsCPU = annotations[AnnotationSidecarResourcesLimitsCPU]
config.SidecarResourcesLimitsMemory = annotations[AnnotationSidecarResourcesLimitsMemory]
config.SidecarBodyParsingSizeLimit = annotations[AnnotationSidecarBodyParsingSizeLimit]

// Validate the configuration before returning
if err = config.Validate(); err != nil {
return config, fmt.Errorf("invalid configuration: %w", err)
Expand Down Expand Up @@ -99,10 +124,59 @@ func (c Config) Validate() error {
}
}

if c.isEnabled() && c.ProcessorServiceName == "" {
return fmt.Errorf("processor service name is required when AppSec is enabled (annotation: %s)",
if c.Mode != "" && c.Mode != "sidecar" && c.Mode != "external" {
return fmt.Errorf("invalid mode %q (allowed values: sidecar, external, annotation: %s)",
c.Mode, AnnotationInjectorMode)
}

// ProcessorServiceName is only required in external mode (not in sidecar mode, which is the default)
if c.isEnabled() && c.Mode == "external" && c.ProcessorServiceName == "" {
return fmt.Errorf("processor service name is required when AppSec is enabled in external mode (annotation: %s)",
AnnotationInjectorProcessorServiceName)
}

if err := validatePort(c.SidecarPort, AnnotationSidecarPort); err != nil {
return err
}

if err := validatePort(c.SidecarHealthPort, AnnotationSidecarHealthPort); err != nil {
return err
}

if c.SidecarBodyParsingSizeLimit != "" {
if _, err := strconv.ParseInt(c.SidecarBodyParsingSizeLimit, 10, 64); err != nil {
return fmt.Errorf("cannot parse annotation %q value: %w", AnnotationSidecarBodyParsingSizeLimit, err)
}
}

for val, annot := range map[string]string{
c.SidecarResourcesRequestsCPU: AnnotationSidecarResourcesRequestsCPU,
c.SidecarResourcesRequestsMemory: AnnotationSidecarResourcesRequestsMemory,
c.SidecarResourcesLimitsCPU: AnnotationSidecarResourcesLimitsCPU,
c.SidecarResourcesLimitsMemory: AnnotationSidecarResourcesLimitsMemory,
} {
if val != "" {
if _, err := resource.ParseQuantity(val); err != nil {
return fmt.Errorf("invalid resource quantity %q for annotation %s: %w",
val, annot, err)
}
}
}

return nil
}

// validatePort checks that a string port value, if non-empty, is a valid port number (1-65535).
func validatePort(portStr, annotation string) error {
if portStr == "" {
return nil
}
v, err := strconv.Atoi(portStr)
if err != nil {
return fmt.Errorf("cannot parse annotation %q value: %w", annotation, err)
}
if errs := validation.IsValidPortNum(v); len(errs) > 0 {
return fmt.Errorf("invalid port for annotation %q: %s", annotation, errs[0])
}
return nil
}
46 changes: 43 additions & 3 deletions internal/controller/datadogagent/feature/appsec/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

package appsec

const ClusterAgentMinVersion = "7.73.0"
const ClusterAgentMinVersion = "7.76.0"

// Appsec proxy injection annotations (Preview feature)
const (
Expand All @@ -19,10 +19,30 @@ const (
AnnotationInjectorProcessorAddress = "agent.datadoghq.com/appsec.injector.processor.address"
// AnnotationInjectorProcessorPort is the processor service port
AnnotationInjectorProcessorPort = "agent.datadoghq.com/appsec.injector.processor.port"
// AnnotationInjectorProcessorServiceName is the processor service name (required)
// AnnotationInjectorProcessorServiceName is the processor service name (required in external mode)
AnnotationInjectorProcessorServiceName = "agent.datadoghq.com/appsec.injector.processor.service.name"
// AnnotationInjectorProcessorServiceNamespace is the processor service namespace (optional, cluster-agent will use its own namespace if not specified)
AnnotationInjectorProcessorServiceNamespace = "agent.datadoghq.com/appsec.injector.processor.service.namespace"
// AnnotationInjectorMode is the injector mode (sidecar or external)
AnnotationInjectorMode = "agent.datadoghq.com/appsec.injector.mode"
// AnnotationSidecarImage is the sidecar container image
AnnotationSidecarImage = "agent.datadoghq.com/appsec.sidecar.image"
// AnnotationSidecarImageTag is the sidecar container image tag
AnnotationSidecarImageTag = "agent.datadoghq.com/appsec.sidecar.image_tag"
// AnnotationSidecarPort is the sidecar container port
AnnotationSidecarPort = "agent.datadoghq.com/appsec.sidecar.port"
// AnnotationSidecarHealthPort is the sidecar container health port
AnnotationSidecarHealthPort = "agent.datadoghq.com/appsec.sidecar.health_port"
// AnnotationSidecarResourcesRequestsCPU is the sidecar container CPU request
AnnotationSidecarResourcesRequestsCPU = "agent.datadoghq.com/appsec.sidecar.resources.requests.cpu"
// AnnotationSidecarResourcesRequestsMemory is the sidecar container memory request
AnnotationSidecarResourcesRequestsMemory = "agent.datadoghq.com/appsec.sidecar.resources.requests.memory"
// AnnotationSidecarResourcesLimitsCPU is the sidecar container CPU limit
AnnotationSidecarResourcesLimitsCPU = "agent.datadoghq.com/appsec.sidecar.resources.limits.cpu"
// AnnotationSidecarResourcesLimitsMemory is the sidecar container memory limit
AnnotationSidecarResourcesLimitsMemory = "agent.datadoghq.com/appsec.sidecar.resources.limits.memory"
// AnnotationSidecarBodyParsingSizeLimit is the sidecar body parsing size limit
AnnotationSidecarBodyParsingSizeLimit = "agent.datadoghq.com/appsec.sidecar.body_parsing_size_limit"
)

const (
Expand All @@ -42,9 +62,29 @@ const (
DDClusterAgentAppsecInjectorProcessorServiceName = "DD_CLUSTER_AGENT_APPSEC_INJECTOR_PROCESSOR_SERVICE_NAME"
// DDClusterAgentAppsecInjectorProcessorServiceNamespace is the processor service namespace
DDClusterAgentAppsecInjectorProcessorServiceNamespace = "DD_CLUSTER_AGENT_APPSEC_INJECTOR_PROCESSOR_SERVICE_NAMESPACE"
// DDClusterAgentAppsecInjectorMode is the injector mode (sidecar or external)
DDClusterAgentAppsecInjectorMode = "DD_CLUSTER_AGENT_APPSEC_INJECTOR_MODE"
// DDAdmissionControllerAppsecSidecarImage is the sidecar container image
DDAdmissionControllerAppsecSidecarImage = "DD_ADMISSION_CONTROLLER_APPSEC_SIDECAR_IMAGE"
// DDAdmissionControllerAppsecSidecarImageTag is the sidecar container image tag
DDAdmissionControllerAppsecSidecarImageTag = "DD_ADMISSION_CONTROLLER_APPSEC_SIDECAR_IMAGE_TAG"
// DDAdmissionControllerAppsecSidecarPort is the sidecar container port
DDAdmissionControllerAppsecSidecarPort = "DD_ADMISSION_CONTROLLER_APPSEC_SIDECAR_PORT"
// DDAdmissionControllerAppsecSidecarHealthPort is the sidecar container health port
DDAdmissionControllerAppsecSidecarHealthPort = "DD_ADMISSION_CONTROLLER_APPSEC_SIDECAR_HEALTH_PORT"
// DDAdmissionControllerAppsecSidecarResourcesRequestsCPU is the sidecar container CPU request
DDAdmissionControllerAppsecSidecarResourcesRequestsCPU = "DD_ADMISSION_CONTROLLER_APPSEC_SIDECAR_RESOURCES_REQUESTS_CPU"
// DDAdmissionControllerAppsecSidecarResourcesRequestsMemory is the sidecar container memory request
DDAdmissionControllerAppsecSidecarResourcesRequestsMemory = "DD_ADMISSION_CONTROLLER_APPSEC_SIDECAR_RESOURCES_REQUESTS_MEMORY"
// DDAdmissionControllerAppsecSidecarResourcesLimitsCPU is the sidecar container CPU limit
DDAdmissionControllerAppsecSidecarResourcesLimitsCPU = "DD_ADMISSION_CONTROLLER_APPSEC_SIDECAR_RESOURCES_LIMITS_CPU"
// DDAdmissionControllerAppsecSidecarResourcesLimitsMemory is the sidecar container memory limit
DDAdmissionControllerAppsecSidecarResourcesLimitsMemory = "DD_ADMISSION_CONTROLLER_APPSEC_SIDECAR_RESOURCES_LIMITS_MEMORY"
// DDAdmissionControllerAppsecSidecarBodyParsingSizeLimit is the sidecar body parsing size limit
DDAdmissionControllerAppsecSidecarBodyParsingSizeLimit = "DD_ADMISSION_CONTROLLER_APPSEC_SIDECAR_BODY_PARSING_SIZE_LIMIT"
)

var allowedProxyValues = []string{"envoy-gateway", "istio"}
var allowedProxyValues = []string{"envoy-gateway", "istio", "istio-gateway"}

// AllowedProxyValues returns the proxy types that the current RBAC supports.
// The returned slice must not be modified.
Expand Down
40 changes: 21 additions & 19 deletions internal/controller/datadogagent/feature/appsec/feature.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,31 +157,33 @@ func (f *appsecFeature) ManageClusterAgent(managers feature.PodTemplateManagers,
}
}

// Set processor port if specified
// Set processor port only when explicitly configured (zero means unset)
if f.config.ProcessorPort != 0 {
if err := addEnvVar(DDAppsecProxyProcessorPort, strconv.Itoa(f.config.ProcessorPort)); err != nil {
return err
}
}

// Set processor address if specified
if f.config.ProcessorAddress != "" {
if err := addEnvVar(DDAppsecProxyProcessorAddress, f.config.ProcessorAddress); err != nil {
return err
}
}

// Set processor service name if specified
if f.config.ProcessorServiceName != "" {
if err := addEnvVar(DDClusterAgentAppsecInjectorProcessorServiceName, f.config.ProcessorServiceName); err != nil {
return err
}
}

// Set processor service namespace if specified
if f.config.ProcessorServiceNamespace != "" {
if err := addEnvVar(DDClusterAgentAppsecInjectorProcessorServiceNamespace, f.config.ProcessorServiceNamespace); err != nil {
return err
// Set optional string env vars (key → value, skipped when value is empty)
for key, value := range map[string]string{
DDAppsecProxyProcessorAddress: f.config.ProcessorAddress,
DDClusterAgentAppsecInjectorProcessorServiceName: f.config.ProcessorServiceName,
DDClusterAgentAppsecInjectorProcessorServiceNamespace: f.config.ProcessorServiceNamespace,
DDClusterAgentAppsecInjectorMode: f.config.Mode,
DDAdmissionControllerAppsecSidecarImage: f.config.SidecarImage,
DDAdmissionControllerAppsecSidecarImageTag: f.config.SidecarImageTag,
DDAdmissionControllerAppsecSidecarPort: f.config.SidecarPort,
DDAdmissionControllerAppsecSidecarHealthPort: f.config.SidecarHealthPort,
DDAdmissionControllerAppsecSidecarResourcesRequestsCPU: f.config.SidecarResourcesRequestsCPU,
DDAdmissionControllerAppsecSidecarResourcesRequestsMemory: f.config.SidecarResourcesRequestsMemory,
DDAdmissionControllerAppsecSidecarResourcesLimitsCPU: f.config.SidecarResourcesLimitsCPU,
DDAdmissionControllerAppsecSidecarResourcesLimitsMemory: f.config.SidecarResourcesLimitsMemory,
DDAdmissionControllerAppsecSidecarBodyParsingSizeLimit: f.config.SidecarBodyParsingSizeLimit,
} {
if value != "" {
if err := addEnvVar(key, value); err != nil {
return err
}
}
}

Expand Down
Loading
Loading