From b7205e09a390ba982ba292ffdd133a09d52d04c2 Mon Sep 17 00:00:00 2001 From: Ben Vincent Date: Thu, 8 Jan 2026 23:54:39 +1100 Subject: [PATCH] Fix S3 SSL certificate validation and boto3 checksum compatibility - Add support for custom CA bundle via REQUESTS_CA_BUNDLE/SSL_CERT_FILE environment variables - Configure boto3 client with custom SSL verification to support Ceph RadosGW through nginx proxy - Maintain boto3 checksum validation configuration for compatibility with third-party S3 providers - Resolves XAmzContentSHA256Mismatch errors when connecting to RadosGW endpoints Fixes #4400 compatibility issue with boto3 v1.36+ stricter checksum validation --- src/artifactapi/storage.py | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/src/artifactapi/storage.py b/src/artifactapi/storage.py index 4a82ea8..a1df714 100644 --- a/src/artifactapi/storage.py +++ b/src/artifactapi/storage.py @@ -22,16 +22,25 @@ class S3Storage: self.bucket = bucket self.secure = secure - self.client = boto3.client( - "s3", - endpoint_url=f"http{'s' if self.secure else ''}://{self.endpoint}", - aws_access_key_id=self.access_key, - aws_secret_access_key=self.secret_key, - config=Config( - request_checksum_calculation="when_required", - response_checksum_validation="when_required" - ) - ) + ca_bundle = os.environ.get('REQUESTS_CA_BUNDLE') or os.environ.get('SSL_CERT_FILE') + config_kwargs = { + "request_checksum_calculation": "when_required", + "response_checksum_validation": "when_required" + } + client_kwargs = { + "endpoint_url": f"http{'s' if self.secure else ''}://{self.endpoint}", + "aws_access_key_id": self.access_key, + "aws_secret_access_key": self.secret_key, + "config": Config(**config_kwargs) + } + + if ca_bundle and os.path.exists(ca_bundle): + client_kwargs["verify"] = ca_bundle + print(f"Debug: Using CA bundle: {ca_bundle}") + else: + print(f"Debug: No CA bundle found. REQUESTS_CA_BUNDLE={os.environ.get('REQUESTS_CA_BUNDLE')}, SSL_CERT_FILE={os.environ.get('SSL_CERT_FILE')}") + + self.client = boto3.client("s3", **client_kwargs) # Try to ensure bucket exists, but don't fail if MinIO isn't ready yet try: