fix: grant AsAIBridged ResourceSystem.ActionCreate for UpsertAISeatState#24603
Conversation
…Create for UpsertAISeatState The aibridged RBAC subject was missing ResourceSystem.ActionCreate, causing UpsertAISeatState to fail with 'unauthorized: rbac: forbidden' on the first AI Bridge connection per user. The seat tracker's throttle logic then suppressed retries for 30 minutes, making the error appear one-time. Add the minimal permission and a regression test that exercises the production call path (dbauthz-wrapped DB + AsAIBridged context). Fixes coder/internal#1444
f39b612 to
e5cf2a9
Compare
e5cf2a9 to
f39b612
Compare
| }, | ||
| rbac.ResourceApiKey.Type: {policy.ActionRead}, // Validate API keys. | ||
| rbac.ResourceAibridgeInterception.Type: {policy.ActionCreate, policy.ActionRead, policy.ActionUpdate, policy.ActionDelete}, | ||
| rbac.ResourceSystem.Type: {policy.ActionCreate}, // Required for UpsertAISeatState. |
There was a problem hiding this comment.
This is much too broad of a permission to add to AI Bridge which will have unintended consequences. Something a bit more fine grained is needed instead.
There was a problem hiding this comment.
Technically agree with you, but as a quick first for seat calculation, this option is the cheapest 👍
EDIT:
It's still better than its counterpart in provisionerd:
rbac.ResourceSystem.Type: {policy.WildcardSymbol},
There was a problem hiding this comment.
I don't fully understand AI bridge but I was thinking because of how it proxies LLM requests, its important to be careful about what access is given here for security reasons
There was a problem hiding this comment.
Fair point. ResourceSystem is a catch-all, so granting any action on it feels too broad on the surface. Here's why I think this is safe short-term, plus what I'd suggest long-term.
Short-term safety argument (for the nearest release):
ResourceSystem + ActionCreate unlocks some querier methods (InsertDBCryptKey, InsertDERPMeshKey, InsertReplica, UpsertRuntimeConfig, etc.). However, none of them are reachable from aibridged code paths. The AI Bridge daemon interacts with the DB exclusively through aibridgedserver.Server methods, which only call:
ResourceAibridgeInterceptionoperations (CRUD)ResourceUser(read)ResourceApiKey(read)SeatTracker.RecordUsage->UpsertAISeatState(the broken one)
Long-term: dedicated ResourceAISeat
ResourceSystem is already deprecated (the comment in object_gen.go says so explicitly), and UpsertAISeatState should have its own resource. Happy to do that as a follow-up. For the immediate fix, the blast radius is zero in practice because there's no code path from AI Bridge to any of the other ResourceSystem + ActionCreate operations.
There was a problem hiding this comment.
FYI here is a draft PR for the long-term version: #24613 , but we'll need RBAC subject-matter expert to review it thoroughly.
Problem
When a user connects to AI Bridge for the first time,
UpsertAISeatStatefails withunauthorized: rbac: forbiddenbecause thesubjectAibridgedRBAC subject is missingResourceSystem.ActionCreate.UpsertAISeatStateis called from two places viaSeatTracker.RecordUsage:enterprise/aibridgedserver/aibridgedserver.go— usesAsAIBridgedcontext (broken)coderd/provisionerdserver/provisionerdserver.go— usesAsProvisionerdcontext (works, becausesubjectProvisionerdhasResourceSystem: {WildcardSymbol})The error only surfaces on the first connection per user because the seat tracker throttles retries for 30 minutes after a failure (
failedThrottleInterval), silencing subsequent attempts.Fix
Add
rbac.ResourceSystem.Type: {policy.ActionCreate}tosubjectAibridged. This is the minimal permission needed (principle of least privilege), matching howsubjectDBPurgescopesResourceSystemto a single action rather than usingWildcardSymbol.A regression test exercises the production call path: dbauthz-wrapped DB with a real authorizer and
AsAIBridgedcontext.Fixes coder/internal#1444