mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-10-21 05:24:28 +02:00
iam: bind CreateServiceAccount ParentUser to the caller (#11218)
* iam: bind CreateServiceAccount target to caller in AuthorizeIamAction A non-admin holding iam:CreateServiceAccount could pass an arbitrary ParentUser and mint a service account for any identity, inheriting that identity permissions. Add a self-target category so a granted non-admin may only target their own identity; admins remain unrestricted. * iam: authorize CreateServiceAccount against its ParentUser target AuthIamManagement passed UserName as the authorization target for every action, so CreateServiceAccount was authorized with an empty target and the self-target binding never saw the caller-supplied ParentUser. Pass ParentUser for that action so the binding takes effect on the live path. * iam: test CreateServiceAccount binds target to caller Regression test: a non-admin holding iam:CreateServiceAccount may target itself but is denied targeting another identity; admins remain unrestricted. * iam: authorize CreateServiceAccount against ParentUser on the S3 port UnifiedPostHandler passed UserName as the authorization target for every IAM action, so CreateServiceAccount was authorized with an empty target on the S3-port route and the self-target binding never saw the caller ParentUser. Extract iamTargetUserName (ParentUser for CreateServiceAccount, UserName otherwise) and use it from both IAM dispatch surfaces so the binding applies on the live S3-port path as well as the standalone iam server. * iam: test CreateServiceAccount ParentUser binding on the S3 port End-to-end regression test through UnifiedPostHandler: a non-admin holding iam:CreateServiceAccount is denied (403) when targeting another identity and passes authorization when targeting itself.
This commit is contained in:
1 parent
361fd6b263
commit
c0a7dbb2bb
4 files changed
+123
-6
No files matched your search
@@ -2474,8 +2474,31 @@ 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).
|
||||
var iamSelfTargetActions = map[string]bool{
|
||||
"CreateServiceAccount": true,
|
||||
}
|
||||
|
||||
func iamRequiresSelfTarget(action string) bool {
|
||||
return iamSelfTargetActions[action]
|
||||
}
|
||||
|
||||
// iamTargetUserName returns the request's target identity for authorization.
|
||||
// Most IAM actions target UserName; CreateServiceAccount targets ParentUser.
|
||||
// Both IAM dispatch surfaces (AuthIamManagement and UnifiedPostHandler) use
|
||||
// this so the authorized target and the acted-on target cannot differ.
|
||||
func iamTargetUserName(action string, r *http.Request) string {
|
||||
if action == "CreateServiceAccount" {
|
||||
return r.PostForm.Get("ParentUser")
|
||||
}
|
||||
return r.PostForm.Get("UserName")
|
||||
}
|
||||
|
||||
// AuthorizeIamAction authorizes an IAM management action for identity, with
|
||||
// targetUserName taken from the request's UserName parameter.
|
||||
// targetUserName taken from the request's target parameter (UserName, or
|
||||
// ParentUser for CreateServiceAccount).
|
||||
//
|
||||
// IAM management is not part of the S3 data plane, so the grant is checked as
|
||||
// iam:<Action>. A coarse S3 action would instead be matched by an ordinary
|
||||
@@ -2496,7 +2519,13 @@ func (iam *IdentityAccessManagement) AuthorizeIamAction(r *http.Request, identit
|
||||
if identity.isAdmin() {
|
||||
return s3err.ErrNone
|
||||
}
|
||||
return iam.VerifyActionPermission(r, identity, Action("iam:"+action), "arn:aws:iam:::*", "")
|
||||
if errCode := iam.VerifyActionPermission(r, identity, Action("iam:"+action), "arn:aws:iam:::*", ""); errCode != s3err.ErrNone {
|
||||
return errCode
|
||||
}
|
||||
if iamRequiresSelfTarget(action) && targetUserName != "" && targetUserName != identity.Name {
|
||||
return s3err.ErrAccessDenied
|
||||
}
|
||||
return s3err.ErrNone
|
||||
}
|
||||
|
||||
// AuthIamManagement authenticates an IAM management request and authorizes the
|
||||
@@ -2530,7 +2559,8 @@ func (iam *IdentityAccessManagement) AuthIamManagement(f http.HandlerFunc) http.
|
||||
|
||||
// UserName comes from the body only, the same place the handlers read it
|
||||
// from, so the authorized target and the acted-on target cannot differ.
|
||||
if errCode := iam.AuthorizeIamAction(r, identity, r.Form.Get("Action"), r.PostForm.Get("UserName")); errCode != s3err.ErrNone {
|
||||
action := r.Form.Get("Action")
|
||||
if errCode := iam.AuthorizeIamAction(r, identity, action, iamTargetUserName(action, r)); errCode != s3err.ErrNone {
|
||||
s3err.WriteErrorResponse(w, r, errCode)
|
||||
return
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user