From 2da5460c1cbd1a0d215ba4e48cee27f96a036e14 Mon Sep 17 00:00:00 2001 From: chrislu Date: Wed, 12 Nov 2025 14:50:49 -0800 Subject: [PATCH] cache bucket policy --- weed/s3api/auth_credentials_subscribe.go | 15 ++++++++++++++ weed/s3api/s3api_bucket_config.go | 15 ++++++++++++++ weed/s3api/s3api_bucket_handlers.go | 25 +++++++++++++++--------- 3 files changed, 46 insertions(+), 9 deletions(-) diff --git a/weed/s3api/auth_credentials_subscribe.go b/weed/s3api/auth_credentials_subscribe.go index 09150f7c8..9dab99457 100644 --- a/weed/s3api/auth_credentials_subscribe.go +++ b/weed/s3api/auth_credentials_subscribe.go @@ -1,11 +1,13 @@ package s3api import ( + "encoding/json" "errors" "time" "github.com/seaweedfs/seaweedfs/weed/filer" "github.com/seaweedfs/seaweedfs/weed/glog" + "github.com/seaweedfs/seaweedfs/weed/iam/policy" "github.com/seaweedfs/seaweedfs/weed/pb" "github.com/seaweedfs/seaweedfs/weed/pb/filer_pb" "github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants" @@ -145,6 +147,19 @@ func (s3a *S3ApiServer) updateBucketConfigCacheFromEntry(entry *filer_pb.Entry) } else { glog.V(3).Infof("updateBucketConfigCacheFromEntry: no Object Lock configuration found for bucket %s", bucket) } + + // Parse bucket policy if present (for performance optimization) + if policyJSON, exists := entry.Extended[BUCKET_POLICY_METADATA_KEY]; exists && len(policyJSON) > 0 { + var policyDoc policy.PolicyDocument + if err := json.Unmarshal(policyJSON, &policyDoc); err != nil { + glog.Errorf("updateBucketConfigCacheFromEntry: failed to parse bucket policy for %s: %v", bucket, err) + } else { + config.BucketPolicy = &policyDoc + glog.V(2).Infof("updateBucketConfigCacheFromEntry: cached bucket policy for bucket %s", bucket) + } + } else { + glog.V(4).Infof("updateBucketConfigCacheFromEntry: no bucket policy found for bucket %s", bucket) + } } // Load CORS configuration from bucket directory content diff --git a/weed/s3api/s3api_bucket_config.go b/weed/s3api/s3api_bucket_config.go index 128b17c06..a103553a1 100644 --- a/weed/s3api/s3api_bucket_config.go +++ b/weed/s3api/s3api_bucket_config.go @@ -14,6 +14,7 @@ import ( "google.golang.org/protobuf/proto" "github.com/seaweedfs/seaweedfs/weed/glog" + "github.com/seaweedfs/seaweedfs/weed/iam/policy" "github.com/seaweedfs/seaweedfs/weed/kms" "github.com/seaweedfs/seaweedfs/weed/pb/filer_pb" "github.com/seaweedfs/seaweedfs/weed/pb/s3_pb" @@ -32,6 +33,7 @@ type BucketConfig struct { IsPublicRead bool // Cached flag to avoid JSON parsing on every request CORS *cors.CORSConfiguration ObjectLockConfig *ObjectLockConfiguration // Cached parsed Object Lock configuration + BucketPolicy *policy.PolicyDocument // Cached bucket policy for performance KMSKeyCache *BucketKMSCache // Per-bucket KMS key cache for SSE-KMS operations LastModified time.Time Entry *filer_pb.Entry @@ -376,6 +378,19 @@ func (s3a *S3ApiServer) getBucketConfig(bucket string) (*BucketConfig, s3err.Err } else { glog.V(3).Infof("getBucketConfig: no Object Lock config found in extended attributes for bucket %s", bucket) } + + // Parse bucket policy if present (for performance optimization) + if policyJSON, exists := entry.Extended[BUCKET_POLICY_METADATA_KEY]; exists && len(policyJSON) > 0 { + var policyDoc policy.PolicyDocument + if err := json.Unmarshal(policyJSON, &policyDoc); err != nil { + glog.Errorf("getBucketConfig: failed to parse bucket policy for %s: %v", bucket, err) + } else { + config.BucketPolicy = &policyDoc + glog.V(3).Infof("getBucketConfig: loaded bucket policy from extended attributes for bucket %s", bucket) + } + } else { + glog.V(4).Infof("getBucketConfig: no bucket policy found for bucket %s", bucket) + } } // Load CORS configuration from bucket directory content diff --git a/weed/s3api/s3api_bucket_handlers.go b/weed/s3api/s3api_bucket_handlers.go index b760a6ece..edac548cf 100644 --- a/weed/s3api/s3api_bucket_handlers.go +++ b/weed/s3api/s3api_bucket_handlers.go @@ -581,25 +581,32 @@ func isPublicReadGrants(grants []*s3.Grant) bool { // isBucketPolicyAllowed checks if a bucket policy allows anonymous access for the given action and resource func (s3a *S3ApiServer) isBucketPolicyAllowed(bucket, object string, action Action) bool { - // Get bucket policy - policyDoc, err := s3a.getBucketPolicy(bucket) - if err != nil { - // No bucket policy or error retrieving it - glog.V(4).Infof("isBucketPolicyAllowed: no bucket policy for %s: %v", bucket, err) + // Get cached bucket config which includes the policy + config, errCode := s3a.getBucketConfig(bucket) + if errCode != s3err.ErrNone { + glog.V(4).Infof("isBucketPolicyAllowed: failed to get bucket config for %s: %v", bucket, errCode) return false } + // Check if bucket has a policy + if config.BucketPolicy == nil { + glog.V(4).Infof("isBucketPolicyAllowed: no bucket policy for %s", bucket) + return false + } + + policyDoc := config.BucketPolicy + // Convert action to S3 action format s3Action := actionToS3Action(action) - + // Build resource ARN resource := buildResourceARN(bucket, object) - - glog.V(4).Infof("isBucketPolicyAllowed: evaluating bucket=%s, resource=%s, action=%s", bucket, resource, s3Action) + + glog.V(4).Infof("isBucketPolicyAllowed: evaluating bucket=%s, resource=%s, action=%s (cached policy)", bucket, resource, s3Action) // Evaluate policy using AWS policy evaluation logic: // 1. Check for explicit Deny - if found, return false - // 2. Check for explicit Allow - if found, return true + // 2. Check for explicit Allow - if found, return true // 3. If no explicit Allow is found, return false (default deny) hasExplicitAllow := false