S3 PUT 的 Boto generate_url 不适用于 IAM 角色?
Boto generate_url for S3 PUT doesn't work with IAM roles?
我使用以下 Python/Boto 代码生成 one-time file-upload URLs 到 Amazon S3 存储桶:
from boto.s3.connection import S3Connection
def get_signed_upload_url():
s3 = S3Connection(ACCESS_KEY_ID, SECRET_ACCESS_KEY, is_secure=True)
return s3.generate_url(300, 'PUT', bucket=BUCKET, key=KEY,
headers={'Content-Type': 'text/plain'})
它已经好几年了,今天还在继续工作。
但现在,我正在转换为 IAM 角色 — 这将使我免于 hard-coding ACCESS_KEY_ID
和 SECRET_ACCESS_KEY
。因此,我删除了 hard-coded 键,导致此代码:
from boto.s3.connection import S3Connection
def get_signed_upload_url():
s3 = S3Connection(is_secure=True)
return s3.generate_url(300, 'PUT', bucket=BUCKET, key=KEY,
headers={'Content-Type': 'text/plain'})
使用 this 代码,每当我生成 URL 并从我的 client-side 网络应用程序向它发出 PUT
请求时(通过Ajax),我从 S3 收到 HTTP 400 Bad Request
错误:
<Error>
<Code>InvalidToken</Code>
<Message>The provided token is malformed or otherwise invalid.</Message>
<!-- account-specific stuff removed -->
</Error>
为什么会这样?
一些额外的细节:
- 这被部署到自动分配其 IAM 角色的 EC2 服务器。我已确认该角色的授权已正确提供给我的 Python 代码。我代码的所有其他部分——包括生成
GET
URLs 的 generate_url()
调用——工作正常,使用自动传递到 EC2 实例环境的 ACCESS_KEY_ID
。
- Python 2.7.9 和 Boto 2.38.0.
- IAM 角色似乎拥有所有适当的权限,我的代码的其他部分使用该角色成功地与 S3 交互(获取项目和创建项目)这一事实证明了这一点。只有这个特殊的
PUT
不起作用。
- front-end 逻辑发送
Content-Type
header,这就是 header 在那里的原因。我尝试从这个 Boto 代码和 front-end 逻辑中删除 Content-Type
header,但问题仍然存在。
无论如何,我无法重现这个问题。我设置了一个全新的 EC2 实例,为其分配了一个 IAM 角色,该角色附加了 AmazonS3FullAccess 权限
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "s3:*",
"Resource": "*"
}
]
}
我能够安装的最早 Python 版本是 2.7.12 和 Boto 2.38.0
[root@ip-172-31-13-14 ~]# python -V
Python 2.7.12
[root@ip-172-31-13-14 ~]# pip freeze|grep boto
boto==2.38.0
botocore==1.5.95
我然后运行代码app.py
from boto.s3.connection import S3Connection
def get_signed_upload_url():
BUCKET = 'test'
KEY = 'test-2'
s3 = S3Connection(is_secure=True)
return s3.generate_url(300, 'PUT', bucket=BUCKET, key=KEY,
headers={'Content-Type': 'text/plain'})
if __name__ == "__main__":
url = get_signed_upload_url()
print "curl -v -X PUT -H 'Content-Type: text/plain' -d @hello2.txt '" + url + "'"
然后运行生成的cURL命令和文件上传成功
> Host: test.s3.amazonaws.com
> User-Agent: curl/7.53.1
> Accept: */*
> Content-Type: text/plain
> Content-Length: 8585
> Expect: 100-continue
>
< HTTP/1.1 100 Continue
* We are completely uploaded and fine
< HTTP/1.1 200 OK
< x-amz-id-2: redacted
< x-amz-request-id: redacted
< Date: Tue, 19 Dec 2017 05:45:15 GMT
< x-amz-version-id: redacted
< ETag: "redacted"
< Content-Length: 0
< Server: AmazonS3
现阶段我只能猜测您使用的 IAM 角色可能没有足够的权限来执行 PUT 操作?如果做不到这一点,您的客户端代码实际执行 PUT 请求的方式可能会有所不同,但这不太可能。
其他要检查的事项:
- 确保 EC2 实例上的时钟时间准确
- 确保设置了 AWS_REGION 环境变量
生成签名时的权限问题URL。您能否提供您附加到实例的角色。
在 EC 上明确地尝试 运行 这个命令,看看它到底在哪里失败了。
如果不是角色问题,那么肯定是您使用的boto SDK有问题。应该是2.5.1或更高版本。
您是否尝试将访问密钥和秘密密钥设置为 None?
好吧,我花了一年多的时间才弄明白 — 但我终于弄明白了。
问题是我的前端 JavaScript 在发出 Ajax 请求之前在已签名的 URL 上调用 decodeURIComponent()
。
在我切换到 IAM 角色之前,这不是问题,因为已签名的 URL 中没有任何有趣的字符 — 因此 decodeURIComponent()
没有任何效果。但 IAM 角色使用不同的签名 URL 语法:查询字符串包含许多斜杠,这些斜杠被 decodeURIComponent()
.
过度转义
希望这对某人有所帮助。
我使用以下 Python/Boto 代码生成 one-time file-upload URLs 到 Amazon S3 存储桶:
from boto.s3.connection import S3Connection
def get_signed_upload_url():
s3 = S3Connection(ACCESS_KEY_ID, SECRET_ACCESS_KEY, is_secure=True)
return s3.generate_url(300, 'PUT', bucket=BUCKET, key=KEY,
headers={'Content-Type': 'text/plain'})
它已经好几年了,今天还在继续工作。
但现在,我正在转换为 IAM 角色 — 这将使我免于 hard-coding ACCESS_KEY_ID
和 SECRET_ACCESS_KEY
。因此,我删除了 hard-coded 键,导致此代码:
from boto.s3.connection import S3Connection
def get_signed_upload_url():
s3 = S3Connection(is_secure=True)
return s3.generate_url(300, 'PUT', bucket=BUCKET, key=KEY,
headers={'Content-Type': 'text/plain'})
使用 this 代码,每当我生成 URL 并从我的 client-side 网络应用程序向它发出 PUT
请求时(通过Ajax),我从 S3 收到 HTTP 400 Bad Request
错误:
<Error>
<Code>InvalidToken</Code>
<Message>The provided token is malformed or otherwise invalid.</Message>
<!-- account-specific stuff removed -->
</Error>
为什么会这样?
一些额外的细节:
- 这被部署到自动分配其 IAM 角色的 EC2 服务器。我已确认该角色的授权已正确提供给我的 Python 代码。我代码的所有其他部分——包括生成
GET
URLs 的generate_url()
调用——工作正常,使用自动传递到 EC2 实例环境的ACCESS_KEY_ID
。 - Python 2.7.9 和 Boto 2.38.0.
- IAM 角色似乎拥有所有适当的权限,我的代码的其他部分使用该角色成功地与 S3 交互(获取项目和创建项目)这一事实证明了这一点。只有这个特殊的
PUT
不起作用。 - front-end 逻辑发送
Content-Type
header,这就是 header 在那里的原因。我尝试从这个 Boto 代码和 front-end 逻辑中删除Content-Type
header,但问题仍然存在。
无论如何,我无法重现这个问题。我设置了一个全新的 EC2 实例,为其分配了一个 IAM 角色,该角色附加了 AmazonS3FullAccess 权限
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "s3:*",
"Resource": "*"
}
]
}
我能够安装的最早 Python 版本是 2.7.12 和 Boto 2.38.0
[root@ip-172-31-13-14 ~]# python -V
Python 2.7.12
[root@ip-172-31-13-14 ~]# pip freeze|grep boto
boto==2.38.0
botocore==1.5.95
我然后运行代码app.py
from boto.s3.connection import S3Connection
def get_signed_upload_url():
BUCKET = 'test'
KEY = 'test-2'
s3 = S3Connection(is_secure=True)
return s3.generate_url(300, 'PUT', bucket=BUCKET, key=KEY,
headers={'Content-Type': 'text/plain'})
if __name__ == "__main__":
url = get_signed_upload_url()
print "curl -v -X PUT -H 'Content-Type: text/plain' -d @hello2.txt '" + url + "'"
然后运行生成的cURL命令和文件上传成功
> Host: test.s3.amazonaws.com
> User-Agent: curl/7.53.1
> Accept: */*
> Content-Type: text/plain
> Content-Length: 8585
> Expect: 100-continue
>
< HTTP/1.1 100 Continue
* We are completely uploaded and fine
< HTTP/1.1 200 OK
< x-amz-id-2: redacted
< x-amz-request-id: redacted
< Date: Tue, 19 Dec 2017 05:45:15 GMT
< x-amz-version-id: redacted
< ETag: "redacted"
< Content-Length: 0
< Server: AmazonS3
现阶段我只能猜测您使用的 IAM 角色可能没有足够的权限来执行 PUT 操作?如果做不到这一点,您的客户端代码实际执行 PUT 请求的方式可能会有所不同,但这不太可能。
其他要检查的事项:
- 确保 EC2 实例上的时钟时间准确
- 确保设置了 AWS_REGION 环境变量
生成签名时的权限问题URL。您能否提供您附加到实例的角色。
在 EC 上明确地尝试 运行 这个命令,看看它到底在哪里失败了。
如果不是角色问题,那么肯定是您使用的boto SDK有问题。应该是2.5.1或更高版本。
您是否尝试将访问密钥和秘密密钥设置为 None?
好吧,我花了一年多的时间才弄明白 — 但我终于弄明白了。
问题是我的前端 JavaScript 在发出 Ajax 请求之前在已签名的 URL 上调用 decodeURIComponent()
。
在我切换到 IAM 角色之前,这不是问题,因为已签名的 URL 中没有任何有趣的字符 — 因此 decodeURIComponent()
没有任何效果。但 IAM 角色使用不同的签名 URL 语法:查询字符串包含许多斜杠,这些斜杠被 decodeURIComponent()
.
希望这对某人有所帮助。