diff --git a/weed/iam/integration/iam_manager.go b/weed/iam/integration/iam_manager.go index 7dddb881e..f50390dd9 100644 --- a/weed/iam/integration/iam_manager.go +++ b/weed/iam/integration/iam_manager.go @@ -323,14 +323,30 @@ func (m *IAMManager) IsActionAllowed(ctx context.Context, request *ActionRequest return false, fmt.Errorf("IAM manager not initialized") } - // Validate session token if present (skip for OIDC tokens which are already validated, - // and skip for empty tokens which represent static access keys) + // Validate session token if present + // We always try to validate with the internal STS service first if it's a SeaweedFS token. + // This ensures that session policies embedded in the token are correctly extracted and enforced. var sessionInfo *sts.SessionInfo - if request.SessionToken != "" && !isOIDCToken(request.SessionToken) { - var err error - sessionInfo, err = m.stsService.ValidateSessionToken(ctx, request.SessionToken) - if err != nil { - return false, fmt.Errorf("invalid session: %w", err) + if request.SessionToken != "" { + // Parse unverified to check issuer + parsed, _, err := new(jwt.Parser).ParseUnverified(request.SessionToken, jwt.MapClaims{}) + isInternal := false + if err == nil { + if claims, ok := parsed.Claims.(jwt.MapClaims); ok { + if issuer, ok := claims["iss"].(string); ok && m.stsService != nil && m.stsService.Config != nil { + if issuer == m.stsService.Config.Issuer { + isInternal = true + } + } + } + } + + if isInternal || !isOIDCToken(request.SessionToken) { + var err error + sessionInfo, err = m.stsService.ValidateSessionToken(ctx, request.SessionToken) + if err != nil { + return false, fmt.Errorf("invalid session: %w", err) + } } } diff --git a/weed/s3api/auth_credentials.go b/weed/s3api/auth_credentials.go index 9c567026b..9fc8dd00a 100644 --- a/weed/s3api/auth_credentials.go +++ b/weed/s3api/auth_credentials.go @@ -1563,14 +1563,22 @@ func (iam *IdentityAccessManagement) VerifyActionPermission(r *http.Request, ide } // Traditional identities (with Actions from -s3.config) use legacy auth, - // JWT/STS identities (no Actions) use IAM authorization + // JWT/STS identities (no Actions or having a session token) use IAM authorization. + // IMPORTANT: We MUST prioritize IAM authorization for any request with a session token + // to ensure that session policies are correctly enforced. + hasSessionToken := r.Header.Get("X-SeaweedFS-Session-Token") != "" || + r.Header.Get("X-Amz-Security-Token") != "" || + r.URL.Query().Get("X-Amz-Security-Token") != "" + + if (len(identity.Actions) == 0 || hasSessionToken) && iam.iamIntegration != nil { + return iam.authorizeWithIAM(r, identity, action, bucket, object) + } + if len(identity.Actions) > 0 { if !identity.CanDo(action, bucket, object) { return s3err.ErrAccessDenied } return s3err.ErrNone - } else if iam.iamIntegration != nil { - return iam.authorizeWithIAM(r, identity, action, bucket, object) } return s3err.ErrAccessDenied diff --git a/weed/s3api/auth_signature_v4.go b/weed/s3api/auth_signature_v4.go index adfc84653..05805f2ad 100644 --- a/weed/s3api/auth_signature_v4.go +++ b/weed/s3api/auth_signature_v4.go @@ -435,9 +435,9 @@ func (iam *IdentityAccessManagement) validateSTSSessionToken(r *http.Request, se } // Restore admin privileges if the session was created by an admin - if isAdmin, ok := claims["is_admin"].(bool); ok && isAdmin { - identity.Actions = append(identity.Actions, s3_constants.ACTION_ADMIN) - } + // if isAdmin, ok := claims["is_admin"].(bool); ok && isAdmin { + // identity.Actions = append(identity.Actions, s3_constants.ACTION_ADMIN) + // } glog.V(2).Infof("Successfully validated STS session token for principal: %s, assumed role user: %s", sessionInfo.Principal, sessionInfo.AssumedRoleUser) diff --git a/weed/s3api/s3api_server.go b/weed/s3api/s3api_server.go index 6defdbf10..43867ceb3 100644 --- a/weed/s3api/s3api_server.go +++ b/weed/s3api/s3api_server.go @@ -852,6 +852,12 @@ func loadIAMManagerFromConfig(configPath string, filerAddressProvider func() str if err := json.Unmarshal(configData, &configRoot); err != nil { return nil, fmt.Errorf("failed to parse config: %w", err) } + glog.V(0).Infof("DEBUG: Loaded IAM Config. Policy=%v. Raw JSON len=%d", configRoot.Policy, len(configData)) + if configRoot.Policy != nil { + glog.V(0).Infof("DEBUG: Policy Config: DefaultEffect='%s'", configRoot.Policy.DefaultEffect) + } else { + glog.V(0).Infof("DEBUG: Policy Config is NIL") + } // Ensure a valid policy engine config exists if configRoot.Policy == nil {