S3 PUT的Boto generate_url不适用于IAM角色?

时间:2017-12-06 10:09:38

标签: amazon-web-services amazon-s3 boto amazon-iam

我使用以下Python / Boto代码为Amazon S3存储桶生成一次性文件上传URL:

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角色 - 这将使我免于对ACCESS_KEY_IDSECRET_ACCESS_KEY进行硬编码。因此,我删除了硬编码密钥,产生了以下代码:

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'})

使用代码,每当我生成一个URL并从我的客户端Web应用程序(通过Ajax)向它发出PUT请求时,我都会得到一个HTTP 400 Bad Request来自S3的错误:

<Error>
    <Code>InvalidToken</Code>
    <Message>The provided token is malformed or otherwise invalid.</Message>
    <!-- account-specific stuff removed -->
</Error>

为什么会这样?

一些额外的细节:

  • 将其部署到EC2服务器,该服务器自动分配其IAM角色。我已经确认该角色的授权可以正确地提供给我的Python代码。我的代码的所有其他部分 - 包括生成generate_url()网址的GET调用 - 使用自动传递到EC2实例环境的ACCESS_KEY_ID工作正常。
  • Python 2.7.9和Boto 2.38.0。
  • IAM角色似乎具有所有适当的权限,我的代码的其他部分使用该角色成功地与S3(获取项目和创建项目)进行交互这一事实证明了这一点。只有这个特殊的PUT无效。
  • 前端逻辑发送Content-Type标头,这就是那个标头在那里的原因。我尝试从此Boto代码和前端逻辑中删除Content-Type标头,但问题仍然存在。

3 个答案:

答案 0 :(得分:1)

为了它的价值,我无法复制这个问题。我已经设置了一个全新的EC2实例,为其分配了一个附有AmazonS3FullAccess权限的IAM角色

{
    "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环境变量

答案 1 :(得分:0)

生成签名网址时是权限问题。您能否提供您附加到实例的角色。

尝试在EC上显式运行此命令,以查看其确切失败的位置。

如果它不是角色问题,那么您正在使用的boto SDK肯定存在问题。它应该是2.5.1或更高版本。

您是否尝试将访问密钥和密钥设置为无?

答案 2 :(得分:0)

好吧,我花了一年多的时间才弄清楚-但我终于做到了。

问题是我的前端JavaScript在发出Ajax请求之前正在签名的URL上调用decodeURIComponent()

在我转换为IAM角色之前,这不是问题,因为签名的URL中没有任何有趣的字符-因此decodeURIComponent()无效。但是IAM角色使用不同的签名URL语法:查询字符串包含许多斜杠,这些斜杠已被decodeURIComponent()过度转义。

希望这对某人有帮助。