Skip to content

fix(auth): enforce scoped authorization for Kubernetes-backed resources - #3281

Open
supreme-gg-gg wants to merge 3 commits into
mainfrom
jetc/feat/k8s-resources-rbac
Open

supreme-gg-gg wants to merge 3 commits into
mainfrom
jetc/feat/k8s-resources-rbac

Conversation

@supreme-gg-gg

Copy link
Copy Markdown
Contributor

Several resource lists bypass collection scopes or authorize only a resource type. Restrictive authorizers therefore cannot consistently hide denied objects or authorize mutations against a specific resource.

This change:

  • Reuses collection-scope matching across CRUD and custom list services.
  • Filters prompt libraries, configured model providers, tool servers, tools, and WorkerPools before exposing their contents.
  • Checks provider refresh permissions before discovery work and keeps WorkerPool permissions separate from other Substrate inventory.
  • Restricts direct prompt operations to labeled library ConfigMaps and rejects ambiguous tool-server deletion.

Adds service, gRPC-client, and MCP regression coverage for scoped results and denial before side effects, including existing SandboxTemplate authorization paths. No protobuf changes.

Signed-off-by: Jet Chiang <pokyuen.jetchiang-ext@solo.io>
@github-actions github-actions Bot added the bug Something isn't working label Oct 19, 2026
Signed-off-by: Jet Chiang <pokyuen.jetchiang-ext@solo.io>
@supreme-gg-gg
supreme-gg-gg marked this pull request as ready for review October 19, 2026 16:51
@supreme-gg-gg
supreme-gg-gg requested a review from a team as a code owner October 19, 2026 16:51

@EItanya EItanya left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 AI-generated review. I looked through the findings and they all seem worth looking at.

if err != nil {
return false, err
}
scope, err := s.authorizer.Scope(ctx, principal, auth.VerbGet, substrateResource)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think Scope(VerbGet, "Substrate") gives the summary a stable authorization boundary. The dedicated actor and worker endpoints still use Check(VerbGet, Resource{Type: "Substrate"}), so an external authorizer can allow one path and deny the other; Scope is otherwise documented for VerbList collection filtering.

Can we give the summary an explicit capability, for example Check(VerbGet, Resource{Type: "SystemSummary"}), which policy can grant to admins or operators? If we want section-level access instead, the Substrate section should at least use the same decision as its sibling endpoints, and a denied section should be distinguishable from an empty cluster.

- `ModelConfig` and `ModelProviderConfig` use separate typed CRUD services and resource types. Configured-provider reads and refresh are authorized independently from ModelConfig operations.
- Prompt libraries, tool discovery, and WorkerPool summary rows use `kubeauth.CollectionMatcher` for custom collection projection.
- Flat tool discovery must filter through the same exact owning-server identities as server discovery.
- WorkerPool scope is independent from the mixed summary's whole-inventory Substrate permission.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this guide treats EP-1270 as the source contract, can we update that design as part of this change? It still says ListConfiguredProviders is unchanged and lists ModelProviderConfig, tool servers, and prompt templates as non-goals. It should also document whichever summary/Substrate authorization contract we choose, otherwise external authorizer implementations don't have a consistent contract to follow.

require.Equal(t, []string{"cached"}, cached.GetModels())
_, err = models.ListProviderModels(ctx, &apiv1.ListProviderModelsRequest{ProviderName: "hidden"})
assertGRPCCode(t, err, codes.PermissionDenied)
_, err = models.ListProviderModels(ctx, &apiv1.ListProviderModelsRequest{ProviderName: "allowed", Refresh: true})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we also exercise this through a real share-bearing RPC? The service test constructs ShareContext directly, while this generated-client case only covers ordinary authorizer denial. A valid x-share-token with an otherwise permissive authorizer would verify the interceptor-to-service path and that refresh is denied before calling the refresher.

Signed-off-by: Jet Chiang <pokyuen.jetchiang-ext@solo.io>
@github-actions github-actions Bot added the enhancement-proposal Indicates that this PR is for an enhancement proposal label Oct 19, 2026
@supreme-gg-gg

Copy link
Copy Markdown
Contributor Author

Addressed in 3748a85

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working enhancement-proposal Indicates that this PR is for an enhancement proposal

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants