iam: scope CreateServiceAccount's ParentUser by policy resource (#11736)

* iam: scope CreateServiceAccount's ParentUser by policy resource

AuthorizeIamAction evaluated the iam:CreateServiceAccount grant against a
hardcoded arn:aws:iam:::* resource, so the policy's Resource never had a
say; #11218 then bound the target to the caller outright. Evaluate
self-target actions against the target identity's ARN instead: a wildcard
resource keeps the deliberate any-target grant, a scoped resource confines
issuance to the named parent identity, anything else is denied.

buildResourceARN gains the ARN pass-through buildS3ResourceArn already has,
so iam: ARNs reach the policy engine intact on the attached-policies route.

Closes #11735

* ci: retrigger after unrelated weed/storage metrics flake

* iam: fold IAM-ARN matching into awsIAMMatch, drop debug test

- remove zz_debug_test.go (local debugging leftover)
- fold awsIAMMatchResource into awsIAMMatch: the arn:aws:iam:: value
  prefix selects case-sensitive matching, so no separate name is needed
- trim comments

---------

Co-authored-by: batonogov <batonogov@users.noreply.github.com>
Co-authored-by: Chris Lu <chris.lu@gmail.com>
This commit is contained in:
authored and GitHub committed 2026-10-18 11:48:01 -07:00
1 parent 4a57432488
commit eea3f232da
5 files changed
+140 -50

No files matched your search

+11 -9
View File
@@ -2528,9 +2528,10 @@ func iamRequiresAdminForOthers(action string) bool {
return iamSelfServiceActions[action]
}
// iamSelfTargetActions require an explicit iam:<Action> grant, but a non-admin
// grant holder may only target their own identity. The target parameter is
// action-specific (UserName for most, ParentUser for CreateServiceAccount).
// iamSelfTargetActions are authorized against the target identity's ARN so a
// policy can scope the grant (arn:aws:iam:::user/team-parent). The target
// parameter is action-specific (UserName for most, ParentUser for
// CreateServiceAccount).
var iamSelfTargetActions = map[string]bool{
"CreateServiceAccount": true,
}
@@ -2573,13 +2574,14 @@ func (iam *IdentityAccessManagement) AuthorizeIamAction(r *http.Request, identit
if identity.isAdmin() {
return s3err.ErrNone
}
if errCode := iam.VerifyActionPermission(r, identity, Action("iam:"+action), "arn:aws:iam:::*", ""); errCode != s3err.ErrNone {
return errCode
// Self-target actions authorize against the target identity's ARN. The
// account segment stays empty so existing arn:aws:iam:::* wildcard
// grants keep matching.
resource := "arn:aws:iam:::*"
if iamRequiresSelfTarget(action) && targetUserName != "" {
resource = fmt.Sprintf("arn:aws:iam:::user/%s", targetUserName)
}
if iamRequiresSelfTarget(action) && targetUserName != "" && targetUserName != identity.Name {
return s3err.ErrAccessDenied
}
return s3err.ErrNone
return iam.VerifyActionPermission(r, identity, Action("iam:"+action), resource, "")
}
// AuthIamManagement authenticates an IAM management request and authorizes the