You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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:
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.
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.
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.
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.
Summary
GridNetworkStatus.budgetStatus(added in #47) exposes every tenant's cumulative spend and cap in one shared array on theGridNetworkcustom resource. Kubernetes RBAC is enforced at the resource/subresource level (get/listongridnetworks/status), not per array element — so any principal granted read access to aGridNetworkcan 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 —
budgetStatusfollows the exact same shared-array convention Grid already uses foroverlayStatus(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 dataGridNetworkStatushas 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:
gridnetworks/statusas a whole; there's no way to say "tenant A may seebudgetStatus[0]but notbudgetStatus[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.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.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 oneGridNetworkper tenant/trust-domain (each with its own scopedbudgetPolicy, in its own namespace with narrowly-scoped RBAC) instead of one sharedGridNetworkspanning 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)
docs/architecture/operations.md(and thebudgetPolicy/budgetStatusAPI doc comments) recommending per-tenantGridNetworks when spend confidentiality across tenants matters. Zero engineering, ships immediately.budgetStatusentries to the requester's tenant claim. Real engineering effort, actually solves it for the shared-GridNetworkcase.budgetStatusas a separate per-tenant CRD instead of an array field, so RBAC's existingresourceNamesmechanism 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
praxis-proxy/ai#301(Multi-Tenancy Support)