AWS S3 GET returns status code 403 - node.js

I'm using node aws-sdk with a user who has been set up with the following policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Stmt1458935963000",
"Effect": "Allow",
"Action": [
"s3:DeleteObject",
"s3:GetObject",
"s3:PutObject"
],
"Resource": [
"arn:aws:s3:::mybucket1/*"
]
}
]
}
The bucket itself does not have any explict policy attached to it. Permissions are set as follows:
The following produces 403:
<video src="https://s3-us-west-2.amazonaws.com/mybucket1/default21.mp4">
</video>
Bucket name has been changed, but it does exist on S3 along with the video. Any help will be much appreciated.
UPDATE 1
Getting the same result even if aws-sdk is booted with the root/owner of S3 account.

your <video> tag is not using the authentication mechanism that your nodejs code has setup.
the video tag gets loaded in a browser, and the browser knows nothing about he AWS SDK or your node server.
you need to use a pre-signed URL for the video tag.
generate the URL on the server, and then use that url in the video tag. for example, if you're using Express:
router.get("/whatever", function(req, res, next){
var params = {Bucket: 'mybucket', Key: 'default21.mp4'};
var url = s3.getSignedUrl('getObject', params);
res.render("some/view", {
videoUrl: url
});
});

Related

Why does AWS cloudfront return Access denied even though the s3 bucket is public and s3 url is working fine

I have an s3 bucket where I store some images these Images are publicly accessible. I have check the image URLs and here are the photos confirming that my bucket is publicly accessible
Now I would like to use cloudFront to serve this image.
I created a cloudFront with the following settings as shown in the picture.
After configuring the Cloudfront distribtion, I ensured the my Origin access identiy is reflected in my bucket policy here is my bucket policy
{
"Version": "2008-10-17",
"Id": "PolicyForCloudFrontPrivateContent",
"Statement": [
{
"Sid": "1",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity XXXXXXXXXX"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::BucketName/*"
}
]
}
I create a signedUrl using the following code in nodejs
const signer = new AWS.CloudFront.Signer(cloudFrontAccessKeyId, cloudFrontPrivateKeyId);
console.log(cloudFrontAccessKeyId,cloudFrontPrivateKeyId," PRIVATE ")
const linkExpiration = 3 * 24 * 60 * 60 * 100;
function returnCFsignedUrl(req) {
return new Promise(function(resolve,reject){
signer.getSignedUrl({
url: `https://cloudfrontDomain/s3Key}`,
expires: linkExpiration
},(err,url)=>{
if (err){
reject(err);
}else{
console.log(url," URL ")
resolve(url)
}
})
})
}
**I get a signed URL from this code which when clicked return access denied
I tried to add Access-Control-Request-Headers Access-Control-Request-Method Origin as suggested by this article https://aws.amazon.com/premiumsupport/knowledge-center/no-access-control-allow-origin-error/ but nothing seems to work**

s3 check if file exist by getting metadata

I am trying to check if file exists in s3 bucket using AWS javascript sdk.
I have defined my policy to Allow HeadBucket for my s3 bucket.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": "s3:HeadBucket",
"Resource": "*"
}
]
}
I have attached the above policy to a user and I am using that user in setting up the config for the s3 as follows:
aws-config.json
{
"user1": {
"bucket": "my-bucket",
"region": "us-east-2",
"accessKey": "********",
"secretKey": "*********"
}
}
In my node.js code, I am trying to use headObject to get the meta data for the object as follows:
var AWS = require('aws-sdk');
var s3Config = require("../data/aws-config.json").user1;
AWS.config.update(s3Config);
var s3 = new AWS.S3;
var params = {
Bucket: "my-bucket",
Key: "mykey.PNG"
};
s3.headObject(params, function (err, metadata) {
console.log(err);
});
This is giving me 403 Forbidden error. I have tried everything from changing AWS policy to allow all s3 operations to allow access to all resources, nothing seems to work.
EDIT:
I checked the AWS.config.credentials and it is loading some random accessKey and secretKey and not from my config file. I am not sure why this is happening.
You are trying to HEAD object. There's no HEAD bucket operation which is what your IAM policy grants.
To do HEAD operation on an object, you need s3:GetObject permission.
See docs for more information.

How to get url from s3.getSignedUrl()

I'm trying to store some images using AWS S3. Everything is running smoothly until I started getting some 400s on PUTting images on URLs I got from s3.getSignedUrl. At that time my code looked like this:
const s3 = new AWS.S3({
accessKeyId,
secretAccessKey
});
const imageRouter = express.Router();
imageRouter.post('/upload', (req, res) => {
const type = req.body.ContentType;
const Key = `${req.session.user.id}/${uuid()}.${type}`;
s3.getSignedUrl(
'putObject',
{
Bucket: 'cms-bucket-06',
ContentType: type,
Key
},
(err, url) => {
console.log('URL ', url); res.send({ Key, url });
}
);
});
I followed link from error and I found out that "The authorization mechanism you have provided is not supported. Please use AWS4-HMAC-SHA256.".
So I did. Like that:
const s3 = new AWS.S3({signatureVersion: 'v4'});
But now I get no URL in my callback function. It's undefined. What am I still missing here?
EDIT:
Alright, I added my key back to the constructor and I'm able to upload images. New problem is that I can't open them. I get access denied every time. I added proper bucket policy but it doesn't help :(
{
"Version": "2012-10-17",
"Id": "Policy1547050603038",
"Statement": [
{
"Sid": "Stmt1547050601490",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::bucket-name/*"
}
]
}

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?
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();
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.

Receiving Access Denied when trying to access Amazon AWS S3 service with nodejs

var AWS = require('aws-sdk');
var S3FS = require('s3fs');
AWS.config.update({region: 'us-east-1'});
var options = {};
exports.storeMedia = function(req, res){
var fsImpl = new S3FS('test-bucket', options);
fsImpl.writeFile('message.txt', 'Hello Node', function (err) {
if (err) throw err;
console.log('It\'s saved!');
});
}
My code is just a sample code I am trying to test out of the doc samples. I have saved the aws key id and secret in the credentials file under .aws on my macbook. I have already built a user and assigned it access policies such that I am able to get and put objects through the assigned user on AWS Console. What else should I look at? Obviously, I am missing something basic just need a little help over the hump.
I researched a couple of additional things. One, I looked at the module page from https://www.npmjs.com/package/s3fs which lists the minimum privileges that s3fs requires.
Secondly, I tried to use the base aws-sdk. I was surprised to see that I could do a ListBucket without any issues. Then, I tried to put an object in an existing bucket, and that worked without issues. I am able to make progress with that, but I need to understand more about which privilege I may not have given so I clearly understand which privilege is missing.
my policy for the bucket is as follows:
{
"Version": "2012-10-17",
"Id": "Policy1437933054275",
"Statement": [
{
"Sid": "Stmt1437932987273",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::meetsites-images/*",
"arn:aws:s3:::meetsites-images"
]
}
]
}
and the policy for the user is
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "s3:*",
"Resource": "*"
}
]
}
I was also able to find a good blog on this topic, hard to find so better to mention [a link] (http://blogs.aws.amazon.com/security/post/TxPOJBY6FE360K/IAM-policies-and-Bucket-Policies-and-ACLs-Oh-My-Controlling-Access-to-S3-Resourc)

Resources