【问题标题】:Presigned POST URLs work locally but not in Lambda预签名的 POST URL 在本地工作,但在 Lambda 中不工作
【发布时间】:2019-10-31 12:15:14
【问题描述】:

我有一些 Python 可以请求预签名的 POST URL 以将对象上传到 S3 存储桶。它可以在具有管理员权限的 IAM 用户下在本地运行,我可以使用 Postman 和 cURL 将内容上传到存储桶。但是,当尝试在 Lambda 中运行相同的代码时,它会显示“您提供的 AWS 访问密钥 ID 在我们的记录中不存在。”。

唯一的区别是 Lambda 函数在没有管理员权限的情况下运行(但它确实有一个允许它在存储桶上运行任何 S3 操作的策略)并且使用的是不同(旧)版本的 Boto3。

这是我尝试使用的代码:https://boto3.amazonaws.com/v1/documentation/api/latest/guide/s3-presigned-urls.html#generating-a-presigned-url-to-upload-a-file

我尝试使用从 Lambda 函数返回的详细信息,其方式与我使用本地返回的详细信息完全相同,但 Lambda 详细信息不起作用。

【问题讨论】:

  • 因此,您的 Lambda 函数配置了一个 IAM 角色,其策略包括相关存储桶上的 s3:*。您的 Lambda 函数能否列出该存储桶中的对象,或从该存储桶获取对象(或者这些操作是否也会因错误而失败)?
  • 正确,我的 Lambda 函数配置为使用一个 IAM 角色,该角色的策略包括 s3:* 在我尝试为其生成预签名 URL 的存储桶上。我刚刚测试过,它也可以列出该桶中的所有对象。
  • 您的客户端最终用于 POST 的预签名 URL 是否已损坏?
  • 您找到解决方案了吗?我遇到了同样的问题 py/.net lambdas,在本地它们都工作得很好......

标签: amazon-web-services amazon-s3 aws-lambda boto3 boto


【解决方案1】:

这是 100% 可行的 AWS lambda 解决方案

  1. 附加策略 AmazonS3FullAccess

  2. 不要使用multipart/form-data上传

  3. 配置 S3 CORS

  4. 使用下一个 python 代码

     import uuid
     import boto3
    
     def lambda_handler(event, context):
    
         s3 = boto3.client('s3')
    
         upload_key = 'myfile.pdf'
         download_key = 'myfile.pdf'
    
         bucket = 'mys3storage'
    
         # Generate the presigned URL for download
         presigned_download_url = s3.generate_presigned_url(
             ClientMethod='get_object',
             Params={
                 'Bucket': bucket,
                 'Key': download_key,
                 'Expires': 3600
             }
         )
    
         # Generate the presigned URL for upload
         presigned_upload_url = s3.generate_presigned_url(
             ClientMethod='put_object',
             Params={
                 'Bucket': bucket,
                 'Key': upload_key,
                 'ContentType': 'application/pdf',
                 'Expires': 3600
             }
         )
    
         # return the result
         return {
             "upload_url": presigned_upload_url
             "download_url": download_url
         }
    

【讨论】:

    【解决方案2】:

    使用角色时需要发布x-amz-security-token

    【讨论】:

      【解决方案3】:

      我遇到了同样的问题,这让我发疯了。本地一切都很顺利,一旦部署到 lambda 中,我使用 create_presigned_post 或 create_presigned_url 得到 403。

      事实证明 lambda 使用的角色与我的本地 aws 用户所拥有的角色不同。 (在我的例子中,lambda 角色是使用 AWS SAM 自动创建的)授予 lambda 角色 S3 权限后,错误得到解决。

      【讨论】:

        【解决方案4】:

        这是轻微的duplicate...

        基本上,一旦 Lambda 函数完成,临时 lambda 执行角色的凭证就会过期。因此,但是当您的客户端使用签名 URL 时,凭据不再有效/不存在。

        这里的解决方案是使用 AWS STS 在创建签名 URL 时使用具有必要 S3 权限的 Lambda(又名 AssumeRole)中的不同 IAM 角色。此角色的凭据不会过期,因此 URL 将保持有效。

        有关更多设置说明,请参阅this example

        【讨论】:

          【解决方案5】:

          好问题。您没有描述如何将凭证获取到 Lambda 函数。你的代码,特别是这个:

          s3_client = boto3.client('s3')
          

          期望使用 ~/.aws/credentials 文件查找默认凭据。您不会(也不应该)在您的 Lambda 执行环境中拥有该文件,但您可能在本地环境中拥有它。我怀疑您根本没有获得 Lambda 函数的凭据。

          在 Lambda 中可以使用两个选项来获取凭证。

          1. 不要使用凭证,而是将 IAM 角色用于提供对所需 S3 的访问的 Lambda 函数。这是最佳实践。如果这样做,您将不需要凭据。这是最佳做法。
          2. 将凭据设置为 lambda 函数的环境变量。您可以直接定义 AWS_ACCESS_KEY_ID 和 AWS_SECRET_ACCESS_KEY,然后上面的代码应该只是将它们拾取并使用它们。

          【讨论】:

          • 我在我的 Lambda 函数中使用:session = boto3.Session(),然后是 s3_client = session.client('s3'),它使用专门为该 Lambda 函数创建的 IAM 角色运行,其中附加的策略之一是允许 s3:*。我现在想知道 Boto 的版本之间是否存在一些差异,因为 s3_client.generate_presigned_post(...) 在本地运行与在 Lambda 中运行时返回的结果略有不同。在Lambda中运行返回一个额外的键值对,键是x-amz-security-token,本地调用时没有。
          【解决方案6】:

          Official python tutorial for this 没有提到 x-amz-security-token 使用 lambda 函数,但是在将文件上传到 S3 时需要将其作为表单值包含在内。回顾一下,在使用 lambda 时,请确保附加到函数的角色具有 s3 访问权限,并且额外的表单字段存在 x-amz-security-token 值。

              <form action="URL HERE" method="post" enctype="multipart/form-data">
                <input type="hidden" name="key" value="KEY HERE" />
                <input type="hidden" name="AWSAccessKeyId" value="ACCESS KEY HERE" />
          
           <!-- ADD THIS ONE -->
            <input type="hidden" name="x-amz-security-token" value="SECURITY TOKEN HERE" />
           <!-- ADD THIS ONE -->
            
              <input type="hidden" name="policy" value="POLICY HERE" />
                <input type="hidden" name="signature" value="SIGNATURE HERE" />
              File:
                <input type="file"   name="file" /> <br />
                <input type="submit" name="submit" value="Upload to Amazon S3" />
              </form>
          

          【讨论】:

            【解决方案7】:

            您可以尝试以下代码为对象生成预签名 URL

            import json
            import boto3
            from botocore.exceptions import ClientError
            
            s3 = boto3.client('s3')
            bucket = 'test1'
            download_key = 'path/to/Object.txt'
            
            def lambda_handler(event, context):
                try:
                    response = s3.generate_presigned_url('get_object',Params={'Bucket': bucket,'Key': download_key},ExpiresIn=3600)
                except ClientError as e:
                    logging.error(e)
                    return None
                url = response
                print(url)
                return{
                    'url' : url
                }
            

            【讨论】:

              猜你喜欢
              • 2021-01-10
              • 2019-04-20
              • 2017-06-05
              • 2023-03-12
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2021-01-08
              • 2014-01-20
              相关资源
              最近更新 更多