diff --git a/api/extensions/storage/aws_s3_storage.py b/api/extensions/storage/aws_s3_storage.py index 3fdfde2372..04d75a0f67 100644 --- a/api/extensions/storage/aws_s3_storage.py +++ b/api/extensions/storage/aws_s3_storage.py @@ -2,7 +2,7 @@ import logging from collections.abc import Generator import boto3 -from botocore.client import Config +from botocore.client import BaseClient, Config from botocore.exceptions import ClientError from configs import dify_config @@ -14,15 +14,34 @@ logger = logging.getLogger(__name__) class AwsS3Storage(BaseStorage): """Implementation for Amazon Web Services S3 storage.""" + client: BaseClient + def __init__(self): super().__init__() self.bucket_name = dify_config.S3_BUCKET_NAME + + # NOTE: + # Some S3-compatible providers (e.g. MinIO) are strict about presigned request + # signature calculation. If the client sends a `Content-Type` header while the + # server generates a SigV2 presigned URL (or otherwise signs an empty Content-Type), + # the request may be rejected with 403 (signature mismatch). + # + # Enforce SigV4 for presigned URLs to avoid accidental SigV2 fallback and to keep + # header signing behavior consistent across providers. + s3_client_config = Config( + signature_version="s3v4", + s3={"addressing_style": dify_config.S3_ADDRESS_STYLE}, + ) if dify_config.S3_USE_AWS_MANAGED_IAM: logger.info("Using AWS managed IAM role for S3") session = boto3.Session() region_name = dify_config.S3_REGION - self.client = session.client(service_name="s3", region_name=region_name) + self.client = session.client( + service_name="s3", + region_name=region_name, + config=s3_client_config, + ) else: logger.info("Using ak and sk for S3") @@ -32,7 +51,7 @@ class AwsS3Storage(BaseStorage): aws_access_key_id=dify_config.S3_ACCESS_KEY, endpoint_url=dify_config.S3_ENDPOINT, region_name=dify_config.S3_REGION, - config=Config(s3={"addressing_style": dify_config.S3_ADDRESS_STYLE}), + config=s3_client_config, ) # create bucket try: