Cloudfront not able access S3 - amazon-web-services

I am trying to use Signed URL for images and once I was able to do it. But then, I deleted it and trying to do it once again and I am not able to do it.
Here is what I have done so far:
Created a Cloudfront Key Pair in IAM Management Console. Downloaded
the private and public key and also noted the Access Key ID =
XXXXXXXXXXXXXXXXXXXX.
Created a S3 bucket. No custom configuration
Created a Cloudfront distribution with the following settings
Origin domain name: my bucket
Restrict bucket access: Yes
Created a new Origin Access Identity
Grant read permissions on Bucket: Selected Yes
Restrict Viewer Access (Use Signed URLs or Signed Cookies) = Yes
Trusted Signers = Self
And then inside my laravel code, I have the following inside my controller:
$keyPairId = 'XXXXXXXXXXXXXXXXXXXX';
$privateKey = config_path('pk-XXXXXXXXXXXXXXXXXXXX.pem');
$url = "http://xxxxxxxxxxxxxx.cloudfront.net/image.jpg";
$cf = new UrlSigner($keyPairId, $privateKey);
$imgSrc = $cf->getSignedUrl($url, time());
echo "<img src='{$imgSrc}' />";
But every time I am getting the same error:
<Error>
<Code>MissingKey</Code>
<Message>Missing Key-Pair-Id query parameter or cookie value</Message>
</Error>
Need help.

You have to use CloudFront specific key pairs and add those to $cf. More information on how to download or upload your own public key:
http://docs.aws.amazon.com/AWSSecurityCredentials/1.0/AboutAWSCredentials.html#KeyPairs
http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-trusted-signers.html#private-content-creating-cloudfront-key-pairs

Related

Error in object URL of static website in S3 bucket: Requests specifying Server Side Encryption with AWS KMS managed keys require AWS Signature

I have created a static website in an S3 bucket AWS. I have created two files in bucket one in index.html and 2nd is error.html. When I open index.html and click on object URL in AWS it gives below error:
This XML file does not appear to have any style information associated with it. The document tree is shown below.
<Error>
<Code>InvalidArgument</Code>
<Message>Requests specifying Server Side Encryption with AWS KMS managed keys require AWS Signature Version 4.</Message>
<ArgumentName>Authorization</ArgumentName>
<ArgumentValue>null</ArgumentValue>
<RequestId>R69TKNDJTYZ8E0SW</RequestId>
<HostId>OAOZKRsA6ATOgH6jBr5jO1fS0zi+GSh4at34nLq8V/Ug8Icvuy8c6NOlCoNqqjpBcORg8bDlzJ0=</HostId>
</Error>
I have checked every possible solution but nothing works. My bucket has public access like below my bucket name it is written in red Publicly accessible. But still I could not find what is issue.

What bucket permissions are required to download zip file directly from URL?

I am following a tutorial here and if I take this s3 URL from the tutorial, https://s3.eu-central-1.amazonaws.com/deepset.ai-farm-qa/datasets/documents/wiki_gameofthrones_txt.zip, I am able to directly download the zip file to local.
When I subsistute my own zip file URL, I get an error that BadZipFile: File is not a zip file, and if I try my URL for zip file, I get permission denied instead of being able to download.
I also confirmed the zip files are formated correctly using terminal: unzip -t zipfile.zip
What permissions do I need to change in s3 or on the s3 object to allow download of zip file directly from URL?
Still very new to IAM s3 permissions and current permission are the standard ones when creating bucket.
Objects in Amazon S3 are private by default. This means that they cannot be accessed by an anonymous URL (like you have shown).
If you want a specific object to be publicly available (meaning that anyone with the URL can access it), then use the Make Public option in the S3 management console. This can also be configured at the time that the object is uploaded by specifying ACL=public-read.
If you want a whole bucket, or particular paths within a bucket, to be public, then you can create a Bucket Policy that grants access to the bucket. This requires S3 Block Public Access to be disabled.
You can also generate n Amazon S3 pre-signed URL, which provides time-limited access to a private object. The pre-signed URL has additional information added that grants permission to access the private object. This is how web applications provide access to private objects to authorized users, such as photo websites.
If an object is accessed via an AWS API call or the AWS Command-Line Interface (CLI), then AWS credentials are used to identify the user. If the user has permission to access the object, then they can download it. This method uses an API call rather than a URL.
Two solutions:
Make your bucket/file public. Check this ( Not recommended)
{
"Version":"2012-10-17",
"Statement":[
{
"Sid":"PublicRead",
"Effect":"Allow",
"Principal": "*",
"Action":["s3:GetObject","s3:GetObjectVersion"],
"Resource":["arn:aws:s3:::DOC-EXAMPLE-BUCKET/*"]
}
]
}
Use pre-signed URLs with SDK .. check this
var params = {Bucket: bucketname , Key: keyfile , Expires: 3600 , ResponseContentDisposition : `attachment; filename="filename.ext"` };
var url = s3.getSignedUrl('getObject', params);

