Forrest
Forrest

Reputation: 17

Access denied when making api calls to s3 bucket with Node.js

Using Node.js, I'm making an api that makes calls to my s3 bucket on AWS. When I try to make use putObject method, i receive this error:

  message: 'Access Denied',
  code: 'AccessDenied',
  region: null,
  time: 2018-07-27T17:08:29.555Z,
  ... etc

} I have a config and credentials file in C:/User/{User}/.aws/ directory

config file:

[default]
region=us-east-2
output=json

credentials file:

[default]
aws_access_key_id=xxxxxxxxxxxxxxx
aws_secret_access_key=xxxxxxxxxxx

I created policies for both IAM user and Bucket. Here's my IAM user inline policy:

    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Allow",
                "Action": [
                    "s3:ListAllMyBuckets",
                    "s3:PutObject",
                    "s3:GetObject"
                ],
                "Resource": [
                    "arn:aws:s3:::*"
                ]
            }
        ]
    }

And my bucket policy:

{
    "Version": "2012-10-17",
    "Id": "Policy1488494182833",
    "Statement": [
        {
            "Sid": "Stmt1488493308547",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::134100338998:user/Test-User"
            },
            "Action": [
                "s3:ListBucket",
                "s3:ListBucketVersions",
                "s3:GetBucketLocation",
                "s3:Get*",
                "s3:Put*"
            ],
            "Resource": "arn:aws:s3:::admin-blog-assets"
        }
    ]
}

And finally, my api

var fs = require('fs'),
    AWS = require('aws-sdk'),
    s3 = new AWS.S3('admin-blog-assets');

...

  var params = {
  Bucket: 'admin-blog-assets',
  Key: file.filename,
  Body: fileData,
  ACL:'public-read'
  };
  s3.putObject(params, function (perr, pres) {
      if (perr) {
          console.log("Error uploading image: ", perr);                                                
      } else {
          console.log("uploading image successfully");                       
      }                      
  });

I've been banging my head on this for hours, can anyone help?

Upvotes: 1

Views: 4763

Answers (2)

Michael - sqlbot
Michael - sqlbot

Reputation: 179064

Some of the permissions you were granting were bucket permissions and others were object permissions. There are actions matching s3:Get* and s3:Put* that apply to both buckets and objects.

"Resource": "arn:aws:s3:::example-bucket" is only the bucket itself, not the objects inside it.

"Resource": "arn:aws:s3:::example-bucket/*" is only the objects in the bucket, and not the bucket itself.

You can write two policy statements, or you can combine the resources, like this:

"Resource": [ 
  "arn:aws:s3:::example-bucket",
  "arn:aws:s3:::example-bucket/*" 
]

Important Security Consideration: By using s3:Put* with both the bucket and object ARNs, your policy likely violates the principle of least privilege, because you have implicitly granted this user s3:PutBucketPolicy which allows these credentials to change the bucket policy. There may be other, similar concerns. You probably do not want to give these credentials that much control.


Credit to @PatNeedham for noticing a second issue that I overlooked, the AWS.S3() constructor expects an object as its first argument, not a string.

Upvotes: 0

Pat Needham
Pat Needham

Reputation: 5928

I believe the source of the problem is related to how you are defining the s3 object, as s3 = new AWS.S3('admin-blog-assets');

If you look at the example used here, it has this line:

var bucketPromise = new AWS.S3({apiVersion: '2006-03-01'}).createBucket({Bucket: bucketName}).promise();

Where the argument passed to AWS.S3 is an object containing that apiVersion field. But you are passing a string value.

The S3 specific documentation overview section has more information:

Sending a Request Using S3 var s3 = new AWS.S3(); s3.abortMultipartUpload(params, function (err, data) { if (err) console.log(err, err.stack); // an error occurred else
console.log(data); // successful response }); Locking the API Version In order to ensure that the S3 object uses this specific API, you can construct the object by passing the apiVersion option to the constructor:

var s3 = new AWS.S3({apiVersion: '2006-03-01'}); You can also set the

API version globally in AWS.config.apiVersions using the s3 service identifier:

AWS.config.apiVersions = { s3: '2006-03-01', // other service API versions };

var s3 = new AWS.S3();

Upvotes: 1

Related Questions