为啥 AWS Lambda CFN S3 响应在删除事件时返回 403?

Posted

技术标签:

【中文标题】为啥 AWS Lambda CFN S3 响应在删除事件时返回 403?【英文标题】:Why does AWS Lambda CFN S3-response returns 403 upon Delete event?为什么 AWS Lambda CFN S3 响应在删除事件时返回 403? 【发布时间】:2019-07-05 12:48:47 【问题描述】:

我使用serverless 部署应用程序,我使用Custom Resource 迁移RDS 数据库。

在我部署时一切正常,但是当我删除堆栈时,自定义资源在一小时后超时,并显示消息“自定义资源未能在预期时间内稳定。”。对预签名 AWS S3 URL 的请求返回 403,错误代码为 AccessDenied

我第一次成功发送了对预签名 URL 的响应正文(在创建时):


    "Status": "SUCCESS",
    "RequestId": "bd487606-8017-49f2-99af-b29b2bbad40b",
    "LogicalResourceId": "SheltersDBMigrationTrigger",
    "StackId": "arn:aws:cloudformation:us-east-1:848139458219:stack/update-shelters-dev/c08a80e0-2e4e-11e9-87a6-124d1eab42ba",
    "PhysicalResourceId": "DB_MIGRATION"

我第二次向预签名 URL 发送了失败的响应正文(删除时):


    "Status": "SUCCESS",
    "RequestId": "2d166d36-7c0c-4848-9eb5-aedaf5e9172c",
    "LogicalResourceId": "SheltersDBMigrationTrigger",
    "StackId": "arn:aws:cloudformation:us-east-1:848139458219:stack/update-shelters-dev/c08a80e0-2e4e-11e9-87a6-124d1eab42ba",
    "PhysicalResourceId": "DB_MIGRATION"

lambda.go:

func handler(ctx context.Context, event cfn.Event) (rid string, data map[string]interface, err error) 
  rid = "DB_MIGRATION"

  if event.RequestType != cfn.RequestCreate 
    return
  

  db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@(%s)/", os.Getenv("DB_MASTER_USER"), os.Getenv("DB_MASTER_PASSWORD"), os.Getenv("DB_ADDRESS")))
  if err != nil 
    panic(err)
  
  defer db.Close()

  defer func() 
    if r := recover(); r != nil 
      err = fmt.Errorf("handler: Failed to migrate DB: %v", r)
    
  ()

  MigrateDb(db)

  return


func main() 
  lambda.Start(cfn.LambdaWrap(handler))

Lambda CFN 的无服务器配置

functions:
  dbMigration:
    handler: lambda-bin/migrate-db
    environment:
      DB_MASTER_USER: $env:DB_MASTER_USER
      DB_MASTER_PASSWORD: $env:DB_MASTER_PASSWORD
      DB_ADDRESS:
        "Fn::GetAtt": [ SheltersDB, Endpoint.Address ]
    vpc:
      securityGroupIds:
        - Ref: SheltersVPCSecurityGroup
      subnetIds:
        - Ref: SheltersSubnet1
        - Ref: SheltersSubnet2

...

Resources:
    SheltersDBMigrationTrigger:
      Type: Custom::DBMigration
      DependsOn:
        - SheltersDB
      Properties:
        ServiceToken: !GetAtt
          - DbMigrationLambdaFunction
          - Arn
    SheltersSubnet1:
      Type: AWS::EC2::Subnet
      Properties:
        AvailabilityZone: !Select [ 0, Fn::GetAZs: "" ]
        CidrBlock: 10.0.1.0/24
        VpcId: !Ref SheltersVPC
    SheltersSubnet2:
      Type: AWS::EC2::Subnet
      Properties:
        AvailabilityZone: !Select [ 1, Fn::GetAZs: "" ]
        CidrBlock: 10.0.2.0/24
        VpcId: !Ref SheltersVPC
    SheltersVPCSecurityGroup:
      Type: AWS::EC2::SecurityGroup
      Properties:
        GroupDescription: "Security group for DB connections"
        VpcId: !Ref SheltersVPC
    SheltersVPCSecurityGroupIngress:
      Type: AWS::EC2::SecurityGroupIngress
      Properties:
        GroupId: !Ref SheltersVPCSecurityGroup
        IpProtocol: tcp
        FromPort: "3306"
        ToPort: "3306"
        SourceSecurityGroupId: !Ref SheltersVPCSecurityGroup
    SheltersVPC:
      Type: AWS::EC2::VPC
      Properties:
        CidrBlock: 10.0.0.0/16
    SheltersRouteTable:
      Type: AWS::EC2::RouteTable
      Properties:
        VpcId: !Ref SheltersVPC
    SheltersSubnet1Association:
      Type: AWS::EC2::SubnetRouteTableAssociation
      Properties:
        SubnetId: !Ref SheltersSubnet1
        RouteTableId: !Ref SheltersRouteTable
    SheltersSubnet2Association:
      Type: AWS::EC2::SubnetRouteTableAssociation
      Properties:
        SubnetId: !Ref SheltersSubnet2
        RouteTableId: !Ref SheltersRouteTable
    SheltersVPCS3Endpoint:
      Type: AWS::EC2::VPCEndpoint
      Properties:
        VpcId: !Ref SheltersVPC
        PolicyDocument: "\"Version\":\"2008-10-17\",\"Statement\":[\"Effect\":\"Allow\",\"Principal\":\"*\",\"Action\":\"*\",\"Resource\":\"*\"]"
        RouteTableIds:
          - !Ref SheltersRouteTable
        ServiceName: !Join ['', ['com.amazonaws.', !Ref 'AWS::Region', '.s3']]

