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_IDSECRET_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>

为什么会这样?

一些额外的细节:

无论如何,我无法重现这个问题。我设置了一个全新的 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().

过度转义

希望这对某人有所帮助。