我在 Cloudformation 堆栈中有一个用于保存工件的 S3 存储桶。同一堆栈中的 lambda 函数会生成一个预签名 URL,供客户端将文件上传到存储桶中。
我之前已经让代码运行了,但今天重新部署堆栈后,我的客户端都无法将任何内容上传到预签名的 URL——S3 似乎关闭了连接而没有返回响应。我检查了 lambda 函数是否对存储桶具有 CRUD 访问权限——但为了排除 IAM 问题,我使用管理员 IAM 凭据在我的计算机上本地运行了测试代码(如下):
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 从不返回响应而是关闭它,并且请求/urllib 会抛出有关管道损坏的异常:
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'))
我以前从未遇到过这个问题——当我错误配置 ACL/预签名条件时,S3 至少会向我返回一个特定的 HTTP 状态代码和一个 XML 消息,说明出了什么问题。
令人沮丧的是,这似乎只发生在属于堆栈的特定存储桶上。如果我使用上面相同的代码为具有各种 ACL 规则/ CORS 设置等的任何其他存储桶生成预签名 URL,它们都可以工作。
重新部署堆栈或以不同的名称创建堆栈的副本(导致不同的存储桶名称,但仍属于堆栈)仍会导致预签名上传失败。
今天出现此问题时,我尚未对堆栈中的存储桶属性进行任何更改。我的 CloudFormation (SAM) 模板中的存储桶声明很简单:
MyCursedBucket:
Type: AWS::S3::Bucket
仅此而已。
是什么导致了这个问题?预签名上传是否需要我在 CF 模板中遗漏的特殊设置才能正常工作?