Search code examples
amazon-web-servicesamazon-s3aws-lambdaserverless-frameworkaws-serverless

S3 upload works on sls offline but don’t work after sls deploy on real aws api url


s3 upload works on sls offline but don't work after sls deploy on real aws api url

after sls deploy and I get the endpoint url and try to upload image but it give access denied error on my catch block thing of s3.putObject but on sls offline local it work and in s3 bucket I can see files but after deploying it's not uploading and giving access denied.

Real Api After Deploy vs Localhost:

local vs aws url api - image

serverless.yml is bellow


org: alvee

app: barikhojo-backend

service: barikhojo-backend

frameworkVersion: '3'

provider:

  name: aws

  region: ap-southeast-1

  runtime: nodejs18.x

  stage: dev

  iam:

    role:

      statements:

      - Effect: Allow

        Action:

        - "s3:*"

        Resource: "arn:aws:s3:::${self:custom.imagesBucketName}" 

      - Effect: Allow

        Action: 

        - 'dynamodb:PutItem'    

        - 'dynamodb:Get*'    

        - 'dynamodb:Scan'    

        - 'dynamodb:Query'    

        - 'dynamodb:UpdateItem'    

        - 'dynamodb:DeleteItem'    

        Resource: "arn:aws:dynamodb:${aws:region}:${aws:accountId}:table/${self:custom.propertyTable}"

  environment: 

    BUCKET_NAME: ${self:custom.imagesBucketName}

    PROPERTY_TABLE: ${self:custom.propertyTable}

functions:

  api:

    handler: index.savePhoto

    events:

      - httpApi:

          path: /

          method: post

custom: 

  propertyTable: ${self:service}-property-table-${sls:stage}

  imagesBucketName: ${self:service}-s3-${sls:stage}

resources:

  Resources:

    # S3 BUCKET

    ImagesBucket: 

      Type: AWS::S3::Bucket

      Properties:

        BucketName: ${self:custom.imagesBucketName}

        # Granting public access to bucket

        PublicAccessBlockConfiguration:

          BlockPublicAcls: false

          BlockPublicPolicy: false

          IgnorePublicAcls: false

          RestrictPublicBuckets: false

    ImagesBucketAllowPublicReadPolicy:

      Type: AWS::S3::BucketPolicy

      Properties:

        Bucket: !Ref ImagesBucket

        PolicyDocument:

          Version: "2012-10-17"

          Statement: 

            - Sid: AllowPublicReadAccess

              Effect: Allow

              Action: 

                - "s3:GetObject"

              Resource: 

                - !Join ['/', [!GetAtt [ImagesBucket, Arn], '*']]

              Principal: "*"    

              Condition:

                Bool:

                  aws:SecureTransport: 'true'

    # DynamoDB 

    PropertyTable:

      Type: AWS::DynamoDB::Table

      Properties:

        TableName: ${self:custom.propertyTable}

        AttributeDefinitions:

          - AttributeName: ID

            AttributeType: S           

        KeySchema:

          - AttributeName: ID

            KeyType: HASH

        BillingMode: PAY_PER_REQUEST

plugins:

  - serverless-offline

  - serverless-plugin-common-excludes

lambda function code



const AWS = require('aws-sdk')

const parser = require("lambda-multipart-parser")

const { v4: uuidv4 } = require("uuid")

const s3 = new AWS.S3();

const BucketName = process.env.BUCKET_NAME;

async function saveFile(file){

  try {

    const savedFile = await s3.putObject({

      Bucket: BucketName,

      Key: `${file.filename}-${uuidv4()}`,

      Body: file.content

    }).promise()

    return 'done'

  } catch (error) {

    return error

  }

} 

module.exports.savePhoto = async (event) => {

  const {files} = await parser.parse(event)

  const status = await saveFile(files[0])

  return {

    statusCode: 200,

    body: JSON.stringify(

      {

        message: "Go Serverless v3.0! Your function executed successfully!",

        input: 'File Uploaded',

        bucketName: BucketName,

        status: status,

        filename: files[0].filename

      },

      null,

      2

    ),

  };

};

Solution

  • I believe your IAM statement that grants access is incomplete. It only grants access to the bucket resource, but not to individual objects within bucket. Please consult the docs here on how it should be defined: https://docs.aws.amazon.com/AmazonS3/latest/userguide/using-with-s3-actions.html

    tldr; You need to specify resource with /* at the end

    In works locally probably because the IAM checks are simply not checked there or you're credentials with appropriate permissions