Skip to content

Enforce authz on cost, policy and scanner report endpoints#427

Open
tamalsaha wants to merge 2 commits into
masterfrom
authz-reports
Open

Enforce authz on cost, policy and scanner report endpoints#427
tamalsaha wants to merge 2 commits into
masterfrom
authz-reports

Conversation

@tamalsaha

Copy link
Copy Markdown
Contributor

Problem

The CostReport (cost.k8s.appscode.com), PolicyReport (policy.k8s.appscode.com) and scanner CVEReport endpoints each took a user-supplied scope and returned cluster data without any authorization check. None of the three storages received an authorizer, unlike the core genericresource/podview storages which already do per-object RBAC. As a result any caller able to reach these endpoints could read cluster-wide cost data, all gatekeeper policy violations, and CVE/image data for arbitrary pods — and the scanner endpoint additionally triggers image scans as a side effect.

Fix

Add an authorizer.Authorizer to each storage (wired in apiserver.go) and gate every request before any cluster data is read, using a small shared helper pkg/shared/authz.go (Authorize + ClusterReadAttributes).

The required access matches what each endpoint actually reads:

  • scanner CVEReport — mirrors graph.LocatePods:
    • cluster / image / cluster-CVE request → cluster-wide list pods
    • namespace / namespace-CVE request → list pods in that namespace
    • pod request → get on the pod
    • object request (e.g. a workload) → get on the referenced object
  • policy PolicyReport:
    • cluster scope → cluster-wide read
    • namespace scope → access to the namespace
    • resource scope → get on the referenced object
  • cost CostReport — aggregates whole-cluster spend with no single resource to scope to → cluster-wide read.

Cluster-wide read is modeled as get on */* (effectively cluster-admin) and is used only where the response genuinely exposes whole-cluster data.

Scope / notes

Follow-up to #425 (resourcegraph/renderrawgraph authz). With this, all of the registry endpoints that returned cluster data without a check are now gated. render/resourcequery are intentionally untouched — they already enforce access via the graph engine's user impersonation.

Because cost and cluster-scope policy reports now require cluster-wide read, non-admin users who previously saw that data will get a 403; this is intentional given the data is whole-cluster, but worth confirming against the UI's expectations.

Testing

  • go build ./... passes.
  • go vet and gofmt clean on the changed files.

The CostReport, PolicyReport and CVEReport (scanner) endpoints returned
cluster data for a user-supplied scope without checking whether the caller
was allowed to read it. Unlike the core genericresource/podview storages,
none of them received an authorizer.

Add an authorizer to each storage (wired in apiserver.go) and gate every
request, with a small shared helper (pkg/shared/authz.go):

- scanner CVEReport: authorize the pods that back the requested scope, the
  same access graph.LocatePods reads - cluster/image/cluster-CVE require
  cluster-wide "list pods", namespace scopes require "list pods" in that
  namespace, a pod request requires "get" on the pod, and an object request
  requires "get" on the referenced object.
- policy PolicyReport: cluster scope requires cluster-wide read, namespace
  scope requires access to the namespace, and a resource scope requires
  "get" on the referenced object.
- cost CostReport: aggregates whole-cluster spend with no single resource to
  scope to, so require cluster-wide read access.

Cluster-wide read is modeled as get on */* (effectively cluster-admin), used
only where the response exposes whole-cluster data.

Signed-off-by: Tamal Saha <tamal@appscode.com>
kodiakhq[bot]
kodiakhq Bot previously approved these changes Jun 25, 2026
Add unit tests using a fake authorizer (and a fake client with a static
RESTMapper) that assert each report endpoint denies with a Forbidden status
and authorizes against the expected attributes for every request scope:

- shared.Authorize: allow, deny->Forbidden, missing-user->BadRequest, and
  ClusterReadAttributes shape.
- scanner CVEReport: cluster/namespace/pod/object scopes map to the right
  pod/object access.
- policy PolicyReport: cluster/namespace/resource scopes.
- cost CostReport: requires cluster-wide read; missing user is a BadRequest.

Signed-off-by: Tamal Saha <tamal@appscode.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant