尝试将对象放入 s3 时访问被拒绝

Posted

技术标签:

【中文标题】尝试将对象放入 s3 时访问被拒绝【英文标题】:Access Denied when trying to PutObject to s3 【发布时间】:2022-01-01 07:46:53 【问题描述】:

我正在使用无服务器框架创建一个 lambda,将 CSV 保存到 S3 存储桶。

我已经有一个类似的 lambda 可以用另一个存储桶执行此操作。

这就是奇怪的地方:我可以将 CSV 上传到我创建的第一个 S3 存储桶(几个月前),但是在将相同的 CSV 上传到新的 S3 存储桶时出现 AccessDenied 错误,如据我所知,通过 serverless.yml 配置以与第一个完全相同的方式创建。

错误是:

Error: AccessDenied: Access Denied

这些是 serverless.yml 中的相关位:

provider:
  name: aws
  runtime: nodejs12.x
  stage: $opt:stage, 'dev'
  region: eu-west-1
  environment:
    BUCKET_NEW: $self:custom.bucketNew
    BUCKET: $self:custom.bucket

  iam:
    role:
      statements:
        - Effect: 'Allow'
          Action: 'lambda:InvokeFunction'
          Resource: '*'
        - Effect: 'Allow'
          Action:
            - 's3:GetObject'
            - 's3:PutObject'
          Resource:
            - 'arn:aws:s3:::*' # Added this whilst debugging
            - 'arn:aws:s3:::*/*' # Added this whilst debugging
            - 'arn:aws:s3:::$self:custom.bucket'
            - 'arn:aws:s3:::$self:custom.bucket/*'
            - 'arn:aws:s3:::$self:custom.bucketNew'
            - 'arn:aws:s3:::$self:custom.bucketNew/*'

functions:
  uploadReport:
    handler: services/uploadReport.handler
    vpc:
      securityGroupIds:
        - 000001
      subnetIds:
        - subnet-00000A
        - subnet-00000B
        - subnet-00000C

resources:
  Resources:
    Bucket:
      Type: 'AWS::S3::Bucket'
      Properties:
        BucketName: $self:custom.bucket
    BucketNew:
      Type: 'AWS::S3::Bucket'
      Properties:
        BucketName: $self:custom.bucketNew

custom:
  stage: $opt:stage, 'dev'
  bucket: $self:service-$self:custom.stage-report
  bucketNew: $self:service-$self:custom.stage-report-new

Lambda 代码(简化):

const fs = require('fs')
const AWS = require('aws-sdk')

const S3 = new AWS.S3(
  httpOptions: 
    connectTimeout: 1000,
  ,
)


const uploadToS3 = (params) => new Promise((resolve, reject) => 
  S3.putObject(params, err => (err ? reject(err) : resolve(params.Key)))
)

module.exports.handler = async () => 
  const fileName = `report-new.csv`
  const filePath = `/tmp/$fileName`

  // Some code that creates a CSV file at filePath.

  const bucketParams = 
    Bucket: process.env.BUCKET_NEW, // Works for process.env.BUCKET, but not process.env.BUCKET_NEW.
    Key: fileName,
    Body: fs.readFileSync(filePath).toString('utf-8'),
  

  try 
    const s3Upload = await uploadToS3(bucketParams)
   catch (e) 
    throw new Error(e) // Throws Error: AccessDenied: Access Denied.
  

【问题讨论】:

您可以为 AWS Lambda 函数分配一个 IAM 角色,从而授予它访问您的 AWS 资源的权限。在我看来,您上面显示的 IAM 角色是用于调用 Lambda 函数 (lambda:InvokeFunction) 的角色,而不是 使用的 IAM 角色执行代码时的 Lambda 函数。 嗨@JohnRotenstein,不幸的是,情况并非如此。我可以在 AWS 控制台中我的 Lambda 配置的 Permissions 选项卡下看到 Lambda 本身具有 lambda:InvokeFunction 和 S3:GetObject 和 S3:PutObject 权限。当您在 serverless.yml 的 Provider 选项卡下分配 IAM 角色时,您将授予所有 lambda 函数其中指定的权限。 【参考方案1】:

我认为您可能缺少一些我经常在我的无服务器应用程序上使用“s3:Put*”的权限,这可能是不可取的,因为它是如此广泛。

这是上传我在What minimum permissions should I set to give S3 file upload access? 找到的对象所需的最低权限列表

"s3:PutObject",
"s3:PutObjectAcl",
"s3:GetObjectAcl",
"s3:ListBucket",
"s3:GetBucketLocation"

【讨论】:

恐怕运气不好。我已将范围完全扩大到 s3:*。我可以上传到我创建的第一个存储桶,但不能上传到新存储桶。即使两者都是以相同的方式创建的。【参考方案2】:

找到了解决方案,但这是我自己的错误。我的 lambda 实际上在 VPC 中。我最初的问题(在编辑之前)没有显示这一点。

VPC 中的 Lambda 无法与 S3 存储桶通信,除非 VPC 具有端点网关,使其能够与任何特别引用的存储桶进行通信。

我之前创建了一个端点网关,让它与我不久前创建的初始存储桶通信,但忘记更新端点网关以让它与新存储桶通信。

除非其他人花一整天时间试图解决一些愚蠢的问题,否则请在此处留下这个答案。

【讨论】:

以上是关于尝试将对象放入 s3 时访问被拒绝的主要内容,如果未能解决你的问题,请参考以下文章

Amazon S3 访问仅在 CopyObject 中被拒绝

AWS S3 - 限制对引用者的访问时访问被拒绝

当从 s3 获取对象时,aws lambda 函数被拒绝访问

Cloudfront 访问被拒绝

S3 错误:使用嵌套堆栈部署 CFN 模板时访问被拒绝

Rails:回形针上传时 AWS S3 访问被拒绝错误