Description
MatchingRules.Namespaces is declared and unmarshalled, but LoadGVRs only ever reads .APIResources. The namespace list is dead config - continuous scanning watches every GVR cluster-wide regardless of what the user configures.
continuousscanning/loader.go:21-25:
type MatchingRules struct {
APIResources []APIResourceMatch `json:"match"`
Namespaces []string `json:"namespaces"` // <-- never read
}
continuousscanning/loader.go:65-77:
func (l *targetLoader) LoadGVRs(ctx context.Context) []schema.GroupVersionResource {
gvrs := []schema.GroupVersionResource{}
rules, _ := l.fetcher.Fetch(ctx)
apiResourceMatches := rules.APIResources // .Namespaces is dropped on the floor
for idx := range apiResourceMatches {
gvrs = append(gvrs, matchRuleToGVR(apiResourceMatches[idx])...)
}
return gvrs
}
LoadGVRs is the only method on the TargetLoader interface, so the namespace list has nowhere to go. The watch pool is then built with an empty metav1.ListOptions{} and no namespace restriction (continuousscanning/service.go:29-34). The only namespace filtering that actually happens is cfg.SkipNamespace() in service.go:47, which is the unrelated global exclude list.
This matters because the Helm chart ships a namespace list by default and documents it as functional helm-charts/charts/kubescape-operator/values.yaml:
continuousScanning:
configMapName: cs-matching-rules
# Matching rules for the monitored resources.
# Kubescape will watch resources of every provided GVR across the provided
# namespaces.
matchingRules:
match:
- apiGroups: ["apps"]
apiVersions: ["v1"]
resources: ["deployments"]
namespaces:
- default
Reproduction
Install with the default chart values (namespaces: [default]) and create Deployments in a namespace other than default. They get scanned anyway.
Observed in the wild on a user's cluster (Slack #kubescape): with stock defaults, the operator triggered scans for Deployments in app-workloads and local-path-storage, neither of which is default:
{"msg":"fetched gvrs","gvrs":[{"Group":"apps","Version":"v1","Resource":"deployments"}]}
{"msg":"triggering scan","kind":"Deployment","name":"backend-api","namespace":"app-workloads"}
{"msg":"triggering scan","kind":"Deployment","name":"local-path-provisioner","namespace":"local-path-storage"}
Expected
Either the namespace list is honoured (watches are established per-namespace, or events from non-listed namespaces are filtered before dispatch), or the field is removed from the struct and the chart so it stops implying scoping that doesn't exist.
Honouring it is the smaller surprise for users, but it is a behaviour change for anyone running the default namespaces: [default] today - they'd go from cluster-wide scanning to default- only on upgrade. Worth deciding explicitly. Happy to open a PR either way once there's a preference.
Description
MatchingRules.Namespacesis declared and unmarshalled, butLoadGVRsonly ever reads.APIResources. The namespace list is dead config - continuous scanning watches every GVR cluster-wide regardless of what the user configures.continuousscanning/loader.go:21-25:continuousscanning/loader.go:65-77:LoadGVRsis the only method on theTargetLoaderinterface, so the namespace list has nowhere to go. The watch pool is then built with an emptymetav1.ListOptions{}and no namespace restriction (continuousscanning/service.go:29-34). The only namespace filtering that actually happens iscfg.SkipNamespace()inservice.go:47, which is the unrelated global exclude list.This matters because the Helm chart ships a namespace list by default and documents it as functional
helm-charts/charts/kubescape-operator/values.yaml:Reproduction
Install with the default chart values (
namespaces: [default]) and create Deployments in a namespace other thandefault. They get scanned anyway.Observed in the wild on a user's cluster (Slack #kubescape): with stock defaults, the operator triggered scans for Deployments in
app-workloadsandlocal-path-storage, neither of which isdefault:Expected
Either the namespace list is honoured (watches are established per-namespace, or events from non-listed namespaces are filtered before dispatch), or the field is removed from the struct and the chart so it stops implying scoping that doesn't exist.
Honouring it is the smaller surprise for users, but it is a behaviour change for anyone running the default
namespaces: [default]today - they'd go from cluster-wide scanning todefault- only on upgrade. Worth deciding explicitly. Happy to open a PR either way once there's a preference.