Normalize string role claims before claim-based role mapping evaluation - #6306
Normalize string role claims before claim-based role mapping evaluation#6306Yanhaoxi wants to merge 4 commits into
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #6306 +/- ##
==========================================
+ Coverage 72.85% 72.98% +0.13%
==========================================
Files 742 742
Lines 77804 78437 +633
==========================================
+ Hits 56683 57248 +565
- Misses 17145 17198 +53
- Partials 3976 3991 +15 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
reyortiz3
left a comment
There was a problem hiding this comment.
Thanks for the clear writeup and tests — this closes a real silent-fallback bug. One correctness issue found, plus two minor nits.
pkg/auth/awssts/role_mapper.go: the fail-closed early return discards already-found higher-priority matches
SelectRole collects matches into matches across the whole loop, then sorts by priority and returns matches[0] only after the loop finishes. But when a claim-based mapping's evalContext/EvaluateBool call errors (unsupported shape, or an eval failure), SelectRole returns "", err immediately — discarding any matches already appended from earlier iterations.
Concretely: a config with a matcher-based mapping (e.g. claims.sub == 'admin1', priority 1) and a claim-based mapping sharing the same RoleClaim (e.g. groups). If a request's groups claim has an unsupported shape (an object), the matcher mapping may legitimately match first and get appended to matches, but the claim-based mapping's normalization error later in the same loop throws that result away — the caller gets HTTP 403 even though a legitimate, higher-priority rule already granted access.
Suggest evaluating all mappings first (collecting per-mapping errors alongside matches), and only failing closed if no valid match exists among what was found — rather than aborting the whole loop on the first error encountered.
Minor: normalizeRoleClaim re-clones the full claims map per mapping
For a config with N claim-based mappings sharing the same RoleClaim, evalContext calls normalizeRoleClaim once per mapping, each doing a full O(len(claims)) clone with an identical result. Could be hoisted to compute the normalized claims map once before the mapping loop.
Minor: the clone logic is duplicated inside normalizeRoleClaim
The "allocate clone sized len(claims)+1, copy every key/value" block appears twice (missing-claim branch and string branch). A small helper would avoid the two copies drifting if either needs to change later.
|
Thanks for the review — addressed all three points.
The PR has been updated. |
Summary
Fixes #6305. Claim-based role mappings are evaluated with the fixed CEL expression
claim_value in claims[role_claim_key], whose semantics depend on the role claim value's runtime type. cel-go v0.30.0 definesinonly for list/map operands, so a string-typed role claim raised a "no such overload" error thatSelectRoleswallowed and treated as a non-match — the user silently gotFallbackRoleArneven when their claim exactly equaled the configuredClaim. An object-typed role claim madeintest map-key membership, spuriously matching when the configured value was a key.WHAT changed:
normalizeRoleClaim, applied to the role claim value before evaluation: string → one-element list (exact membership restored); missing claim → empty list (fallback preserved); any other shape (object, number, bool, null) → rejected.SelectRolenow fails closed on unsupported claim shapes and claim-mapping evaluation errors (ErrNoRoleMapping, HTTP 403 via theaws_stsmiddleware) instead of silently grantingFallbackRoleArn; matcher-expression errors keep their skip-and-fall-back behavior but log at Warn instead of Debug.Type of change
Test plan
task test)task test-e2e)task lint-fix)Run:
go test ./pkg/auth/awssts/... ./pkg/vmcp/auth/strategies/.... New testsTestRoleMapper_SelectRole_StringRoleClaimandTestRoleMapper_SelectRole_UnsupportedRoleClaimShapeFAIL without the source fix and PASS with it; full suites pass.go vetandgofmtclean;golangci-lintreports only 3 pre-existinggciissues in files this PR does not touch.API Compatibility
v1beta1API. Configuration shape is unchanged.Changes
pkg/auth/awssts/role_mapper.gopkg/auth/awssts/role_mapper_test.goDoes this introduce a user-facing change?
Yes. Two intentional behavior changes:
Claimnow selects the mapped role (e.g.groups: "admins"withClaim: "admins"→AdminRole); previously it silently fell back. Strings merely containing the value still do not match.Special notes for reviewers