Here's a gist with my full source files and log.

发现问题的更新

似乎我到 S3 的 VPCEndpoint SheltersVPCS3EndpointdBMigration 之前被删除,这就是我收到 403 的原因。

对于纯 Cloudformation,我想这可以通过将 DependsOn 放在 dbMigration 上轻松解决,但对于无服务器,这似乎是不可能的。

【问题讨论】:

从您的源代码中我可以理解,当您收到“DELETE”请求类型时,您没有返回成功消息。遇到这种情况时需要显式发送成功消息,否则您的 CF 堆栈会卡住。 谢谢,但我正在使用 github.com/aws/aws-lambda-go/tree/master/cfn 包装函数并处理响应。在日志中,您可以看到已发送响应以及我返回的正文。 啊,我的错。尽管自定义资源因无法稳定而被卡住一个小时是指该问题。在这种情况下,请检查与自定义资源触发的 lambda 函数关联的 IAM 角色。由于我从未使用过无服务器框架并且使用过 SAM,因此它很难导航。希望有帮助! 哦。 IAM 角色可能值得进一步研究。您知道执行此操作需要哪些角色吗? 【参考方案1】:

您需要为您的 lambda 函数提供正确的权限。 如果你添加

provider:
  name: aws
  iamRoleStatements:
    $ref: ./iamRoleStatements.json

到您的无服务器配置

然后构造您的 iamRoleStatements.json 以授予您对放置在与无服务器配置相同目录中的 S3 存储桶的函数权限

[
  
    "Effect": "Allow",
    "Action": [
      "s3:*"
    ],
    "Resource": "*"
  
]

这确实使您的所有资源都有权对您的 s3 存储桶做任何他们想做的事情

如果您想了解有关使用 IAM i 无服务器功能的更多信息,请查看 https://serverless.com/blog/abcs-of-iam-permissions/

【讨论】:

谢谢。但这并没有给我不属于我的权限? Lambda fn 使用 CloudFormation 拥有的存储桶的预签名 URL 获取请求。它也没有解释为什么当 Lambda fn 收到 Create req 而不是 Delete req 时它会起作用?【参考方案2】:

经过与 AWS 支持的长期调查,我们发现 SheltersVPCS3EndpointdbMigration 被删除之前被删除,因此 Lambda fn 无法与 S3 存储桶取得任何联系,从而触发了超时。

由于无法将任何DependsOn 添加到 Serverless 中的函数,我不得不从 Serverless 迁移到 Cloudformation。当我添加以下内容时,它似乎解决了。

  DbMigrationLambdaFunction:
     DependsOn:
       - SheltersVPCS3Endpoint

【讨论】:

以上是关于为啥 AWS Lambda CFN S3 响应在删除事件时返回 403?的主要内容,如果未能解决你的问题,请参考以下文章

AWS 云形成;将模板分解为多个文件并使用 cfn-include 传入变量

从 Java AWS lambda API 响应中的字节数组返回图像

使用 Node.js 和 AWS Lambda 将 S3 文件的内容记录到 postgres 表

AWS Lambda S3 访问被拒绝

多个 AWS Lambda 中的 AWS::S3::Bucket LambdaConfiguration

AWS Lambda 函数写入 S3