I have an S3 bucket in a Cloudformation stack that holds artifacts. A lambda function in the same stack generates a presigned URL for clients to upload files into the bucket.
I already had the code working beforehand, but after I redeployed the stack today, none of my clients were able to upload anything to the presigned URLs – S3 seems to close the connection without returning a response. I checked the lambda function had CRUD access to the bucket – but to rule out IAM issues, I ran testing code (below) locally on my machine with admin IAM credentials:
import boto3
import requests
s3_client = boto3.client("s3")
# re-enacting what my lambda function would have done
data = s3_client.generate_presigned_post(Bucket="my-cursed-bucket", Key="somefile.txt")
# {'url' : 'https://my-cursed-bucket.s3.amazonaws.com', 'fields': ...}
# re-enacting what my REST clients would have done
file = open('somefile.txt', 'rb')
requests.post(url=data['url'], data=data['fields'], files={'file': file})
S3 never returns a response but closes it, and requests/urllib throws exceptions about broken pipes:
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/lib/python3.8/site-packages/requests/api.py", line 119, in post
return request('post', url, data=data, json=json, **kwargs)
File "/usr/local/lib/python3.8/site-packages/requests/api.py", line 61, in request
return session.request(method=method, url=url, **kwargs)
File "/usr/local/lib/python3.8/site-packages/requests/sessions.py", line 530, in request
resp = self.send(prep, **send_kwargs)
File "/usr/local/lib/python3.8/site-packages/requests/sessions.py", line 643, in send
r = adapter.send(request, **kwargs)
File "/usr/local/lib/python3.8/site-packages/requests/adapters.py", line 498, in send
raise ConnectionError(err, request=request)
requests.exceptions.ConnectionError: ('Connection aborted.', BrokenPipeError(32, 'Broken pipe'))
I have never run into this issue before – when I had badly configured ACLs/presign conditions, S3 would at least return me a certain HTTP status code and an XML message saying what was wrong.
Frustratingly, this appears to be happening to only that specific bucket belonging to the stack. If I use the same code above to generate presigned URLs for any other bucket with a variety of ACL rules / CORS settings etc, they all work.
Redeploying the stack, or creating a copy of the stack under a different name (resulting in different bucket names, but still belonging to the stack) will still result in presigned uploads failing.
I had not made any changes to the bucket's properties in the stack when this issue started today. My bucket's declaration in my CloudFormation (SAM) template is simply:
MyCursedBucket:
Type: AWS::S3::Bucket
and nothing more.
What is causing this issue? Is there a special setting needed for presign uploads to work that I left out in the CF template?