AWS Get Pre-Signed URL with custom domain

Following is what I'm doing. I'm generating a pre-signed URL using a custom domain for my s3 bucket resources which are not public.
https://files.customdomain.com/file123?AWSAccessKeyId=XXX&Expires=1541220685&Signature=XXXX
Also to add the certificate I've created a cloudfront distribution for the bucket having following origin settings
Origin Domain Name: bucket-name.s3.amazonaws.com
Origin Id : s3.bucket-name
Restrict Bucket Access: No
Yet I'm unable to access my resources. Throws access denied error. Any help would be appreciated.
There are two cases:
If your bucket has regular name.
In this case you should use CloudFront to access your bucket.
And like mentioned above URL looks like in this answer:
https://cloudfront-url/file123?AWSAccessKeyId=XXX&Expires=1541220685&Signature=XXXX
If your bucket has s3 static website name.
In this case your bucket name looks like files.customdomain.com and you can generate pre-signed url for this bucket:
https://files.customdomain.com/file123?AWSAccessKeyId=XXX&Expires=1541220685&Signature=XXXX
In your DNS you will have CNAME files.customdomain.com points to files.customdomain.com.s3.[bucket-region].amazonaws.com.
NOTICE
When I generate pre-signed URL via aws-cli:
aws s3 presign s3://files.customdomain.com/file123 --endpoint-url https://files.customdomain.com
I get URL with duplicate bucket name in the path:
https://files.customdomain.com/files.customdomain.com/file123?AWSAccessKeyId=XXX&Expires=1541220685&Signature=XXXX
instead of:
https://files.customdomain.com/file123?AWSAccessKeyId=XXX&Expires=1541220685&Signature=XXXX
I don't know if it has the same behavior via SDK.
Have you tried initializing S3 with the custom url var S3 = new AWS.S3({endpoint: 'media.domain.com', s3BucketEndpoint: true});
More info https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html
Also, make sure signature is correct as well https://docs.aws.amazon.com/AmazonS3/latest/dev/UsingAWSSDK.html#specify-signature-version
Ref : https://github.com/aws/aws-sdk-js/issues/891
When using S3 with CloudFront, you don't want an S3 signed URL... you want a CloudFront signed URL.
Read Configuring Security and Limiting Access to Content in the CloudFront developer guide.
I found a solution for this question. The signed url needs to be generated for cloudfront url endpoint from s3 bucket. Therefore instead of
https://files.customdomain.com/file123?AWSAccessKeyId=XXX&Expires=1541220685&Signature=XXXX
it needs to be
https://cloudfront-url/file123?AWSAccessKeyId=XXX&Expires=1541220685&Signature=XXXX
and DNS records had to resolve custom domain to cloudfront url.

Amazon S3 - Access to Private Bucket

