Resolve authz ConfigMap for VirtualMCPServer#5290
Conversation
There was a problem hiding this comment.
Large PR Detected
This PR exceeds 1000 lines of changes and requires justification before it can be reviewed.
How to unblock this PR:
Add a section to your PR description with the following format:
## Large PR Justification
[Explain why this PR must be large, such as:]
- Generated code that cannot be split
- Large refactoring that must be atomic
- Multiple related changes that would break if separated
- Migration or data transformationAlternative:
Consider splitting this PR into smaller, focused changes (< 1000 lines each) for easier review and reduced risk.
See our Contributing Guidelines for more details.
This review will be automatically dismissed once you add the justification section.
Large PR justification has been provided. Thank you!
|
✅ Large PR justification has been provided. The size review has been dismissed and this PR can now proceed with normal review. |
|
Heads up on dropping The four Cedar fields touched here split into two buckets:
So the second bucket went onto The non-breaking alternative was just leaving
The cost is the schema break: anyone setting |
bf2fafb to
7895464
Compare
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #5290 +/- ##
==========================================
- Coverage 68.41% 68.40% -0.02%
==========================================
Files 620 620
Lines 63316 63401 +85
==========================================
+ Hits 43317 43367 +50
- Misses 16772 16798 +26
- Partials 3227 3236 +9 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
7895464 to
f8c1696
Compare
f8c1696 to
12defec
Compare
A `VirtualMCPServer` with `spec.incomingAuth.authzConfig.type: configMap`
silently produced a vmcp `config.yaml` that referenced the unresolved
`configMap` type token. The vmcp binary's `AuthzConfig` validator only
accepts `cedar` or `none`, so the pod crashed in `CrashLoopBackOff` at
startup. Inline authz also silently dropped `GroupClaimName`,
`RoleClaimName`, `GroupEntityType`, and `EntitiesJSON`, so any enterprise
Cedar policy that walked a `Client → ClaimGroup → PlatformRole` hierarchy
denied every request because the runtime Cedar authorizer built
`THVGroup::` parents while the entity store contained `ClaimGroup::`
entities.
Wire the configMap path end-to-end, plumb the four missing fields
through both source paths, and move `PrimaryUpstreamProvider` onto the
auth server config where it belongs:
* Extract `LoadAuthzConfigFromConfigMap` as the shared fetch/parse/
validate helper in `controllerutil`; `AddAuthzConfigOptions` now
delegates to it. The vMCP converter calls the same helper so the
failure modes match the `MCPServer`/`MCPRemoteProxy` runner path.
* Extend `pkg/vmcp/config.AuthzConfig` with `EntitiesJSON`,
`GroupClaimName`, `RoleClaimName`, `GroupEntityType`, and forward
all four into `cedar.ConfigOptions` in the Cedar middleware factory.
`EntitiesJSON` defaults to `"[]"` when unset to preserve the
historical Cedar contract.
* Lift the source-agnostic Cedar JWT-claim mapping fields
(`GroupClaimName`, `RoleClaimName`, `GroupEntityType`) onto
`AuthzConfigRef` so they work identically for inline and configMap
users. For configMap users the parsed payload provides the default
and the spec-level field overrides when set.
* Move `PrimaryUpstreamProvider` onto `EmbeddedAuthServerConfig`
(`spec.authServerConfig.primaryUpstreamProvider` on
`VirtualMCPServer`). The field describes which upstream IDP token
Cedar reads claims from, which is a property of the embedded auth
server, not of the authz policies. Placing it next to
`upstreamProviders` co-locates the choice with the set it selects
from. Reachable from `MCPExternalAuthConfig` of type
`embeddedAuthServer` as well; on `MCPServer`/`MCPRemoteProxy`
(single-upstream consumers) the only validated values are empty
(auto-select) or the name of that single upstream. The legacy
`spec.incomingAuth.authzConfig.inline.primaryUpstreamProvider`
location is read as a backward-compatibility fallback for one
release with a `Warning` event of reason
`AuthzPrimaryUpstreamProviderDeprecated`.
* Pre-validate the referenced authz `ConfigMap` in the controller
and distinguish `NotFound` from other parse/validation failures
via two condition reasons on `AuthConfigured`: the existing shared
`AuthzConfigMapNotFound` and a new `AuthzConfigMapInvalid`. This
mirrors the diagnostic `MCPRemoteProxy` emits today and gives
users a status-level error before the converter fails opaquely.
* Validate the resolved `PrimaryUpstreamProvider` (from either
source) against the declared embedded auth server upstreams so an
unresolvable provider is rejected at convert time.
CRD compatibility: not a breaking change. The legacy
`spec.incomingAuth.authzConfig.inline.primaryUpstreamProvider`
location continues to work, with a Warning event emitted whenever it
is read; planned removal one release after the deprecation cycle.
The new fields on `AuthzConfigRef` (`GroupClaimName`, `RoleClaimName`,
`GroupEntityType`) and on `EmbeddedAuthServerConfig`
(`PrimaryUpstreamProvider`) are additive.
Closes #4919
Closes #5208
Closes #5277
The relocation of `PrimaryUpstreamProvider` onto `EmbeddedAuthServerConfig` in the parent commit introduced two new behaviours and surfaced one pre-existing coverage gap. None of the three were captured by a test. Add focused unit tests that pin each behaviour so a future refactor cannot silently drop it. * `*VirtualMCPServer.ExplicitPrimaryUpstreamProvider()` is the central precedence helper: canonical `spec.authServerConfig.primaryUpstreamProvider` wins, deprecated `spec.incomingAuth.authzConfig.inline.primaryUpstreamProvider` is the fallback, otherwise empty. A unit test covers all four combinations (canonical only, deprecated only, both, neither) and locks the `fromDeprecated` flag the caller uses to gate the deprecation event. * `AuthzConfigRef.DeprecatedInlinePrimaryUpstreamProvider()` is the helper the advisory paths on `MCPServer` and `MCPRemoteProxy` read. A unit test covers the nil receiver, nil `Inline` subtree, inline without the field, and inline with the field set. * `MCPServer.validateAuthzPrimaryUpstreamProviderIgnored` and the equivalent on `MCPRemoteProxy` set the `AuthzPrimaryUpstreamProviderIgnored` advisory condition when the deprecated field is set on a CR that has no embedded auth server to act on it. Both functions existed before this PR but had no test coverage at all. The relocation of the field did not change their behaviour, but the absence of coverage means a regression there would go unnoticed. Add table-driven tests that exercise the True-on-set, absent-when-unset, and stale-cleared cases. * `VirtualMCPServerReconciler.validateAuthzUpstreamAvailable` emits a `Warning` event with reason `AuthzPrimaryUpstreamProviderDeprecated` whenever it resolves the primary from the deprecated location, so operators see a kubectl-visible signal even when the deprecated value still validates. A test using `events.NewFakeRecorder` confirms the event fires for the deprecated path, does not fire for the canonical path, and does not fire when neither location is set. No production code changes; only tests.
12defec to
0925161
Compare
A
VirtualMCPServerwithspec.incomingAuth.authzConfig.type: configMapsilently produced a vmcp
config.yamlthat referenced the unresolvedconfigMaptype token. The vmcp binary'sAuthzConfigvalidator onlyaccepts
cedarornone, so the pod crashed inCrashLoopBackOffatstartup. Inline authz also silently dropped
GroupClaimName,RoleClaimName,GroupEntityType, andEntitiesJSON, so any enterpriseCedar policy that walked a
Client → ClaimGroup → PlatformRolehierarchydenied every request because the runtime Cedar authorizer built
THVGroup::parents while the entity store containedClaimGroup::entities.
Wire the configMap path end-to-end, plumb the four missing fields
through both source paths, and move
PrimaryUpstreamProvideronto theauth server config where it belongs:
Extract
LoadAuthzConfigFromConfigMapas the shared fetch/parse/validate helper in
controllerutil;AddAuthzConfigOptionsnowdelegates to it. The vMCP converter calls the same helper so the
failure modes match the
MCPServer/MCPRemoteProxyrunner path.Extend
pkg/vmcp/config.AuthzConfigwithEntitiesJSON,GroupClaimName,RoleClaimName,GroupEntityType, and forwardall four into
cedar.ConfigOptionsin the Cedar middleware factory.EntitiesJSONdefaults to"[]"when unset to preserve thehistorical Cedar contract.
Lift the source-agnostic Cedar JWT-claim mapping fields
(
GroupClaimName,RoleClaimName,GroupEntityType) ontoAuthzConfigRefso they work identically for inline and configMapusers. For configMap users the parsed payload provides the default
and the spec-level field overrides when set.
Move
PrimaryUpstreamProviderontoEmbeddedAuthServerConfig(
spec.authServerConfig.primaryUpstreamProvideronVirtualMCPServer). The field describes which upstream IDP tokenCedar reads claims from, which is a property of the embedded auth
server, not of the authz policies. Placing it next to
upstreamProvidersco-locates the choice with the set it selectsfrom. Reachable from
MCPExternalAuthConfigof typeembeddedAuthServeras well; onMCPServer/MCPRemoteProxy(single-upstream consumers) the only validated values are empty
(auto-select) or the name of that single upstream. The legacy
spec.incomingAuth.authzConfig.inline.primaryUpstreamProviderlocation is read as a backward-compatibility fallback for one
release with a
Warningevent of reasonAuthzPrimaryUpstreamProviderDeprecated.Pre-validate the referenced authz
ConfigMapin the controllerand distinguish
NotFoundfrom other parse/validation failuresvia two condition reasons on
AuthConfigured: the existing sharedAuthzConfigMapNotFoundand a newAuthzConfigMapInvalid. Thismirrors the diagnostic
MCPRemoteProxyemits today and givesusers a status-level error before the converter fails opaquely.
Validate the resolved
PrimaryUpstreamProvider(from eithersource) against the declared embedded auth server upstreams so an
unresolvable provider is rejected at convert time.
CRD compatibility: not a breaking change. The legacy
spec.incomingAuth.authzConfig.inline.primaryUpstreamProviderlocation continues to work, with a Warning event emitted whenever it
is read; planned removal one release after the deprecation cycle.
The new fields on
AuthzConfigRef(GroupClaimName,RoleClaimName,GroupEntityType) and onEmbeddedAuthServerConfig(
PrimaryUpstreamProvider) are additive.Closes #4919
Closes #5208
Closes #5277
Large PR Justification
~950 lines of code are relevant, everything else is auto-generated.