Skip to content

Decision needed: GridNetwork.status.budgetStatus exposes all tenants' spend to any RBAC-permitted reader #48

Description

@jordigilh

Summary

GridNetworkStatus.budgetStatus (added in #47) exposes every tenant's cumulative spend and cap in one shared array on the GridNetwork custom resource. Kubernetes RBAC is enforced at the resource/subresource level (get/list on gridnetworks/status), not per array element — so any principal granted read access to a GridNetwork can see all tenants' spend, not just their own.

This was flagged by the security review on #47 as a medium/operational finding. It isn't a code defect in that PR — budgetStatus follows the exact same shared-array convention Grid already uses for overlayStatus (per-gateway) and other status fields — but it's a real gap worth a design decision rather than silently assuming it's fine, especially since spend data is more sensitive (tenant-identifying financial data) than the topology/site data GridNetworkStatus has exposed this way until now.

Why this needs a decision, not just a fix

I checked whether this is fixable as a quick patch — it isn't, for a few reasons:

  1. Kubernetes RBAC has no native per-array-element scoping. You can allow/deny gridnetworks/status as a whole; there's no way to say "tenant A may see budgetStatus[0] but not budgetStatus[1]" within one object without a custom aggregated API server or an admission/response-filtering webhook — both are platform-engineering projects, not a Grid code change.
  2. A real per-tenant isolation fix would mean a CRD redesign (separate per-tenant objects instead of one shared array), so standard resourceNames-scoped RBAC could apply. That's a bigger architectural change than "wire up tenant-budget plumbing" and would need to reopen the feat: tenant-budget Grid-side plumbing for MVP 1b (app/tenant-based routing) #40 design conversation.
  3. There's already a documented, zero-code mitigation available today: Grid explicitly supports running multiple GridNetworks per cluster "when separate tenants, environments, or trust domains require independent provider inventories and routing." A platform team that cares about this isolation can deploy one GridNetwork per tenant/trust-domain (each with its own scoped budgetPolicy, in its own namespace with narrowly-scoped RBAC) instead of one shared GridNetwork spanning many tenants. This requires no new engineering — just documenting it as the recommended pattern when per-tenant spend confidentiality matters.

Proposed options (not yet decided — looking for input)

  • A. Document-only: add an explicit note to docs/architecture/operations.md (and the budgetPolicy/budgetStatus API doc comments) recommending per-tenant GridNetworks when spend confidentiality across tenants matters. Zero engineering, ships immediately.
  • B. Build a lightweight filtering layer (webhook or small aggregated API) that scopes budgetStatus entries to the requester's tenant claim. Real engineering effort, actually solves it for the shared-GridNetwork case.
  • C. Redesign budgetStatus as a separate per-tenant CRD instead of an array field, so RBAC's existing resourceNames mechanism applies natively. Biggest change; would need to revisit the feat: tenant-budget Grid-side plumbing for MVP 1b (app/tenant-based routing) #40 design decision.

My inclination is A now (fast, honest, unblocks nothing) with B or C as a later GA-hardening item if/when real customer tenants require cross-tenant confidentiality guarantees — but flagging for a second pair of eyes since this is a security/product posture call, not a pure engineering one.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions