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.
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="PUTnn${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}
Advertisement
Answer
I managed to succesfully upload a file by using your code.
Here are the steps I followed:
Created a new bucket and a new IAM user
Set IAM user’s policy as below:
{ "Version": "2012-10-17", "Statement": [ { "Sid": "Stmt1418647210000", "Effect": "Allow", "Action": [ "s3:Put*" ], "Resource": [ "arn:aws:s3:::myBucket/*" ] } ] }Did NOT create a bucket policy
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', ContentType: 'image/jpeg', }, function (err, url) { console.log(url); });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.
Please note: if you get CORS errors, ensure you have provided a ContentType argument to the s3.getSignedUrl call. As Hugo Mallet discusses below, “When you upload, your browser will add the content-type to the request headers. [Therefore, if you do not provide the MediaType server-side, there will be] a difference between the executed request and the signature you obtained with getSignerUrl. Of course you have to set the correct content-type depending on the file you want to upload.”
