the concept is simple.
Create a Cname switch that points to blue / green deployment channels via AWS API Gateway. The API has two stages for blue and green mapped back into an environment variable which in turns maps back into the Lambda alias associated with it's own dedicated version. Therefore there are now two separate channels for conducting deployments into. All of this works fine.
The issue arises when a Cname is created in Route53 to point to either of the API Gateways blue or green custom domains. The SSL cert is held in AWS Certificate Manager.
When we call the blue endpoint via the Cname we get an SSL error
curl -Il -H "Host:blue-api.example.com" -H "x-api-key:xxxxxxxxx"
-X GET https://cname-api.example.com/questions/health
curl: (35) SSL peer handshake failed, the server most likely requires a client certificate to connect
Whereas when we call the custom domain directly it works
curl -Il -H "Host:blue-api.example.com" -H "x-api-key:xxxxxxxxx"
-X GET https://blue-api.example.com/questions/health
HTTP/1.1 200 OK
Any pointers or suggestions would be much appreciated?
Response to first comment
Thanks Michael - yes we've exported the the SSL Cert's from eu-west-1 into the AWS Cert Manager for us-east-1, as we are running an edge optimised custom domain name. The API Gateway generated Cloudfront is hosted in us-east-1 along with the costom domain and the cname, but the root domain is hosted in eu-west-1. This may be the issue?
We are trying some further tests around enabling the following headers and will report back -
ResponseParameters:
method.response.header.Access-Control-Allow-Headers: true
method.response.header.Access-Control-Allow-Methods: true
method.response.header.Access-Control-Allow-Origin: true
I realise that we can use one custom domain, and carve up the channels using stages mapped to paths instead, but the aforementioned is the preferred solution.
We also have a ticket open with AWS as this has also flummoxed them, and has been escalated to their Internal service team.
:)
So after many weeks for too'ing and fro'ing with Amazon Support we got a solution working, but perhaps not the most cost effective as the early solution mentioned.
Workflow -
1.Create API GW, where each stage has it's own custom domain.
E.G. blue-stage > blue-api.example.com and green-stage > green-api.example.com
2.Then create a Cname in the same AWS region as where the API GW has been created.
E.G. cname-api.example.com
3.Create a Web Edge Cloudfront instance with the same DNS entry (duplicate) used for the Cname (cname-api.example.com) and point this Cloudfront instance to an arbitrary S3 bucket.
Now make the request
curl -Il -H "Host:blue-api.example.com" -H "x-api-key:xxxxxxxxx"
-X GET https://cname-api.example.com/questions/health
HTTP/1.1 200 OK
Yes this is overkill for what we need to achive and also not the most cost effective nor are we doing KISS (Keep It Simple Stupid). So yeah - it would be great to hear back on how others are building similar infrastructure!
Related
I am trying to access an AWS service directly from the browser- specifically the SNS service. I want to be able to post a message directly to an sns topic, but using a CNAME record so I can control which region the browser ultimately goes to (sns.mydomain.com -> sns.us-east-1.amazonaws.com | sns.eu-west-1.amazonaws.com depending on requesters region).
My issue is that if I make an HTTPS request to my aliased endpoint, the returned certificate will not be signed to my endpoint and the browser will refuse to work with it. And while I can get around this by making only HTTP requests, the browser will refuse to make an HTTP request from a secure origin (a site served on HTTPS).
Is it possible to have a CNAME point to an AWS service in the way that I'm trying to do it?
Ultimately, i'm trying to avoid locking the client application in the browser into an aws region.
Is it possible to have a CNAME point to an AWS service in the way that I'm trying to do it?
No. You're hitting up against a central feature of https verification, namely the Common Name of the cert or a SAN ( Subject Alternative Name) must match the certificate. If it weren't so, HTTPS would not be validating that the server is who they claim to be.
Ultimately, i'm trying to avoid locking the client application in the browser into an aws region.
That's a fine goal. Instead of doing so at the DNS layer, why not create an endpoint or configuration setting that supplies region or regions to use? A smart client could even iterate through regions in the case of some failures that appeared to be regional outages, which is somewhat better than a CNAME that you still have to fix when a region goes down.
My Situation
I have a web api hosted in an EC2 instance. I am trying to configure a cloudfront instance "infront" of that EC2 instance.
However, I have not been able to get my cloudfront to forward requests to the EC2 instance. I get hit with an error response like this:
Access to XMLHttpRequest at 'https://api.example.com' from origin 'https://example.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No access-control-Allow-Origin header is present on the requested resource
However, if I change my DNS to point https://api.example.com to EC2 instance's IP address, it works.
What I have done so far
Configured to use correct SSL certificate (for a different problem earlier)
Configured my CF distribution's behaviors to Whitelist Headers: "Origin"
Configure my CF distribution's behaviors to "All" - (which disables caching)
Invalidated cloudfront cache
What I am trying to do
I came across this AWS doc titled "Configuring CloudFront to Respect CORS Settings".
Link
However, it only says "Custom origins – Forward the Origin header along with any other headers required by your origin."
But... How do I do that? How do I forward origin header along with any other headers required? The docs doesn't specify or link to another docs to do it.
I have spent 4 hours or so now and it's extremely frustrating because Cloudfront takes ~30 minutes to deploy.
I have managed to fix this issue it turned out I had overlooked another error returned by Cloudfront: 502 Bad Gateway. Even though Chrome will show the abovementioned error "Access to XMLHttpRequest...". This was caused by my improper DNS and SSL certificates configuration due to my inexperience.
I will try to answer my own question, seeing after hours of searching, there wasn't a straight answer regarding (Cloudfront, EC2 and HTTPS) in Stackoverflow and there are many unaswered questions.
The goal my group was trying to achieve was enabling HTTPS connectivity for the entire set-up: Users' browsers, Cloudfront distribution and my EC2 instance.
What I did to fix this:
Generated a free SSL certificate (e.g. Let's Encrypt) to use for EC2 instance using a sub-domain (i.e. ec2.example.com or wildcard *.example.com). *Note: ACM does not allow public SSL certificates to be exported that can be used in EC2 instances, so use other free online SSL services. Do not use self-signed certs.
Import this certificate into ACM to be used for Cloudfront later too.
Created a new DNS A record to map the sub-domain to the EC2 instance. (e.g. ec2.example.com to ec2-xx-xxx-xx.ap1-location.amazonaws)
Created a new Cloudfront distribution and set the origin as the sub-domain, ec2.example.com. Also, under "Cache Based on Selected Request Headers", set it to "Whitelist" and to forward "Origin" headers. For SSL cert in Cloudfront, use back the one generated back in step 1)
Created a new DNS A record and map an "api" sub-domain to the Cloudfront. (e.g. api.example.com to abcdxyz.cloudfront.net)
I am now able to use a sub-domain (api.example.com) to communicate with Cloudfront which in turns communicates back to my EC2 and performs caching, using HTTPS all along.
Reference links: link1,
link2
There is probably a better way to set this up and if so, please do correct me so I can improve too! Hopefully this answer will help someone else new like me in the future too.
I'm developing little service using lambda functions which returns "Fact of the day" in your CLI using curl.
First, I developed business logic, deployed and created lambda using Serverless.
Second, I bought domain using aws route 53, Provisioned certificate and routed domain using `Custom Domain Name on API gateway.
At the moment if you would visit https://domain.io service works as intented but if you would try call curl domain.io it outputs:
<html>
<head><title>301 Moved Permanently</title></head>
<body bgcolor="white">
<center><h1>301 Moved Permanently</h1></center>
<hr><center>CloudFront</center>
</body>
</html>
My goal is to get service running without SSL (or redirect), by calling curl domain.io.
Is it possible to avoid redirection? Or can you create API custom domain name without certificate?
Currently I call curl -F domain.io it will follow redirect, but it's not solution I'm looking for.
Thank you!
Remove the custom domain configuration from API Gateway.
Wait a few minutes for API Gateway to release the custom domain in the AWS Edge Network (there isn't a way to determine when this is complete, but you'll get an error on one of the subsequent steps until it is. 20 minutes should be sufficient).
Create a CloudFront distribution, using the generic ...execute-api...amazonaws.com domain name assigned to your API stage.
For Origin Protocol Policy, select HTTPS Only.
Set the Origin Path to your stage prefix (e.g. /prod or /v1) -- whatever you set up as the stage prefix.
Set the Viewer Protocol Policy to HTTP and HTTPS.
Set the Minimum TTL and Default TTL to 0.
Set the Alternate Domain Name for the distribution to your custom domain.
If you want SSL to optionally work on your custom domain, associate an ACM certificate with the CloudFront distribution.
Change your DNS entry to point to the *.cloudfront.net hostname assigned to your distribution.
Wait for the CloudFront distribution state to change from In Progress to Deployed.
Test.
This seems like a lot of effort to enable HTTP against API Gateway, but it is necessary, because API Gateway was specifically designed not to support HTTP -- it only works with HTTPS, because that's a best-practice for APIs, generally.
Q: Can I create HTTPS endpoints?
Yes, all of the APIs created with Amazon API Gateway expose HTTPS endpoints only. Amazon API Gateway does not support unencrypted (HTTP) endpoints.
https://aws.amazon.com/api-gateway/faqs/
CloudFront is commonly known as a CDN, but it is in fact something of a Swiss Army knife of custom HTTP request manipulation, and this is a case of that.
Once you verify your behavior, you can optionally increase the Default TTL in CloudFront, which will cause it to cache responses for up to that value in seconds, reducing your costs by sending fewer actual requests to API Gateway and replaying cached responses to the callers.
This setup differs from what you have, now, because you are in control of the CloudFront distribution, instead of API Gateway... so you can customize it in ways that API Gateway doesn't allow when it is in control.
I have a Lambda function proxied by API Gateway. However, API Gateway only expose https and not http. I searched everywhere but looks like API Gateway is not possible to accept http.
So my question is how to translate http client calls to https and send to api gateway? I am asking because my client can only make http calls and they won't change.
You can put a Cloudfront distribution in front of API Gateway with to following setup:
Origin Protocol Policy: HTTPS Only
Viewer Protocol Policy: HTTP and HTTPS
Forward Headers: None
Object Caching: Use Origin Cache Headers
CloudFront is a quick solution as it's much easier to set up compared to instantiating an Elastic Beanstalk.
I have spent couple of hours trying to get this right, so just to share some good write-ups and one more gentle reminder:
This Smartcam project utilize Amazon API Gateway and CloudFront, it describes the setup procedure in details.
The official example given in Amazon's doc demonstrates how to create GET and POST method with query string parameters (e.g. /search?keyword=mars&...), JSON payload and plus path parameters. Must read!
Personal 2 cents:
if you are using query string parameters, make sure to edit behavior and then choose forward all, cache all under Query String Forwarding and Caching.
Alternatively, read this doc for another two possible configurations.
If you already set the Origin Path to your stage variable (by default: /prod), then when you invoke your CloudFront domain, skip the /prod, simply: xxxx.cloudfront.net/resource1/resource2?param1=...¶m2=...
I recently had a 4 hour long phone call with an AWS representative about a similar problem we had in production stage. My situation was similar, there was nothing we could change in APIGateway to fix it (the rep tried all kinds of tricks, but nothing seemed working). So our conclusion was to spin up an EC2 instance as a proxy server for APIGateway and forward all the traffic. There was some additional work such as transferring the domain name, but overall it worked just fine. In your case, as you only need to redirect HTTP traffic, a simple ElasticBeanstalk proxy app might be enough (EB uses HTTP by default and is behind a Nginx proxy server).
You may create a CloudFront distribution just for the HTTP to HTTPS redirection.
I advise you to first obtain a SSL certificate for your domain, in ACM (Certificate Manager), the region must be us-east-1.
In CloudFront, click Create Distribution then select Web to create a web distribution.
Here are all the settings you may use, with some extra explanation. Please note that I use v0 as API Gateway stage for this example.
# ORIGIN SETTINGS
# ---
# Origin Domain Name - Paste the domain name of your API Gateway > Stages > v0 but without the path at the end
Origin Domain Name: https://<getway-id>.execute-api.eu-central-1.amazonaws.com
# Origin Path - The name of your API Gateway stage
Origin Path: /v0
# Origin ID - The ID for this origin
# By default it will be defined as `Custom-<getway-id>.execute-api.eu-central-1.amazonaws.com/v0`
# I replace `Custom` by `v0` just to quickly recognise it in the list later on.
Origin ID: v0-<getway-id>.execute-api.eu-central-1.amazonaws.com/v0
# Minimum Origin SSL Protocol - Choose the minimum SSL protocol for CloudFront to use when it establishes an HTTPS connection to your origin.
Minimum Origin SSL Protocol: TLSv1.2
# Origin Protocol Policy - HTTPS since that is all that API Gateway supports. So with HTTPS CloudFront to connects to your origin only over HTTPS.
Origin Protocol Policy: HTTPS
# DEFAULT CACHE BEHAVIOR SETTINGS
# ---
# Viewer Protocol Policy - CloudFront allowed protocol to access your web content
Viewer Protocol Policy: Redirect HTTP to HTTPS
# Allowed HTTP Methods - HTTP methods you want to allow for this cache behavior
# Select at least GET, HEAD, OPTIONS
Allowed HTTP Methods: GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE
# Compress Objects Automatically - No since we are not serving files via CloudFront, we are just returning redirects
Compress Objects Automatically: No
# DISTRIBUTION SETTINGS
# ---
# Price Class
# There is no extra charge for using extra locations, so you may leave it on `Use All Edge Locations`. The total request count is aggregated across all locations for the free tier.
Price Class: Use All Edge Locations
# Alternate Domain Names (CNAMEs) - The domain names of your websites
Alternate Domain Names (CNAMEs): www.example.com
# SSL Certificate - The `Custom SSL Certificate` MUST be a certificate obtained in us-east-1
# So, you may go to ACM (certificate manager) and request a certificate there
# https://console.aws.amazon.com/acm/home?region=us-east-1
SSL Certificate: Custom SSL Certificate > www.example.com
# Custom SSL Client Support - Leave to default value
Custom SSL Client Support: Clients that Support Server Name Indication (SNI)
# Security Policy - Leave to default value
Security Policy: TLSv1.2_2018
Once your CloudFront distribution deployed, go to Route 53 and select your Domain Name in your Hosted Zones.
Create a Record Set > A or select the existing A record. Then in the Record edition console:
Set Alias to Yes. Then set the Alias Target so it points to your CloudFront distribution (instead of your API Gateway if that's what you previously defined). It will appear in a dropdown, and be like www.example.com (<cloudfront-id>.cloudfront.net)
You could also use an Application Load Balancer in front of the Lambda to accomplish this:
https://docs.aws.amazon.com/lambda/latest/dg/services-alb.html
I'm trying to set a CNAME on Cloudflare to point to an Amazon API Gateway endpoint. The CNAME is for use when referring to one of my subdomains. The gateway in turn points to the IP of a server on DigitalOcean. I am very new to Amazon web services and would appreciate if someone could give me an overview of the correct configuration for the DNS, Amazon Gateway and Cloudfront (which I think is needed to expose the gateway to DNS servers external to Amazon). Any help would be much appreciated.
UPDATE
I've been going at this for a while now and not making much progress. Does anyone have an idea if this is a viable approach or how else it might be done?
UPDATE2
I thought I needed to add the CNAME record to cloudFlare and just ended up in a redirect loop, observed by:
curl -L -i -v https://sub.mydomain.com/
NOTE: It seems this method doesn't work anymore as AWS now only accepts certificates from certain authorities. I haven't tested it myself, but the answer by Gunar looks promising.
There are several reasons why it doens't work to simply point Cloudflare at your API Gateway domain and call it a day:
API Gateway uses shared hosting so it uses the domain name to figure out what API to send requests to. It has no way of knowing that api.yourdomain.com belongs to your API.
API Gateway requires that you use https, but the certificate that it uses is only valid for the default domain.
There is a solution, however. Here are the steps that I followed when I recently set this up:
Generate an origin certificate from the crypto tab of the Cloudflare dashboard.
Import the certificate to AWS Certificate manager in the us-east-1 region, even if your API is located in a different region. If you are prompted for the certificate chain you can copy it from here.
Add your custom domain in the API Gateway console and select the certificate you just added. Check the AWS support article for more information on how to do this.
It usually takes about 45 minutes for the custom domain to finish initializing. Once it's done it will give you a new Cloudfront URL. Go ahead and make sure your API still works through this new URL.
Go to the Cloudflare DNS tab and setup a CNAME record pointing to Cloudfront URL you just created.
Switch to the crypto tab and set your SSL mode to "Full (Strict)". If you skip this step you'll get a redirect loop.
That's it. Enjoy your new highly available API served from your custom domain!
Set up Amazon's API Gateway Custom Domain with CloudFlare
In your AWS management console go to the API Gateway service and select Custom Domain Names from the left menu.
Click the Create button.
Log into CloudFlare, select your domain and open the Crypto tab
Go to SSL and set your SSL mode to "Full (Strict)" to avoid a redirect loop.
Go to Origin Certificates and click Create Certificate
Let CloudFlare generate a private key and a CSR and choose RSA as the private key type
Make sure that the hostname for your custom API domain is covered. (e.g. api.mydomain.com. You can specifically configure this custom domain or use a wildcard such as *.mydomain.com as is configured by default.
Pick PEM as the key format which is selected by default.
In AWS switch to region US-EAST-1 and goto the Certificate Manager.
Click Import a Certificate.
Copy the certificate body from your CloudFlare certificate to Certificate body to the configuration of the custom domain in the AWS Management Console.
Copy the Private key to the certificate private key field in the console
In the certificate chain copy the Cloudflare Origin CA - RSA Root which can be found here.
Enter your custom domain name in the AWS console and a name for your certificate
Now the custom domain name will be created in AWS CloudFront. It can take up to an hour before the domain becomes active.
The next thing you need to do is set up the mappings of the custom domain in the AWS Console.
The final step is to create a new CNAME Record in CloudFlare to link your domain to the CloudFront url. When you open the settings page of your custom domain in the AWS console copy the Distribution domain name. This is the domain you need to use when creating the new CNAME Record.
Source
I couldn't get any of the other answers to work. So I ended up having AWS generate the certificate instead of using a Cloudflare Origin one. That's because AWS wouldn't accept my Cloudflare certificate, even when the chain was provided. I couldn't see Cloudflare in Mozilla's Certificate Authority list (which is what AWS relies on, according to the docs) so I guess that makes sense.
Here's the outline of my solution:
Create AWS Route53 Zone
Create AWS ACM Certificate (must be in us-east-1) with validation method DNS
Create Cloudflare DNS Record with the output of (2)
Create AWS API Gateway Domain Name
Create Cloudflare DNS CNAME Record pointing '#' (root domain) to the Cloudfront domain name from step (4)
Create AWS API Gateway Base Path Mapping
This should be roughly it. May this help someone. Feel free to ask questions.
Both existing answers to this question are correct, but if the issue still persists even after following these directions perfectly, try going into the API Gateway settings, navigate to "Custom Domain Name" and configure the Base Path Mappings.
This was the missing step that solved all my problems.