Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I put object to amazon s3 using presigned url?

I am trying to use signed url to upload images to s3 bucket. Following is my bucket policy:

{     "Version": "2012-10-17",     "Statement": [         {             "Sid": "",             "Effect": "Allow",             "Principal": {                 "AWS": [                     "arn:aws:iam::12345678:user/myuser",                     "arn:aws:iam::12345678:root"                 ]             },         "Action": [                 "s3:List*",                 "s3:Put*",                 "s3:Get*"             ],             "Resource": [                 "arn:aws:s3:::myBucket",                 "arn:aws:s3:::myBucket/*"             ]         }     ] } 

I am generating the signed url from the server as follows:

var aws = require('aws-sdk'); aws.config = {     accessKeyId: myAccessKeyId,     secretAccessKey: mySecretAccessKey };  var s3 = new aws.s3(); s3.getSignedUrl('putObject', {     Bucket: 'myBucket',     Expires: 60*60,     key: 'myKey' }, function (err, url) {     console.log(url); }); 

I get the url. But when I try to put an object I get the following error:

<Error>     <Code>AccessDenied</Code>     <Message>Access Denied</Message>     <RequestId>FXXXXXXXXX</RequestId>     <HostId>fXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX</HostId> </Error> 

Update 1

Here is myuser's policy:

{   "Version": "2012-10-17",   "Statement": [     {         "Sid": "",         "Effect": "Allow",         "Principal": {             "AWS": [                 "arn:aws:iam::2xxxxxxxxxxx:user/myuser",                 "arn:aws:iam::2xxxxxxxxxxx:root"             ]         },         "Action": [             "s3:*"         ],         "Resource": [             "arn:aws:s3:::myBucket",             "arn:aws:s3:::myBucket/*"         ]     }   ] } 

Update 2 I can upload only when following option is set. I dont understand whats the use of bucket policy if only the manual selection of permission work.

Permission for everyone

Update 3

The following code works. Now the only problem is the signed url

 #!/bin/bash   file="$1"   bucket="mybucket"  resource="/${bucket}/${file}"  contentType="image/png"  dateValue=`date -R`  stringToSign="PUT\n\n${contentType}\n${dateValue}\n${resource}"  s3Key="AKxxxxxxxxxxxxxxxxx"  s3Secret="/Wuxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"  signature=`echo -en ${stringToSign} | openssl sha1 -hmac ${s3Secret}     -binary | base64`  curl -X PUT -T "${file}" \    -H "Host: ${bucket}.s3.amazonaws.com" \    -H "Date: ${dateValue}" \    -H "Content-Type: ${contentType}" \    -H "Authorization: AWS ${s3Key}:${signature}" \    https://${bucket}.s3.amazonaws.com/${file} 
like image 438
Pravin Avatar asked Mar 04 '16 06:03

Pravin


People also ask

How do you make an object in the Amazon S3 bucket viewable by using a URL?

Navigate to the AWS S3 console and click on your bucket's name. Use the search input to find the object if necessary. Click on the checkbox next to the object's name. Click on the Copy URL button.

How does Presigned URL work S3?

S3 pre-signed URLs are a form of an S3 URL that temporarily grants restricted access to a single S3 object to perform a single operation — either PUT or GET — for a predefined time limit. To break it down: It is secure — the URL is signed using an AWS access key.

Does S3 bucket need to be public for Presigned URL?

By default, all S3 objects are private. Only the object owner has permission to access them. However, the object owner can optionally share objects with others by creating a presigned URL, using their own security credentials, to grant time-limited permission to download the objects.


2 Answers

I managed to succesfully upload a file by using your code.

Here are the steps I followed:

  1. Created a new bucket and a new IAM user

  2. Set IAM user's policy as below:

    {     "Version": "2012-10-17",     "Statement": [         {             "Sid": "Stmt1418647210000",             "Effect": "Allow",             "Action": [                 "s3:Put*"             ],             "Resource": [                 "arn:aws:s3:::myBucket/*"             ]         }     ] } 
  3. Did NOT create a bucket policy

  4. Used your code to generate the pre-signed URL:

    var aws = require('aws-sdk'); aws.config = {     accessKeyId: myAccessKeyId,     secretAccessKey: mySecretAccessKey };  var s3 = new aws.s3(); s3.getSignedUrl('putObject', {     Bucket: 'myBucket',     Expires: 60*60,     Key: 'myKey' }, function (err, url) {     console.log(url); }); 
  5. Copied the URL on the screen and used curl to test the upload as below:

    curl.exe -k -X PUT -T "someFile" "https://myBucket.s3.amazonaws.com/myKey?AWSAccessKeyId=ACCESS_KEY_ID&Expires=1457632663&Signature=Dhgp40j84yfjBS5v5qSNE4Q6l6U%3D" 

In my case it generally took 5-10 seconds for the policy changes to take effect so if it fails the first time make sure to keep sending it for a while.

Hope this helps.

like image 149
Volkan Paksoy Avatar answered Oct 14 '22 11:10

Volkan Paksoy


It may help you too :) Add a ContentType property :

s3.getSignedUrl('putObject', {     Bucket: 'myBucket',     Expires: 60*60,     Key: 'myKey',     ContentType: 'image/jpeg', }, function (err, url) {    console.log(url); }); 
like image 37
Hugo Mallet Avatar answered Oct 14 '22 11:10

Hugo Mallet