I have multiple images within a private S3 bucket and I would like an instance of Tableau to be able to access those images. Is there a URL or some way to access those images while still keeping the S3 bucket private?
Access Private Bucket through Tableau
You can setup a IAM user with access permission to S3 and allow Tableau access.
Check the article on Connect to your S3 data with the Amazon Athena connector in Tableau 10.3 for more details.
Note: You need to configure Amazon Athena for Querying the S3 content.
Custom Generated S3 Urls to Access Private Bucket
Yes. You can generate a Signed URL from your backend using AWS SDK. This can be done directly using S3 or through AWS CloudFront.
Using S3 Signed Urls. e.g, Signed Url for GET Object.
var params = {Bucket: 'bucket', Key: 'key'};
var url = s3.getSignedUrl('getObject', params);
console.log('The URL is', url);
Using CloudFront Signed Urls. e.g, Signed Url for GET in CloudFront.
var cfsign = require('aws-cloudfront-sign');
var signingParams = {
keypairId: process.env.PUBLIC_KEY,
privateKeyString: process.env.PRIVATE_KEY,
// Optional - this can be used as an alternative to privateKeyString
privateKeyPath: '/path/to/private/key',
expireTime: 1426625464599
}
// Generating a signed URL
var signedUrl = cfsign.getSignedUrl(
'http://example.cloudfront.net/path/to/s3/object',
signingParams
);
Note: Generating the Url needs to be done in your backend. You can setup a serverless solution for this by using AWS API Gateway and Lambda to provide an endpoint for authenticated users to access.
In addition you can also use AWS Cognito UserPools with Identity Pool to get direct access to S3 Private Content without the above steps. For this you need to use the Cognito UserPools or a federated identity as the identity provider which is connected with Cognito Identity Pools.

AWS CloudFront with Signed URL: 403 Access Denied

I'm configuring an environment with a Amazon S3 Bucket for storage of media files and Amazon CloudFront for restricted distribution purposes.
The access to those media files needs to be private and should be done via a signed URL. So I created the S3 Bucket on South America (São Paulo) region and uploaded some test files. Then I created a CloudFront Distribution with that previous bucket as Origin and it's Bucket Access is restricted. I created a new OAI (Origin Access Identity) and also selected the option Yes, Update Bucket Policy so that it auto-configures the S3 Bucket Policies.
I'm only using the default Behavior and it's configured with HTTP and HTTPS viewer protocol policy and GET, HEAD allowed methods. Restrict Viewer Access (Use Signed URLs or Signed Cookies) is set and the Trusted Signer is set to Self.
Here's some images to clarify the setup:
S3 Bucket Policy
Distribution's Origin
Distribution's Behavior
I'm getting a HTTP 403 while trying to access the signed URL generated with either awscli or cfsign.pl
<Error>
<Code>AccessDenied</Code>
<Message>Access denied</Message>
</Error>
Is there something missing that I don't know? It looks like I made everything the docs said to do.
I received the same Access Denied error and spent the last couple hours trying to figure out what was going on. I finally realized that the Expires parameter was set in the past since I was using my local time instead of UTC. Make sure to set the Expires in the future according to UTC.
In my case the problem was with URL I was passing to URL signing code (I was using AWS SDK for Node.js).
cloudFront.getSignedUrl({
url: `${distributionUrl}/${encodeURI(key)}`,
expires: Math.floor(new Date().getTime() / 1000) + 60 * 60
})
Note encodeURI. I was not doing that. The resulting signed URL would still have URI components encoded, BUT would have invalid signature, thus causing 403 error.
EDIT: ...And you have to wrap it into url.format() like this:
cloudFront.getSignedUrl({
url: url.format(`${distributionUrl}/${encodeURI(key)}`),
expires: Math.floor(new Date().getTime() / 1000) + 60 * 60
})
I guess they should be doing that in SDK.
After recreating both the Amazon S3 Bucket and Amazon CloudFront Distribution I was still experiencing the issue. After a session with my rubber duck I found out that the Private Key file that I was using belongs to a deleted CloudFront Key-pair.
Now that I'm using the correct key to encrypt things everything is working fine. That doesn't explain why the first bucket and distribution weren't working because in that specific case I was using the same set of configurations and the right Private Key file.
I also encountered the same issue. Probably, we have to re-generate Clouf Front key-pair.