Browse Source

feat(sts): implement strict trust policy validation for AssumeRole

pull/8003/head
Chris Lu 17 hours ago
parent
commit
5dcc2f8642
  1. 43
      weed/iam/integration/iam_manager_trust.go
  2. 15
      weed/s3api/auth_credentials_trust.go
  3. 9
      weed/s3api/s3_iam_middleware.go
  4. 27
      weed/s3api/s3api_sts.go

43
weed/iam/integration/iam_manager_trust.go

@ -0,0 +1,43 @@
package integration
import (
"context"
"fmt"
"github.com/seaweedfs/seaweedfs/weed/iam/policy"
"github.com/seaweedfs/seaweedfs/weed/iam/utils"
)
// ValidateTrustPolicyForPrincipal validates if a principal is allowed to assume a role
func (m *IAMManager) ValidateTrustPolicyForPrincipal(ctx context.Context, roleArn, principalArn string) error {
if !m.initialized {
return fmt.Errorf("IAM manager not initialized")
}
// Extract role name from ARN
roleName := utils.ExtractRoleNameFromArn(roleArn)
// Get role definition
roleDef, err := m.roleStore.GetRole(ctx, m.getFilerAddress(), roleName)
if err != nil {
return fmt.Errorf("role not found: %s", roleName)
}
if roleDef.TrustPolicy == nil {
return fmt.Errorf("role has no trust policy")
}
// Create evaluation context
evalCtx := &policy.EvaluationContext{
Principal: principalArn,
Action: "sts:AssumeRole",
Resource: roleArn,
}
// Evaluate the trust policy
if !m.evaluateTrustPolicy(roleDef.TrustPolicy, evalCtx) {
return fmt.Errorf("trust policy denies access to principal: %s", principalArn)
}
return nil
}

15
weed/s3api/auth_credentials_trust.go

@ -0,0 +1,15 @@
package s3api
import (
"context"
"fmt"
)
// ValidateTrustPolicyForPrincipal validates if a principal is allowed to assume a role
// Delegates to the IAM integration if available
func (iam *IdentityAccessManagement) ValidateTrustPolicyForPrincipal(ctx context.Context, roleArn, principalArn string) error {
if iam.iamIntegration != nil {
return iam.iamIntegration.ValidateTrustPolicyForPrincipal(ctx, roleArn, principalArn)
}
return fmt.Errorf("IAM integration not available")
}

9
weed/s3api/s3_iam_middleware.go

@ -23,6 +23,7 @@ type IAMIntegration interface {
AuthenticateJWT(ctx context.Context, r *http.Request) (*IAMIdentity, s3err.ErrorCode)
AuthorizeAction(ctx context.Context, identity *IAMIdentity, action Action, bucket string, objectKey string, r *http.Request) s3err.ErrorCode
ValidateSessionToken(ctx context.Context, token string) (*sts.SessionInfo, error)
ValidateTrustPolicyForPrincipal(ctx context.Context, roleArn, principalArn string) error
}
// S3IAMIntegration provides IAM integration for S3 API
@ -224,6 +225,14 @@ func (s3iam *S3IAMIntegration) AuthorizeAction(ctx context.Context, identity *IA
return s3err.ErrNone
}
// ValidateTrustPolicyForPrincipal delegates to IAMManager to validate trust policy
func (s3iam *S3IAMIntegration) ValidateTrustPolicyForPrincipal(ctx context.Context, roleArn, principalArn string) error {
if s3iam.iamManager == nil {
return fmt.Errorf("IAM manager not available")
}
return s3iam.iamManager.ValidateTrustPolicyForPrincipal(ctx, roleArn, principalArn)
}
// IAMIdentity represents an authenticated identity with session information
type IAMIdentity struct {
Name string

27
weed/s3api/s3api_sts.go

@ -274,6 +274,14 @@ func (h *STSHandlers) handleAssumeRole(w http.ResponseWriter, r *http.Request) {
return
}
// Validate that the target role trusts the caller (Trust Policy)
// This ensures the role's trust policy explicitly allows the principal to assume it
if err := h.iam.ValidateTrustPolicyForPrincipal(r.Context(), roleArn, identity.PrincipalArn); err != nil {
glog.V(2).Infof("AssumeRole: trust policy validation failed for %s to assume %s: %v", identity.Name, roleArn, err)
h.writeSTSErrorResponse(w, r, STSErrAccessDenied, fmt.Errorf("trust policy denies access"))
return
}
// Generate common STS components
stsCreds, assumedUser, err := h.prepareSTSCredentials(roleArn, roleSessionName, identity.PrincipalArn, durationSeconds, nil)
if err != nil {
@ -342,14 +350,21 @@ func (h *STSHandlers) handleAssumeRoleWithLDAPIdentity(w http.ResponseWriter, r
// Find an LDAP provider from the registered providers
var ldapProvider *ldap.LDAPProvider
ldapProvidersFound := 0
for _, provider := range h.stsService.GetProviders() {
// Check if this is an LDAP provider by type assertion
if p, ok := provider.(*ldap.LDAPProvider); ok {
ldapProvider = p
break
if ldapProvider == nil {
ldapProvider = p
}
ldapProvidersFound++
}
}
if ldapProvidersFound > 1 {
glog.Warningf("Multiple LDAP providers found (%d). Using the first one found (non-deterministic).", ldapProvidersFound)
}
if ldapProvider == nil {
glog.V(2).Infof("AssumeRoleWithLDAPIdentity: no LDAP provider configured")
h.writeSTSErrorResponse(w, r, STSErrAccessDenied,
@ -390,9 +405,11 @@ func (h *STSHandlers) handleAssumeRoleWithLDAPIdentity(w http.ResponseWriter, r
PrincipalArn: fmt.Sprintf("arn:aws:iam::%s:user/%s", accountId, identity.UserID),
}
if authErr := h.iam.VerifyActionPermission(r, ldapUserIdentity, Action("sts:AssumeRole"), "", roleArn); authErr != s3err.ErrNone {
glog.V(2).Infof("AssumeRoleWithLDAPIdentity: authorization failed for %s to assume %s: %v", ldapUsername, roleArn, authErr)
h.writeSTSErrorResponse(w, r, STSErrAccessDenied, fmt.Errorf("access denied"))
// Verify that the identity is allowed to assume the role by checking the Trust Policy
// The LDAP user doesn't have identity policies, so we strictly check if the Role trusts this principal.
if err := h.iam.ValidateTrustPolicyForPrincipal(r.Context(), roleArn, ldapUserIdentity.PrincipalArn); err != nil {
glog.V(2).Infof("AssumeRoleWithLDAPIdentity: trust policy validation failed for %s to assume %s: %v", ldapUsername, roleArn, err)
h.writeSTSErrorResponse(w, r, STSErrAccessDenied, fmt.Errorf("trust policy denies access"))
return
}

Loading…
Cancel
Save