Skip to content

continuousScanning.matchingRules.namespaces is parsed but never used - namespace scoping is silently ignored #397

Description

@harshitg927

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    Status
    Accepted

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions