Getting "Missing Authentication Token" behind CloudFront distribution - amazon-web-services

I have an API Gateway as a custom origin on a CloudFront distribution hosted on a custom domain. The API works great when I call it from amazonaws.com url but from my custom domain I get "Missing Authentication Token". I assume I have the CloudFront distribution configured wrong somehow?
Dashboard:
Type: 'AWS::CloudFront::Distribution'
Properties:
DistributionConfig:
Aliases:
- promptdash.com
CacheBehaviors:
- AllowedMethods:
- DELETE
- GET
- HEAD
- OPTIONS
- PATCH
- POST
- PUT
ForwardedValues:
QueryString: false
Headers: []
PathPattern: api/*
TargetOriginId: rest-api-origin
ViewerProtocolPolicy: https-only
DefaultCacheBehavior:
Compress: true
ForwardedValues:
QueryString: false
Headers: []
Cookies:
Forward: none
TargetOriginId: static-site-origin
ViewerProtocolPolicy: allow-all
DefaultRootObject: index.html
Enabled: true
Origins:
- DomainName: ddg-prompt.s3.amazonaws.com
Id: static-site-origin
S3OriginConfig:
OriginAccessIdentity: !Sub origin-access-identity/cloudfront/${AccessIdentity}
- DomainName: !Sub ${Api}.execute-api.${AWS::Region}.amazonaws.com
Id: rest-api-origin
CustomOriginConfig:
OriginProtocolPolicy: https-only
OriginPath: /prod
PriceClass: PriceClass_100
ViewerCertificate:
AcmCertificateArn: arn:aws:acm:us-east-1:(my AccountId):certificate/(the certificate)
SslSupportMethod: sni-only

Related

Cloudfront differentiate origin based on hostname of the request

I have a Cloudfront distribution that I want to use it to serve two different hostnames (let's say 1.hostname.com and 2.hostname.com). In my CloudFormation template, I include these as aliases like below.
AppDistribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Origins:
- DomainName: !Ref ALBDomainName1
Id: ALBOrigin1
CustomOriginConfig:
OriginProtocolPolicy: https-only
OriginSSLProtocols:
- TLSv1.2
- DomainName: !Ref ALBDomainName2
Id: ALBOrigin2
CustomOriginConfig:
OriginProtocolPolicy: https-only
OriginSSLProtocols:
- TLSv1.2
Aliases:
- "1.hostname.com"
- "2.hostname.com"
DefaultCacheBehavior:
AllowedMethods:
- GET
- HEAD
- OPTIONS
- PUT
- PATCH
- POST
- DELETE
TargetOriginId: ALBOrigin1
ForwardedValues:
QueryString: False
Cookies:
Forward: none
ViewerProtocolPolicy: redirect-to-https
MinTTL: 0
DefaultTTL: 60
MaxTTL: 60
ResponseHeadersPolicyId: "123123"
I want to configure the cloudformation to use one origin if the request is coming to 1.hostname.com and the other origin if the request is coming to 2.hostname.com. The path pattern can be the same in both cases. Is there a way to add CacheBehaviors achieve this?

How to get the CloudFront distribution ARN in a CloudFormation stack for WebACLAssociation?

I've setup a CloudFront distribution in CloudFormation and I'm building an AWS WAF ACL to act as a firewall for it. To associate the ACL to the CloudFront distribution, I've added a AWS::WAFv2::WebACLAssociation entry which requires the ARN of the CloudFront distribution for the ResourceArn entry. However, I can't seem to find out how to get the CloudFront distribution ARN from the official documentation. I thought I could use !Ref however it used the CloudFront ID as per the documentation instead of the ARN.
How do I reference the CloudFront distribution ARN from the WebACLAssociation entry?
Example below (other resources omitted for brevity):
---
AWSTemplateFormatVersion: 2010-09-09
Description: CloudFront
Parameters:
# ...
CloudFront:
Type: AWS::CloudFront::Distribution
DependsOn:
- IssuedCertificate
- S3Bucket
Properties:
DistributionConfig:
Origins:
- DomainName: !Sub
- ${S3Bucket}.${S3WebEndpoint}
- {
S3Bucket: !Ref S3Bucket,
S3WebEndpoint:
!FindInMap [RegionMap, !Ref "AWS::Region", websiteendpoint],
}
Id: S3origin
CustomOriginConfig:
OriginProtocolPolicy: http-only
Enabled: "true"
Comment: !Sub Distribution for ${DomainName}
HttpVersion: http2
Aliases:
- !Ref DomainName
DefaultCacheBehavior:
AllowedMethods:
- GET
- HEAD
- OPTIONS
TargetOriginId: S3origin
Compress: True
DefaultTTL: 604800
ForwardedValues:
QueryString: "false"
Cookies:
Forward: none
ViewerProtocolPolicy: redirect-to-https
PriceClass: PriceClass_100
ViewerCertificate:
AcmCertificateArn: !Ref Certificate
SslSupportMethod: sni-only
# ...
AWSWAF:
Type: AWS::WAFv2::WebACL
Properties:
Name: allowlist
Description: Allowlist
Scope: CLOUDFRONT
DefaultAction:
Block: {}
Rules:
- Name: ipset-rule
Priority: 0
Action:
Allow: {}
Statement:
IPSetReferenceStatement:
Arn: # <ARN>
VisibilityConfig:
SampledRequestsEnabled: true
CloudWatchMetricsEnabled: true
MetricName: ipset-metrics
VisibilityConfig:
SampledRequestsEnabled: true
CloudWatchMetricsEnabled: true
MetricName: allowlist-metrics
AWSWAFAssociation:
Type: AWS::WAFv2::WebACLAssociation
Properties:
ResourceArn: !Ref CloudFront
WebACLArn: !Ref AWSWAF
There is no direct Attribute for the same but you can construct it:
arn:aws:cloudfront::${AWS::AccountId}:distribution/${CloudFront}
Turns out I had been approaching the problem wrong all along. Diving into the docs, I found that AWS details how to deploy an ACL for a CloudFront distribution here under the ResouceArn entry.
To fix this issue, all I had to do was add the following to the CloudFront distribution DistributionConfig and remove the WebACLAssociation entry:
WebACLId: !GetAtt AWSWAF.Arn
So the final CloudFront entry looked like this:
CloudFront:
Type: AWS::CloudFront::Distribution
DependsOn:
- IssuedCertificate
- S3Bucket
Properties:
DistributionConfig:
Origins:
- DomainName: !Sub
- ${S3Bucket}.${S3WebEndpoint}
- {
S3Bucket: !Ref S3Bucket,
S3WebEndpoint:
!FindInMap [RegionMap, !Ref "AWS::Region", websiteendpoint],
}
Id: S3origin
CustomOriginConfig:
OriginProtocolPolicy: http-only
Enabled: "true"
Comment: !Sub Distribution for ${DomainName}
HttpVersion: http2
Aliases:
- !Ref DomainName
DefaultCacheBehavior:
AllowedMethods:
- GET
- HEAD
- OPTIONS
TargetOriginId: S3origin
Compress: True
DefaultTTL: 604800
ForwardedValues:
QueryString: "false"
Cookies:
Forward: none
ViewerProtocolPolicy: redirect-to-https
PriceClass: PriceClass_100
ViewerCertificate:
AcmCertificateArn: !Ref Certificate
SslSupportMethod: sni-only
WebACLId: !GetAtt AWSWAF.Arn

Cloudfront distribution directs to S3 instead of API Gateway, producing 404 Error NoSuchKey

I have created an API Gateway that works. If I trigger the endpoint manually by https://example-api.amazonaws.com/Prod/some/endpoint the API gives me the desired result. Also I have set up a cloudfront behavior to route to this endpoint whenever the path contains /api/*.
If I now visit the endpoint through the cloudfront distribution: https://xxxxxxx.cloudfront.net/some/endpoint it gives me a 404 error. Cloudfront correctly appends the /prod to the path which tells me that something is working at least.
More specifically it gives me:
404 Not Found
Code: NoSuchKey
Message: The specified key does not exist.
An Error Occurred While Attempting to Retrieve a Custom Error Document
I can see the cloudfront distribution working since it redirects correctly. I have been struggling with this for a while now. Is this error code to ambiguous to be helpful?
After doing some more digging it seems as if the cloudfront distribution is looking in my s3 for an object with the key. So for some reason the PathPattern: /api/v1/* is not redirecting to the APIOrigin.
I have attached my Cloudformation template below:
Resources:
WebsiteCloudfront:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Comment: Cloudfront Distribution pointing to S3 bucket and API Gateway.
Origins:
- Id: S3Origin
DomainName: bucket-for-static-files.amazonaws.com
CustomOriginConfig:
HTTPPort: 80
HTTPSPort: 443
OriginProtocolPolicy: http-only
- Id: APIOrigin
DomainName: example.api.endpoint.amazonaws.com
OriginPath: /prod
CustomOriginConfig:
# HTTPPort: 80
HTTPSPort: 443
OriginProtocolPolicy: https-only
OriginSSLProtocols:
- TLSv1.2
Enabled: true
HttpVersion: 'http2'
DefaultRootObject: index.html
Aliases:
- !Ref DomainName
DefaultCacheBehavior:
AllowedMethods:
- DELETE
- GET
- HEAD
- OPTIONS
- PATCH
- POST
- PUT
Compress: true
# Logging: Logging
TargetOriginId: S3Origin
ForwardedValues:
QueryString: true
Cookies:
Forward: none
ViewerProtocolPolicy: redirect-to-https
CacheBehaviors:
- TargetOriginId: APIOrigin
ViewerProtocolPolicy: https-only
PathPattern: /api/v1/*
ForwardedValues:
QueryString: true
Headers:
- Host
AllowedMethods:
- DELETE
- GET
- HEAD
- OPTIONS
- PATCH
- POST
- PUT
PriceClass: PriceClass_All
ViewerCertificate:
AcmCertificateArn: arn:aws:acm:SOME_HARD_CODED_ARN
SslSupportMethod: sni-only
MinimumProtocolVersion: TLSv1.2_2019
However, the strange part remains where the cloudfront distribution appends the /prod. Making it seem as if the pathPattern correctly routes to API Gateway.
Edit: Currently thinking I might be missing Headers for the API Gateway.

Configure Cloudformation to have requests to GET /subdirectory serve /subdirectory/index.html using Cloudfront & S3

I have a Cloudformation template that sets up a AWS::CloudFront::Distribution & AWS::S3::Bucket. Unfortunately, requests to GET /subdirectory respond with a 403. How can I configure the Cloudformation template to have GET /subdirectory serve /subdirectory/index.html?
My Cloudfront configuration looks like:
CloudFrontDistribution:
Type: 'AWS::CloudFront::Distribution'
Properties:
DistributionConfig:
Aliases:
- !FindInMap [Domain, !Ref Stage, Domain]
ViewerCertificate:
AcmCertificateArn: !Ref Cert
SslSupportMethod: sni-only
CustomErrorResponses:
- ErrorCode: 403 # not found
ResponseCode: 404
ResponsePagePath: !Ref ErrorPagePath
DefaultCacheBehavior:
AllowedMethods:
- GET
- HEAD
- OPTIONS
CachedMethods:
- GET
- HEAD
- OPTIONS
Compress: true
DefaultTTL: 3600 # in seconds
ForwardedValues:
Cookies:
Forward: none
QueryString: false
MaxTTL: 86400 # in seconds
MinTTL: 60 # in seconds
TargetOriginId: s3origin
ViewerProtocolPolicy: redirect-to-https
DefaultRootObject: !Ref DefaultRootObject
Enabled: true
HttpVersion: http2
Origins:
- DomainName: !GetAtt 'S3Bucket.DomainName'
Id: s3origin
S3OriginConfig:
OriginAccessIdentity: !Sub 'origin-access-identity/cloudfront/${CloudFrontOriginAccessIdentity}'
PriceClass: 'PriceClass_All'
Everything works except requests to GET /subdirectory.
I also tried:
- DomainName: !GetAtt 'S3Bucket.RegionalDomainName'
Id: s3origin
S3OriginConfig:
OriginProtocolPolicy: http-only
However I received the error Property TemplateURL cannot be empty. on the AWS::CloudFormation::Stack.
Please check this docs, you can find an example like:
'use strict';
const querystring = require('querystring');
exports.handler = (event, context, callback) => {
const request = event.Records[0].cf.request;
/**
* Reads query string to check if S3 origin should be used, and
* if true, sets S3 origin properties.
*/
const params = querystring.parse(request.querystring);
if (params['useS3Origin']) {
if (params['useS3Origin'] === 'true') {
const s3DomainName = 'my-bucket.s3.amazonaws.com';
/* Set S3 origin fields */
request.origin = {
s3: {
domainName: s3DomainName,
region: '',
authMethod: 'none',
path: '',
customHeaders: {}
}
};
request.headers['host'] = [{ key: 'host', value: s3DomainName}];
}
}
callback(null, request);
};
You could change this to point correct path using the query string from the request.
Check this to set up CloudFormation, there is an example on how to set up the trigger
Resources:
CFDistribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Enabled: 'true'
Comment: !Sub '${Stage} - CI/CD for Lambda#Edge'
Aliases:
- !FindInMap [AliasMap, !Ref Stage, Alias]
Origins:
-
Id: MyOrigin
DomainName: aws.amazon.com
CustomOriginConfig:
HTTPPort: 80
OriginProtocolPolicy: match-viewer
DefaultCacheBehavior:
TargetOriginId: MyOrigin
LambdaFunctionAssociations:
-
EventType: origin-request
LambdaFunctionARN: !Ref LambdaEdgeFunctionSample.Version

Cloudfront does not redirect properly to https and subdomain

According to this post and this article, one should create a second cloudfront distribution in front of an S3 bucket which redirects to another bucket, which contains the static files.
Asuuming the frontend bucket name is www.example.com and the redirect bucket is example.com. I have set up my AWS resources according to the following cloudformation template.
However, some problems occur: If I hit example.com it does not redirect to www.example.com, only when I have clicked another link on the site. In addition, it does not load the favicon for example. http://example.com is redirect to https://example.com. http://www.example.com it does not redirect to https, the webiste is not found.
What am I missing in my AWS settings?
This mentions to not set the Default Root Object property, which I did not. But maybe it is related somehow?
FrontendBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: ${self:custom.frontendBucketName}
AccessControl: PublicRead
WebsiteConfiguration:
IndexDocument: index.html
ErrorDocument: 404.html
RedirectdBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: ${self:custom.redirectBucketName}
AccessControl: PublicRead
WebsiteConfiguration:
RedirectAllRequestsTo:
HostName: ${self:custom.frontendBucketName}
Protocol: https
WebAppCloudFrontDistribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Origins:
- DomainName: ${self:custom.frontendBucketName}.s3-website.${self:provider.region}.amazonaws.com
Id: Frontend
CustomOriginConfig:
HTTPPort: 80
HTTPSPort: 443
OriginProtocolPolicy: http-only
Enabled: 'true'
Aliases:
- ${self:custom.frontendBucketName}
CustomErrorResponses:
- ErrorCode: 404
ResponseCode: 200
ResponsePagePath: /index.html
DefaultCacheBehavior:
DefaultTTL: 31536000
MaxTTL: 31536000
MinTTL: 31536000
AllowedMethods:
- DELETE
- GET
- HEAD
- OPTIONS
- PATCH
- POST
- PUT
TargetOriginId: Frontend
ForwardedValues:
QueryString: 'false'
Cookies:
Forward: none
ViewerProtocolPolicy: redirect-to-https
AcmCertificateArn: 'arn:aws:acm:us-east-1:xxxx:certificate/xxxx'
SslSupportMethod: 'sni-only'
RedirectCloudFrontDistribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Origins:
- DomainName: ${self:custom.redirectBucketName}.s3-website.${self:provider.region}.amazonaws.com
Id: Redirect
CustomOriginConfig:
HTTPPort: 80
HTTPSPort: 443
OriginProtocolPolicy: http-only
Enabled: 'true'
Aliases:
- {self:custom.redirectBucketName}
DefaultCacheBehavior:
DefaultTTL: 31536000
MaxTTL: 31536000
MinTTL: 31536000
AllowedMethods:
- DELETE
- GET
- HEAD
- OPTIONS
- PATCH
- POST
- PUT
TargetOriginId: Redirect
ForwardedValues:
QueryString: 'false'
Cookies:
Forward: none
ViewerProtocolPolicy: redirect-to-https
ViewerCertificate:
AcmCertificateArn: 'arn:aws:acm:us-east-1:xxxx:certificate/xxxx'
SslSupportMethod: 'sni-only'
DnsRecord:
Type: "AWS::Route53::RecordSet"
Properties:
AliasTarget:
DNSName:
Fn::GetAtt:
- WebAppCloudFrontDistribution
- DomainName
HostedZoneId: XXXXX
HostedZoneId: XXXX
Name: ${self:custom.frontendBucketName}
Type: 'A'
RedirectDnsRecord:
Type: "AWS::Route53::RecordSet"
Properties:
AliasTarget:
DNSName:
Fn::GetAtt:
- RedirectCloudFrontDistribution
- DomainName
HostedZoneId: XXXX
HostedZoneId: XXXX
Name: ${self:custom.redirectBucketName}
Type: 'A'
Currently, you have a DNS problem, you haven't published DNS record for "www.example.com".
You have DNS for example.com --> CloudFront but not for www.example.com , even though you have added www.example.com to a CloudFront CNMAE/Alternative filed, when client gets a 301/302 with a new location and see the change in host, it resolves it again.
curl -I --resolve www.example.com:443:13.249.210.66 https://www.example.com/index.html
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 80232
Connection: keep-alive
Date: Sun, 23 Jun 2019 10:07:43 GMT
Last-Modified: Fri, 21 Jun 2019 07:21:38 GMT
ETag: "8d536768b2173a7f3869f4178f75b331"
Server: AmazonS3
X-Cache: Miss from cloudfront
Via: 1.1 7db8064d915149cac923df11147875f9.cloudfront.net (CloudFront)
X-Amz-Cf-Pop: BLR50-C3
X-Amz-Cf-Id: wOpVoz1gVlxYDBORSfGr9fmt8L-Q6vhWyEm1XPgJVQy-sbes9HTuuQ==
You just need to create a alias record in Route53 for www.example.com as well.