From c5a1e56e6e52ad74928fec9c464fa15c483d072d Mon Sep 17 00:00:00 2001 From: Eliott Bouhana Date: Tue, 7 Apr 2026 18:28:44 +0200 Subject: [PATCH 1/4] Add RBAC for Gateway API, service mesh, and ingress controller CRDs Add opt-in `networkCRDs.enabled` boolean to OrchestratorExplorerFeatureConfig that grants list/watch RBAC for 15 API groups used by Gateway API, service mesh (Istio, Envoy Gateway, Traefik Legacy, Linkerd, Consul, Kuma), and ingress controllers (NGINX, Traefik, Kong, HAProxy). Uses resource-specific rules for high-volume vendors (Gateway API, Istio, NGINX, Traefik) and group-level wildcards for the long tail. Also replaces inline string literals in appsec RBAC with shared constants. --- api/datadoghq/v2alpha1/datadogagent_types.go | 16 ++++ .../v2alpha1/zz_generated.deepcopy.go | 25 ++++++ .../v2alpha1/zz_generated.openapi.go | 29 ++++++- .../datadoghq.com_datadogagentinternals.yaml | 24 ++++++ ...hq.com_datadogagentinternals_v1alpha1.json | 22 +++++ .../datadoghq.com_datadogagentprofiles.yaml | 12 +++ ...ghq.com_datadogagentprofiles_v1alpha1.json | 11 +++ .../bases/v1/datadoghq.com_datadogagents.yaml | 24 ++++++ .../datadoghq.com_datadogagents_v2alpha1.json | 22 +++++ docs/configuration.v2alpha1.md | 1 + docs/configuration_public.md | 3 + .../datadogagent/feature/appsec/rbac.go | 12 +-- .../datadogagent/feature/appsec/rbac_test.go | 12 +-- .../feature/orchestratorexplorer/feature.go | 28 ++++--- .../feature/orchestratorexplorer/rbac.go | 80 ++++++++++++++++++- .../feature/orchestratorexplorer/rbac_test.go | 2 +- pkg/kubernetes/rbac/const.go | 36 +++++++++ 17 files changed, 332 insertions(+), 27 deletions(-) diff --git a/api/datadoghq/v2alpha1/datadogagent_types.go b/api/datadoghq/v2alpha1/datadogagent_types.go index 3929616e1d..0cf92a6d4b 100644 --- a/api/datadoghq/v2alpha1/datadogagent_types.go +++ b/api/datadoghq/v2alpha1/datadogagent_types.go @@ -885,6 +885,22 @@ type OrchestratorExplorerFeatureConfig struct { // URL Default: "https://orchestrator.datadoghq.com". // +optional DDUrl *string `json:"ddUrl,omitempty"` + + // NetworkCRDs enables RBAC for collecting Gateway API, service mesh, + // and ingress controller CRDs in the orchestrator explorer. + // Default: false + // +optional + NetworkCRDs *NetworkCRDsConfig `json:"networkCRDs,omitempty"` +} + +// NetworkCRDsConfig contains the configuration for collecting Gateway API, service mesh, +// and ingress controller CRDs in the orchestrator explorer. +// +k8s:openapi-gen=true +type NetworkCRDsConfig struct { + // Enabled enables collection of network topology CRDs. + // Default: false + // +optional + Enabled *bool `json:"enabled,omitempty"` } // KubeStateMetricsCoreFeatureConfig contains the Kube State Metrics Core check feature configuration. diff --git a/api/datadoghq/v2alpha1/zz_generated.deepcopy.go b/api/datadoghq/v2alpha1/zz_generated.deepcopy.go index 9c768f10ab..f2aa017e38 100644 --- a/api/datadoghq/v2alpha1/zz_generated.deepcopy.go +++ b/api/datadoghq/v2alpha1/zz_generated.deepcopy.go @@ -2756,6 +2756,26 @@ func (in *NamespaceSelector) DeepCopy() *NamespaceSelector { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NetworkCRDsConfig) DeepCopyInto(out *NetworkCRDsConfig) { + *out = *in + if in.Enabled != nil { + in, out := &in.Enabled, &out.Enabled + *out = new(bool) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkCRDsConfig. +func (in *NetworkCRDsConfig) DeepCopy() *NetworkCRDsConfig { + if in == nil { + return nil + } + out := new(NetworkCRDsConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *NetworkPolicyConfig) DeepCopyInto(out *NetworkPolicyConfig) { *out = *in @@ -2953,6 +2973,11 @@ func (in *OrchestratorExplorerFeatureConfig) DeepCopyInto(out *OrchestratorExplo *out = new(string) **out = **in } + if in.NetworkCRDs != nil { + in, out := &in.NetworkCRDs, &out.NetworkCRDs + *out = new(NetworkCRDsConfig) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OrchestratorExplorerFeatureConfig. diff --git a/api/datadoghq/v2alpha1/zz_generated.openapi.go b/api/datadoghq/v2alpha1/zz_generated.openapi.go index e35c857471..4baf7c28bc 100644 --- a/api/datadoghq/v2alpha1/zz_generated.openapi.go +++ b/api/datadoghq/v2alpha1/zz_generated.openapi.go @@ -40,6 +40,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.KubeStateMetricsCoreFeatureConfig": schema_datadog_operator_api_datadoghq_v2alpha1_KubeStateMetricsCoreFeatureConfig(ref), "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.LocalService": schema_datadog_operator_api_datadoghq_v2alpha1_LocalService(ref), "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.MultiCustomConfig": schema_datadog_operator_api_datadoghq_v2alpha1_MultiCustomConfig(ref), + "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.NetworkCRDsConfig": schema_datadog_operator_api_datadoghq_v2alpha1_NetworkCRDsConfig(ref), "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.NetworkPolicyConfig": schema_datadog_operator_api_datadoghq_v2alpha1_NetworkPolicyConfig(ref), "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.OTLPFeatureConfig": schema_datadog_operator_api_datadoghq_v2alpha1_OTLPFeatureConfig(ref), "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.OTLPGRPCConfig": schema_datadog_operator_api_datadoghq_v2alpha1_OTLPGRPCConfig(ref), @@ -1377,6 +1378,26 @@ func schema_datadog_operator_api_datadoghq_v2alpha1_MultiCustomConfig(ref common } } +func schema_datadog_operator_api_datadoghq_v2alpha1_NetworkCRDsConfig(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "NetworkCRDsConfig contains the configuration for collecting Gateway API, service mesh, and ingress controller CRDs in the orchestrator explorer.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "enabled": { + SchemaProps: spec.SchemaProps{ + Description: "Enabled enables collection of network topology CRDs. Default: false", + Type: []string{"boolean"}, + Format: "", + }, + }, + }, + }, + }, + } +} + func schema_datadog_operator_api_datadoghq_v2alpha1_NetworkPolicyConfig(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -1640,11 +1661,17 @@ func schema_datadog_operator_api_datadoghq_v2alpha1_OrchestratorExplorerFeatureC Format: "", }, }, + "networkCRDs": { + SchemaProps: spec.SchemaProps{ + Description: "NetworkCRDs enables RBAC for collecting Gateway API, service mesh, and ingress controller CRDs in the orchestrator explorer. Default: false", + Ref: ref("github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.NetworkCRDsConfig"), + }, + }, }, }, }, Dependencies: []string{ - "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.CustomConfig"}, + "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.CustomConfig", "github.com/DataDog/datadog-operator/api/datadoghq/v2alpha1.NetworkCRDsConfig"}, } } diff --git a/config/crd/bases/v1/datadoghq.com_datadogagentinternals.yaml b/config/crd/bases/v1/datadoghq.com_datadogagentinternals.yaml index 30ad410214..c4c8442a6a 100644 --- a/config/crd/bases/v1/datadoghq.com_datadogagentinternals.yaml +++ b/config/crd/bases/v1/datadoghq.com_datadogagentinternals.yaml @@ -2107,6 +2107,18 @@ spec: type: string type: array x-kubernetes-list-type: set + networkCRDs: + description: |- + NetworkCRDs enables RBAC for collecting Gateway API, service mesh, + and ingress controller CRDs in the orchestrator explorer. + Default: false + properties: + enabled: + description: |- + Enabled enables collection of network topology CRDs. + Default: false + type: boolean + type: object scrubContainers: description: |- ScrubContainers enables scrubbing of sensitive container data (passwords, tokens, etc. ). @@ -10539,6 +10551,18 @@ spec: type: string type: array x-kubernetes-list-type: set + networkCRDs: + description: |- + NetworkCRDs enables RBAC for collecting Gateway API, service mesh, + and ingress controller CRDs in the orchestrator explorer. + Default: false + properties: + enabled: + description: |- + Enabled enables collection of network topology CRDs. + Default: false + type: boolean + type: object scrubContainers: description: |- ScrubContainers enables scrubbing of sensitive container data (passwords, tokens, etc. ). diff --git a/config/crd/bases/v1/datadoghq.com_datadogagentinternals_v1alpha1.json b/config/crd/bases/v1/datadoghq.com_datadogagentinternals_v1alpha1.json index b6e0f69009..50cfb8aeed 100644 --- a/config/crd/bases/v1/datadoghq.com_datadogagentinternals_v1alpha1.json +++ b/config/crd/bases/v1/datadoghq.com_datadogagentinternals_v1alpha1.json @@ -2174,6 +2174,17 @@ "type": "array", "x-kubernetes-list-type": "set" }, + "networkCRDs": { + "additionalProperties": false, + "description": "NetworkCRDs enables RBAC for collecting Gateway API, service mesh,\nand ingress controller CRDs in the orchestrator explorer.\nDefault: false", + "properties": { + "enabled": { + "description": "Enabled enables collection of network topology CRDs.\nDefault: false", + "type": "boolean" + } + }, + "type": "object" + }, "scrubContainers": { "description": "ScrubContainers enables scrubbing of sensitive container data (passwords, tokens, etc. ).\nDefault: true", "type": "boolean" @@ -10343,6 +10354,17 @@ "type": "array", "x-kubernetes-list-type": "set" }, + "networkCRDs": { + "additionalProperties": false, + "description": "NetworkCRDs enables RBAC for collecting Gateway API, service mesh,\nand ingress controller CRDs in the orchestrator explorer.\nDefault: false", + "properties": { + "enabled": { + "description": "Enabled enables collection of network topology CRDs.\nDefault: false", + "type": "boolean" + } + }, + "type": "object" + }, "scrubContainers": { "description": "ScrubContainers enables scrubbing of sensitive container data (passwords, tokens, etc. ).\nDefault: true", "type": "boolean" diff --git a/config/crd/bases/v1/datadoghq.com_datadogagentprofiles.yaml b/config/crd/bases/v1/datadoghq.com_datadogagentprofiles.yaml index afd1b0e507..dcdc5be967 100644 --- a/config/crd/bases/v1/datadoghq.com_datadogagentprofiles.yaml +++ b/config/crd/bases/v1/datadoghq.com_datadogagentprofiles.yaml @@ -2107,6 +2107,18 @@ spec: type: string type: array x-kubernetes-list-type: set + networkCRDs: + description: |- + NetworkCRDs enables RBAC for collecting Gateway API, service mesh, + and ingress controller CRDs in the orchestrator explorer. + Default: false + properties: + enabled: + description: |- + Enabled enables collection of network topology CRDs. + Default: false + type: boolean + type: object scrubContainers: description: |- ScrubContainers enables scrubbing of sensitive container data (passwords, tokens, etc. ). diff --git a/config/crd/bases/v1/datadoghq.com_datadogagentprofiles_v1alpha1.json b/config/crd/bases/v1/datadoghq.com_datadogagentprofiles_v1alpha1.json index 3185fb8533..56f46a04d0 100644 --- a/config/crd/bases/v1/datadoghq.com_datadogagentprofiles_v1alpha1.json +++ b/config/crd/bases/v1/datadoghq.com_datadogagentprofiles_v1alpha1.json @@ -2178,6 +2178,17 @@ "type": "array", "x-kubernetes-list-type": "set" }, + "networkCRDs": { + "additionalProperties": false, + "description": "NetworkCRDs enables RBAC for collecting Gateway API, service mesh,\nand ingress controller CRDs in the orchestrator explorer.\nDefault: false", + "properties": { + "enabled": { + "description": "Enabled enables collection of network topology CRDs.\nDefault: false", + "type": "boolean" + } + }, + "type": "object" + }, "scrubContainers": { "description": "ScrubContainers enables scrubbing of sensitive container data (passwords, tokens, etc. ).\nDefault: true", "type": "boolean" diff --git a/config/crd/bases/v1/datadoghq.com_datadogagents.yaml b/config/crd/bases/v1/datadoghq.com_datadogagents.yaml index 22ecfd013a..a1f35b698b 100644 --- a/config/crd/bases/v1/datadoghq.com_datadogagents.yaml +++ b/config/crd/bases/v1/datadoghq.com_datadogagents.yaml @@ -2111,6 +2111,18 @@ spec: type: string type: array x-kubernetes-list-type: set + networkCRDs: + description: |- + NetworkCRDs enables RBAC for collecting Gateway API, service mesh, + and ingress controller CRDs in the orchestrator explorer. + Default: false + properties: + enabled: + description: |- + Enabled enables collection of network topology CRDs. + Default: false + type: boolean + type: object scrubContainers: description: |- ScrubContainers enables scrubbing of sensitive container data (passwords, tokens, etc. ). @@ -10619,6 +10631,18 @@ spec: type: string type: array x-kubernetes-list-type: set + networkCRDs: + description: |- + NetworkCRDs enables RBAC for collecting Gateway API, service mesh, + and ingress controller CRDs in the orchestrator explorer. + Default: false + properties: + enabled: + description: |- + Enabled enables collection of network topology CRDs. + Default: false + type: boolean + type: object scrubContainers: description: |- ScrubContainers enables scrubbing of sensitive container data (passwords, tokens, etc. ). diff --git a/config/crd/bases/v1/datadoghq.com_datadogagents_v2alpha1.json b/config/crd/bases/v1/datadoghq.com_datadogagents_v2alpha1.json index 84ba9c4e7d..e7b565c055 100644 --- a/config/crd/bases/v1/datadoghq.com_datadogagents_v2alpha1.json +++ b/config/crd/bases/v1/datadoghq.com_datadogagents_v2alpha1.json @@ -2174,6 +2174,17 @@ "type": "array", "x-kubernetes-list-type": "set" }, + "networkCRDs": { + "additionalProperties": false, + "description": "NetworkCRDs enables RBAC for collecting Gateway API, service mesh,\nand ingress controller CRDs in the orchestrator explorer.\nDefault: false", + "properties": { + "enabled": { + "description": "Enabled enables collection of network topology CRDs.\nDefault: false", + "type": "boolean" + } + }, + "type": "object" + }, "scrubContainers": { "description": "ScrubContainers enables scrubbing of sensitive container data (passwords, tokens, etc. ).\nDefault: true", "type": "boolean" @@ -10436,6 +10447,17 @@ "type": "array", "x-kubernetes-list-type": "set" }, + "networkCRDs": { + "additionalProperties": false, + "description": "NetworkCRDs enables RBAC for collecting Gateway API, service mesh,\nand ingress controller CRDs in the orchestrator explorer.\nDefault: false", + "properties": { + "enabled": { + "description": "Enabled enables collection of network topology CRDs.\nDefault: false", + "type": "boolean" + } + }, + "type": "object" + }, "scrubContainers": { "description": "ScrubContainers enables scrubbing of sensitive container data (passwords, tokens, etc. ).\nDefault: true", "type": "boolean" diff --git a/docs/configuration.v2alpha1.md b/docs/configuration.v2alpha1.md index 3705d38cdd..0ac8a2cbea 100644 --- a/docs/configuration.v2alpha1.md +++ b/docs/configuration.v2alpha1.md @@ -165,6 +165,7 @@ spec: | features.orchestratorExplorer.ddUrl | Override the API endpoint for the Orchestrator Explorer. URL Default: "https://orchestrator.datadoghq.com". | | features.orchestratorExplorer.enabled | Enables the Orchestrator Explorer. Default: true | | features.orchestratorExplorer.extraTags | Additional tags to associate with the collected data in the form of `a b c`. This is a Cluster Agent option distinct from DD_TAGS that is used in the Orchestrator Explorer. | +| features.orchestratorExplorer.networkCRDs.enabled | Enables collection of network topology CRDs. Default: false | | features.orchestratorExplorer.scrubContainers | ScrubContainers enables scrubbing of sensitive container data (passwords, tokens, etc. ). Default: true | | features.otelAgentGateway.conf.configData | ConfigData corresponds to the configuration file content. | | features.otelAgentGateway.conf.configMap.items | Maps a ConfigMap data `key` to a file `path` mount. | diff --git a/docs/configuration_public.md b/docs/configuration_public.md index c536cfd615..74fb01e79d 100644 --- a/docs/configuration_public.md +++ b/docs/configuration_public.md @@ -321,6 +321,9 @@ spec: `features.orchestratorExplorer.extraTags` : Additional tags to associate with the collected data in the form of `a b c`. This is a Cluster Agent option distinct from DD_TAGS that is used in the Orchestrator Explorer. +`features.orchestratorExplorer.networkCRDs.enabled` +: Enables collection of network topology CRDs. Default: false + `features.orchestratorExplorer.scrubContainers` : ScrubContainers enables scrubbing of sensitive container data (passwords, tokens, etc. ). Default: true diff --git a/internal/controller/datadogagent/feature/appsec/rbac.go b/internal/controller/datadogagent/feature/appsec/rbac.go index a36904d981..5bbb6c8201 100644 --- a/internal/controller/datadogagent/feature/appsec/rbac.go +++ b/internal/controller/datadogagent/feature/appsec/rbac.go @@ -55,9 +55,9 @@ func getRBACPolicyRules() []rbacv1.PolicyRule { { APIGroups: []string{rbac.GatewayAPIGroup}, Resources: []string{ - "gateways", + rbac.GatewaysResource, "gatewayclasses", - "httproutes", + rbac.HTTPRoutesResource, }, Verbs: []string{ rbac.GetVerb, @@ -77,7 +77,7 @@ func getRBACPolicyRules() []rbacv1.PolicyRule { }, }, { - APIGroups: []string{"gateway.envoyproxy.io"}, + APIGroups: []string{rbac.EnvoyGatewayAPIGroup}, Resources: []string{"envoyextensionpolicies", "envoypatchpolicies", "backend"}, Verbs: []string{ rbac.GetVerb, @@ -87,7 +87,7 @@ func getRBACPolicyRules() []rbacv1.PolicyRule { }, // Istio resources { - APIGroups: []string{"networking.istio.io"}, + APIGroups: []string{rbac.IstioNetworkingAPIGroup}, Resources: []string{"envoyfilters"}, Verbs: []string{ rbac.GetVerb, @@ -96,8 +96,8 @@ func getRBACPolicyRules() []rbacv1.PolicyRule { }, }, { - APIGroups: []string{"networking.istio.io"}, - Resources: []string{"gateways"}, + APIGroups: []string{rbac.IstioNetworkingAPIGroup}, + Resources: []string{rbac.GatewaysResource}, Verbs: []string{ rbac.GetVerb, rbac.ListVerb, diff --git a/internal/controller/datadogagent/feature/appsec/rbac_test.go b/internal/controller/datadogagent/feature/appsec/rbac_test.go index 6fb455ecad..b19d22f06d 100644 --- a/internal/controller/datadogagent/feature/appsec/rbac_test.go +++ b/internal/controller/datadogagent/feature/appsec/rbac_test.go @@ -56,9 +56,9 @@ func TestAppsecRBACPolicyRules(t *testing.T) { for _, rule := range rules { if len(rule.APIGroups) > 0 && rule.APIGroups[0] == rbac.GatewayAPIGroup { if len(rule.Resources) > 2 { // The main gateway rule has gateways, gatewayclasses, httproutes - assert.Contains(t, rule.Resources, "gateways", "Should have gateways permission") + assert.Contains(t, rule.Resources, rbac.GatewaysResource, "Should have gateways permission") assert.Contains(t, rule.Resources, "gatewayclasses", "Should have gatewayclasses permission") - assert.Contains(t, rule.Resources, "httproutes", "Should have httproutes permission") + assert.Contains(t, rule.Resources, rbac.HTTPRoutesResource, "Should have httproutes permission") assert.Contains(t, rule.Verbs, rbac.GetVerb) assert.Contains(t, rule.Verbs, rbac.ListVerb) assert.Contains(t, rule.Verbs, rbac.WatchVerb) @@ -72,7 +72,7 @@ func TestAppsecRBACPolicyRules(t *testing.T) { // Test Istio EnvoyFilter permissions var foundIstioRule bool for _, rule := range rules { - if len(rule.APIGroups) > 0 && rule.APIGroups[0] == "networking.istio.io" { + if len(rule.APIGroups) > 0 && rule.APIGroups[0] == rbac.IstioNetworkingAPIGroup { if len(rule.Resources) > 0 && rule.Resources[0] == "envoyfilters" { assert.Contains(t, rule.Resources, "envoyfilters") assert.Contains(t, rule.Verbs, rbac.GetVerb) @@ -87,8 +87,8 @@ func TestAppsecRBACPolicyRules(t *testing.T) { // Test Istio Gateway watching permissions var foundIstioGatewayRule bool for _, rule := range rules { - if len(rule.APIGroups) > 0 && rule.APIGroups[0] == "networking.istio.io" { - if len(rule.Resources) > 0 && rule.Resources[0] == "gateways" { + if len(rule.APIGroups) > 0 && rule.APIGroups[0] == rbac.IstioNetworkingAPIGroup { + if len(rule.Resources) > 0 && rule.Resources[0] == rbac.GatewaysResource { assert.Contains(t, rule.Verbs, rbac.GetVerb) assert.Contains(t, rule.Verbs, rbac.ListVerb) assert.Contains(t, rule.Verbs, rbac.WatchVerb) @@ -101,7 +101,7 @@ func TestAppsecRBACPolicyRules(t *testing.T) { // Test Envoy Gateway permissions var foundEnvoyRule bool for _, rule := range rules { - if len(rule.APIGroups) > 0 && rule.APIGroups[0] == "gateway.envoyproxy.io" { + if len(rule.APIGroups) > 0 && rule.APIGroups[0] == rbac.EnvoyGatewayAPIGroup { assert.Contains(t, rule.Resources, "envoyextensionpolicies") assert.Contains(t, rule.Verbs, rbac.GetVerb) assert.Contains(t, rule.Verbs, rbac.CreateVerb) diff --git a/internal/controller/datadogagent/feature/orchestratorexplorer/feature.go b/internal/controller/datadogagent/feature/orchestratorexplorer/feature.go index 1e87c7a1b8..b79e434973 100644 --- a/internal/controller/datadogagent/feature/orchestratorexplorer/feature.go +++ b/internal/controller/datadogagent/feature/orchestratorexplorer/feature.go @@ -52,17 +52,18 @@ func buildOrchestratorExplorerFeature(options *feature.Options) feature.Feature } type orchestratorExplorerFeature struct { - enabled bool - runInClusterChecksRunner bool - scrubContainers bool - extraTags []string - ddURL string - rbacSuffix string - serviceAccountName string - owner metav1.Object - customConfig *v2alpha1.CustomConfig - customResources []string - configConfigMapName string + enabled bool + runInClusterChecksRunner bool + scrubContainers bool + collectKubernetesNetworkResources bool + extraTags []string + ddURL string + rbacSuffix string + serviceAccountName string + owner metav1.Object + customConfig *v2alpha1.CustomConfig + customResources []string + configConfigMapName string logger logr.Logger customConfigAnnotationKey string @@ -108,6 +109,9 @@ func (f *orchestratorExplorerFeature) Configure(dda metav1.Object, ddaSpec *v2al } f.customResources = ddaSpec.Features.OrchestratorExplorer.CustomResources + if orchestratorExplorer.NetworkCRDs != nil { + f.collectKubernetesNetworkResources = apiutils.BoolValue(orchestratorExplorer.NetworkCRDs.Enabled) + } f.configConfigMapName = constants.GetConfName(dda, f.customConfig, defaultOrchestratorExplorerConf) f.scrubContainers = apiutils.BoolValue(orchestratorExplorer.ScrubContainers) f.extraTags = orchestratorExplorer.ExtraTags @@ -206,7 +210,7 @@ func (f *orchestratorExplorerFeature) ManageDependencies(managers feature.Resour // Manage RBAC permission rbacName := GetOrchestratorExplorerRBACResourceName(f.owner, f.rbacSuffix) - return managers.RBACManager().AddClusterPolicyRules(f.owner.GetNamespace(), rbacName, f.serviceAccountName, getRBACPolicyRules(f.logger, f.customResources)) + return managers.RBACManager().AddClusterPolicyRules(f.owner.GetNamespace(), rbacName, f.serviceAccountName, getRBACPolicyRules(f.logger, f.customResources, f.collectKubernetesNetworkResources)) } // ManageClusterAgent allows a feature to configure the ClusterAgent's corev1.PodTemplateSpec diff --git a/internal/controller/datadogagent/feature/orchestratorexplorer/rbac.go b/internal/controller/datadogagent/feature/orchestratorexplorer/rbac.go index f095693283..21705d03f1 100644 --- a/internal/controller/datadogagent/feature/orchestratorexplorer/rbac.go +++ b/internal/controller/datadogagent/feature/orchestratorexplorer/rbac.go @@ -18,7 +18,7 @@ import ( ) // getRBACRules generates the cluster role permissions required for the orchestrator explorer feature -func getRBACPolicyRules(logger logr.Logger, crs []string) []rbacv1.PolicyRule { +func getRBACPolicyRules(logger logr.Logger, crs []string, collectKubernetesNetworkResources bool) []rbacv1.PolicyRule { rbacRules := []rbacv1.PolicyRule{ // To get the kube-system namespace UID and generate a cluster ID { @@ -144,6 +144,84 @@ func getRBACPolicyRules(logger logr.Logger, crs []string) []rbacv1.PolicyRule { }, } + if collectKubernetesNetworkResources { + rbacRules = append(rbacRules, + // Gateway API — resource-specific + rbacv1.PolicyRule{ + APIGroups: []string{rbac.GatewayAPIGroup}, + Resources: []string{ + rbac.GatewaysResource, + rbac.HTTPRoutesResource, + rbac.GRPCRoutesResource, + rbac.TLSRoutesResource, + rbac.ListenerSetsResource, + }, + }, + // Service Mesh — Istio: resource-specific + rbacv1.PolicyRule{ + APIGroups: []string{rbac.IstioNetworkingAPIGroup}, + Resources: []string{ + rbac.VirtualServicesResource, + rbac.GatewaysResource, + rbac.DestinationRulesResource, + rbac.ServiceEntriesResource, + rbac.SidecarsResource, + }, + }, + // Service Mesh — Others: group-level + rbacv1.PolicyRule{ + APIGroups: []string{rbac.EnvoyGatewayAPIGroup}, + Resources: []string{rbac.Wildcard}, + }, + rbacv1.PolicyRule{ + APIGroups: []string{rbac.TraefikLegacyAPIGroup}, + Resources: []string{rbac.Wildcard}, + }, + rbacv1.PolicyRule{ + APIGroups: []string{rbac.LinkerdPolicyAPIGroup}, + Resources: []string{rbac.Wildcard}, + }, + rbacv1.PolicyRule{ + APIGroups: []string{rbac.ConsulAPIGroup}, + Resources: []string{rbac.Wildcard}, + }, + rbacv1.PolicyRule{ + APIGroups: []string{rbac.ConsulMeshAPIGroup}, + Resources: []string{rbac.Wildcard}, + }, + rbacv1.PolicyRule{ + APIGroups: []string{rbac.KumaAPIGroup}, + Resources: []string{rbac.Wildcard}, + }, + // Ingress Controllers — NGINX: resource-specific + rbacv1.PolicyRule{ + APIGroups: []string{rbac.NginxAPIGroup}, + Resources: []string{ + rbac.VirtualServersResource, + rbac.VirtualServerRoutesResource, + }, + }, + // Ingress Controllers — Traefik: resource-specific + rbacv1.PolicyRule{ + APIGroups: []string{rbac.TraefikAPIGroup}, + Resources: []string{rbac.IngressRoutesResource}, + }, + // Ingress Controllers — Others: group-level + rbacv1.PolicyRule{ + APIGroups: []string{rbac.KongAPIGroup}, + Resources: []string{rbac.Wildcard}, + }, + rbacv1.PolicyRule{ + APIGroups: []string{rbac.HAProxyCoreAPIGroup}, + Resources: []string{rbac.Wildcard}, + }, + rbacv1.PolicyRule{ + APIGroups: []string{rbac.HAProxyIngressV1APIGroup}, + Resources: []string{rbac.Wildcard}, + }, + ) + } + if len(crs) > 0 { for _, cr := range crs { crSplit := strings.Split(cr, "/") diff --git a/internal/controller/datadogagent/feature/orchestratorexplorer/rbac_test.go b/internal/controller/datadogagent/feature/orchestratorexplorer/rbac_test.go index d45134fb67..6e17342d80 100644 --- a/internal/controller/datadogagent/feature/orchestratorexplorer/rbac_test.go +++ b/internal/controller/datadogagent/feature/orchestratorexplorer/rbac_test.go @@ -105,7 +105,7 @@ func TestGetRBACPolicyRules(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - rules := getRBACPolicyRules(logger, tt.customResources) + rules := getRBACPolicyRules(logger, tt.customResources, false) // check base rules for _, expectedRule := range expectedBaseRules { diff --git a/pkg/kubernetes/rbac/const.go b/pkg/kubernetes/rbac/const.go index 871702ada8..4dee562e4a 100644 --- a/pkg/kubernetes/rbac/const.go +++ b/pkg/kubernetes/rbac/const.go @@ -42,6 +42,22 @@ const ( KarpenterAWSAPIGroup = "karpenter.k8s.aws" KarpenterAzureAPIGroup = "karpenter.azure.com" + // Service Mesh API groups + IstioNetworkingAPIGroup = "networking.istio.io" + EnvoyGatewayAPIGroup = "gateway.envoyproxy.io" + TraefikLegacyAPIGroup = "traefik.containo.us" + LinkerdPolicyAPIGroup = "policy.linkerd.io" + ConsulAPIGroup = "consul.hashicorp.com" + ConsulMeshAPIGroup = "mesh.consul.hashicorp.com" + KumaAPIGroup = "kuma.io" + + // Ingress Controller API groups + NginxAPIGroup = "k8s.nginx.org" + TraefikAPIGroup = "traefik.io" + KongAPIGroup = "configuration.konghq.com" + HAProxyCoreAPIGroup = "core.haproxy.org" + HAProxyIngressV1APIGroup = "ingress.v1.haproxy.org" + // Resources APIServicesResource = "apiservices" @@ -107,6 +123,26 @@ const ( Helmrepositories = "helmrepositories" Ocirepositories = "ocirepositories" Kustomizations = "kustomizations" + + // Gateway API resources + GatewaysResource = "gateways" + HTTPRoutesResource = "httproutes" + GRPCRoutesResource = "grpcroutes" + TLSRoutesResource = "tlsroutes" + ListenerSetsResource = "listenersets" + + // Istio resources + VirtualServicesResource = "virtualservices" + DestinationRulesResource = "destinationrules" + ServiceEntriesResource = "serviceentries" + SidecarsResource = "sidecars" + + // NGINX Ingress Controller resources + VirtualServersResource = "virtualservers" + VirtualServerRoutesResource = "virtualserverroutes" + + // Traefik Ingress Controller resources + IngressRoutesResource = "ingressroutes" SecretsResource = "secrets" ServiceAccountResource = "serviceaccounts" ServicesResource = "services" From f5d09e2855d35914b76999ed04295dfabbba8a48 Mon Sep 17 00:00:00 2001 From: Eliott Bouhana Date: Tue, 7 Apr 2026 18:41:41 +0200 Subject: [PATCH 2/4] fix: correct gci formatting in rbac const.go The gci formatter requires a blank line between the Traefik resources block and the general resources block, and re-aligns the equals signs to match the new block's column width. --- pkg/kubernetes/rbac/const.go | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/pkg/kubernetes/rbac/const.go b/pkg/kubernetes/rbac/const.go index 4dee562e4a..54e92ca968 100644 --- a/pkg/kubernetes/rbac/const.go +++ b/pkg/kubernetes/rbac/const.go @@ -143,18 +143,19 @@ const ( // Traefik Ingress Controller resources IngressRoutesResource = "ingressroutes" - SecretsResource = "secrets" - ServiceAccountResource = "serviceaccounts" - ServicesResource = "services" - StatefulsetsResource = "statefulsets" - StorageClassesResource = "storageclasses" - SubjectAccessReviewResource = "subjectaccessreviews" - ValidatingConfigResource = "validatingwebhookconfigurations" - VolumeAttachments = "volumeattachments" - VPAResource = "verticalpodautoscalers" - WpaResource = "watermarkpodautoscalers" - EKSKubeControllerManagerMetrics = "kcm/metrics" - EKSKubeSchedulerMetrics = "ksh/metrics" + + SecretsResource = "secrets" + ServiceAccountResource = "serviceaccounts" + ServicesResource = "services" + StatefulsetsResource = "statefulsets" + StorageClassesResource = "storageclasses" + SubjectAccessReviewResource = "subjectaccessreviews" + ValidatingConfigResource = "validatingwebhookconfigurations" + VolumeAttachments = "volumeattachments" + VPAResource = "verticalpodautoscalers" + WpaResource = "watermarkpodautoscalers" + EKSKubeControllerManagerMetrics = "kcm/metrics" + EKSKubeSchedulerMetrics = "ksh/metrics" // Non resource URLs From fc902daf955d3f0ee8b663676c17726fa116f077 Mon Sep 17 00:00:00 2001 From: Eliott Bouhana Date: Fri, 10 Apr 2026 10:59:06 +0200 Subject: [PATCH 3/4] feat(orchestrator): forward network CRD flags as env vars to cluster-agent When networkCRDs.enabled=true, set all three per-family agent config flags on the cluster-agent pod so collection is activated alongside the RBAC: DD_ORCHESTRATOR_EXPLORER_CUSTOM_RESOURCES_OOTB_GATEWAY_API=true DD_ORCHESTRATOR_EXPLORER_CUSTOM_RESOURCES_OOTB_SERVICE_MESH=true DD_ORCHESTRATOR_EXPLORER_CUSTOM_RESOURCES_OOTB_INGRESS_CONTROLLERS=true These map to orchestrator_explorer.custom_resources.ootb.{gateway_api, service_mesh,ingress_controllers} in the agent config (DataDog/datadog-agent#48966). --- .../feature/orchestratorexplorer/envvar.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/internal/controller/datadogagent/feature/orchestratorexplorer/envvar.go b/internal/controller/datadogagent/feature/orchestratorexplorer/envvar.go index b4b07847c0..66650283d7 100644 --- a/internal/controller/datadogagent/feature/orchestratorexplorer/envvar.go +++ b/internal/controller/datadogagent/feature/orchestratorexplorer/envvar.go @@ -19,6 +19,11 @@ const ( DDOrchestratorExplorerDDUrl = "DD_ORCHESTRATOR_EXPLORER_ORCHESTRATOR_DD_URL" DDOrchestratorExplorerAdditionalEndpoints = "DD_ORCHESTRATOR_ADDITIONAL_ENDPOINTS" DDOrchestratorExplorerContainerScrubbingEnabled = "DD_ORCHESTRATOR_EXPLORER_CONTAINER_SCRUBBING_ENABLED" + + // Network CRD collection — maps to orchestrator_explorer.custom_resources.ootb.* config keys + DDOrchestratorExplorerOOTBGatewayAPI = "DD_ORCHESTRATOR_EXPLORER_CUSTOM_RESOURCES_OOTB_GATEWAY_API" + DDOrchestratorExplorerOOTBServiceMesh = "DD_ORCHESTRATOR_EXPLORER_CUSTOM_RESOURCES_OOTB_SERVICE_MESH" + DDOrchestratorExplorerOOTBIngressControllers = "DD_ORCHESTRATOR_EXPLORER_CUSTOM_RESOURCES_OOTB_INGRESS_CONTROLLERS" ) func (f *orchestratorExplorerFeature) getEnabledEnvVar() *corev1.EnvVar { @@ -51,5 +56,14 @@ func (f *orchestratorExplorerFeature) getEnvVars() []*corev1.EnvVar { }) } + if f.collectKubernetesNetworkResources { + trueVal := true + envVarsList = append(envVarsList, + &corev1.EnvVar{Name: DDOrchestratorExplorerOOTBGatewayAPI, Value: apiutils.BoolToString(&trueVal)}, + &corev1.EnvVar{Name: DDOrchestratorExplorerOOTBServiceMesh, Value: apiutils.BoolToString(&trueVal)}, + &corev1.EnvVar{Name: DDOrchestratorExplorerOOTBIngressControllers, Value: apiutils.BoolToString(&trueVal)}, + ) + } + return envVarsList } From 974bcf290b6875e7e912f93997ac09e10812cf58 Mon Sep 17 00:00:00 2001 From: Eliott Bouhana Date: Fri, 10 Apr 2026 11:23:54 +0200 Subject: [PATCH 4/4] fix: correct gci formatting in envvar.go --- .../datadogagent/feature/orchestratorexplorer/envvar.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/controller/datadogagent/feature/orchestratorexplorer/envvar.go b/internal/controller/datadogagent/feature/orchestratorexplorer/envvar.go index 66650283d7..1ee7638dce 100644 --- a/internal/controller/datadogagent/feature/orchestratorexplorer/envvar.go +++ b/internal/controller/datadogagent/feature/orchestratorexplorer/envvar.go @@ -21,9 +21,9 @@ const ( DDOrchestratorExplorerContainerScrubbingEnabled = "DD_ORCHESTRATOR_EXPLORER_CONTAINER_SCRUBBING_ENABLED" // Network CRD collection — maps to orchestrator_explorer.custom_resources.ootb.* config keys - DDOrchestratorExplorerOOTBGatewayAPI = "DD_ORCHESTRATOR_EXPLORER_CUSTOM_RESOURCES_OOTB_GATEWAY_API" - DDOrchestratorExplorerOOTBServiceMesh = "DD_ORCHESTRATOR_EXPLORER_CUSTOM_RESOURCES_OOTB_SERVICE_MESH" - DDOrchestratorExplorerOOTBIngressControllers = "DD_ORCHESTRATOR_EXPLORER_CUSTOM_RESOURCES_OOTB_INGRESS_CONTROLLERS" + DDOrchestratorExplorerOOTBGatewayAPI = "DD_ORCHESTRATOR_EXPLORER_CUSTOM_RESOURCES_OOTB_GATEWAY_API" + DDOrchestratorExplorerOOTBServiceMesh = "DD_ORCHESTRATOR_EXPLORER_CUSTOM_RESOURCES_OOTB_SERVICE_MESH" + DDOrchestratorExplorerOOTBIngressControllers = "DD_ORCHESTRATOR_EXPLORER_CUSTOM_RESOURCES_OOTB_INGRESS_CONTROLLERS" ) func (f *orchestratorExplorerFeature) getEnabledEnvVar() *corev1.EnvVar {