【问题标题】:Boto generate_url for S3 PUT doesn't work with IAM roles?S3 PUT 的 Boto generate_url 不适用于 IAM 角色?
【发布时间】:2018-05-20 04:18:40
【问题描述】:

我使用以下 Python/Boto 代码生成一次性文件上传 URL 到 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 角色 - 这将使我免于对 ACCESS_KEY_ID 和 SECRET_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'})

使用 this 代码,每当我生成一个 URL 并从我的客户端 Web 应用程序(通过 Ajax)向它发出 PUT 请求时,我都会从 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 URL 的 generate_url() 调用)都可以正常工作,使用自动传递给 EC2 实例环境的 ACCESS_KEY_ID。
  • Python 2.7.9 和 Boto 2.38.0。
  • IAM 角色似乎拥有所有适当的权限,我的代码的其他部分使用该角色成功地与 S3 交互(获取项目和创建项目)就证明了这一点。只有这个特定的PUT 不起作用。
  • 前端逻辑发送一个Content-Type 标头,这就是该标头存在的原因。我尝试从这个 Boto 代码和前端逻辑中删除 Content-Type 标头,但问题仍然存在。

【问题讨论】:

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


【解决方案1】:

嗯,我花了一年多的时间才弄明白——但我终于弄明白了。

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

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

希望这对某人有所帮助。

【讨论】:

    【解决方案2】:

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

    尝试在 EC 上显式运行此命令,以查看它到底在哪里失败。

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

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

    【讨论】:

      【解决方案3】:

      对于它的价值,我无法复制这个问题。我已经设置了一个全新的 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 环境变量

      【讨论】:

      • 感谢您抽出宝贵时间对此进行调查!我已确认 EC2 实例和 S3 存储桶位于同一区域。我会尝试弄乱 IAM 角色。
      • 我已将角色设置为完全 S3 访问权限,与上面的 JSON 完全相同,但问题仍然存在。
      猜你喜欢
      • 1970-01-01
      • 2022-01-26
      • 1970-01-01
      • 2019-12-13
      • 2019-10-26
      • 2015-11-13
      • 1970-01-01
      • 2016-09-28
      • 2016-07-10
      相关资源
      最近更新 更多