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
Related
I have the following AWS architecture:
Dns Name -> Cloud front
Cloud front -> API gateway
API gateway -> Lambda function
Problem can not identify Dns Name inside Lambda function
The only thing that I can get from request headers is dns name of API gateway
Are there any Cloud front configuration that allows to pass user entered dns name through all the layers till backend code?
in headers I receive host field but it contains dns of api gateway and not cloud front or public dns name
"Host": [
"9b8vc8vvhl.execute-api.us-east-1.amazonaws.com"
],
Here's what is passed in browser
UPDATE:
Implementation attempt from comment
gives
503 ERROR
The request could not be satisfied.
The Lambda function associated with the CloudFront distribution is invalid or doesn't have the required permissions. We can't connect to the server for this app or website at this time. There might be too much traffic or a configuration error. Try again later, or contact the app or website owner.
If you provide content to customers through CloudFront, you can find steps to troubleshoot and help prevent this error by reviewing the CloudFront documentation.
Generated by cloudfront (CloudFront)
Request ID: n03zM0u93vrvdcwQi-hgyhONbv3b10x3ETq-A4Ru7-fC-RUlskjJxQ==
The reason why you get 9b8vc8vvhl.execute-api.us-east-1.amazonaws.com as Host in your lambda function is because, by default, CloudFront rewrites the Host in the origin request.
From HTTP request headers and CloudFront behavior documentation:
Header
Behavior if you don't configure CloudFront to cache based on header values
Host
CloudFront sets the value to the domain name of the origin that is associated with the requested object.
Host in the request header that your browser sends to CloudFront: yuriy-test-cars2.pp.ca (CloudFront uses this value to route your request to the distribution owned by you)
Host in the request header of the origin request that CloudFront sends to API Gateway: 9b8vc8vvhl.execute-api.us-east-1.amazonaws.com (This is the origin domain that you configured in your distribution. CloudFront sets it as the value of the Host header in the origin request. This is consistent with what the documentation says.)
Issue
You can't simply passthrough the Host header to the origin in CloudFront since the API Gateway service uses this value to route your request to the API that you created.
Solution
Create a CloudFront Function to make a copy of the Host header.
function handler(event) {
var request = event.request;
request.headers['cf-host'] = request.headers.host;
return request;
}
Here, I save the value to a custom header named Cf-Host.
Associate this function to the viewer request event.
Create an Origin request policy and include the Cf-Host header in the policy. This makes sure that the Cf-Host header is included in the origin request.
You will be able to access the Cf-Host header in your lambda function. The value of the Cf-Host header is the alternative dns name that you are looking for.
Don't know if my solutions is suitable for you, but I have a similar architecture, except the step Cloud Front > API Gateway.
My DNS is a direct CNAME to API Gateway DNS which result in the original header once the request is logged:
My APIGateway also has a "Custom domain name" ties to it, this way AWS create the proper ssl certificate for the my public domain.
Also the API Gateway endpoint could be edge-optimized (https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-api-endpoint-types.html) to be server phisiclaly near the user.
If you don't need any caching feature, maybe you could give a try.
I'm currently testing out how to setup CloudFront with Regional Api Gateway that includes a Custom Domain but have run into a bit of snag.
I would like to be able to use: https://mySuperDomain.com/rest/get_stuff
Setup:
Api Gateway
I have a regional Api Gateway that points to a Lambda function.
The Gateway includes a Cognito Authorizer
The Integration Request is a Lambda Proxy
The base Resource is "/" with Methods: GET & OPTIONS
The stage is "dev"
Custom Domain name and ACM are set to "mySuperDomain.com"
Configuration is also Regional
Directly calling the url works as expected: https://XXXXXXXX.execute-api.REGION.amazonaws.com/dev
Route53
A record is an alias that points to the CloudFront Distribution
Base domain also contains NS, SOA, and MX
CloudFront
Origins:
Alternate Domain Names: www.mySuperDomain.com & mySuperDomain.com
Origin and Domain Path: XXXXXXXX.execute-api.REGION.amazonaws.com/dev
HTTPS only
Behaviors:
/rest/*
Origin group is set to the Origin
HTTPS only
This is where things get confusing.
When including no headers, I get a "Missing Authentication Token" error. This signals to me that I'm hitting the correct endpoint but the Authentication Token is not making it through CloudFront.
However, when I whitelist what I describe as the kitchen sink: "Authorization, Host, Origin, Referer, Accept-Datetime", I get 403 Forbidden when calling: https://mySuperDomain.com/rest/get_stuff.
What am I missing in order to access the specified custom domain URL? Thanks ahead of time.
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.
Refering to AWS Cloudfront Documentation, AWS API Gateway supports TLS v1.0, v1.1, v1.2.
But I want to limit the encryption protocols to TLS v1.1 and v1.2 for my Gateway API. Where do I configure this?
I do not see any cloudfront distribution for my API. Gateway resource page does not have an option to specify the security protocol.
My API is running in production for last 2 years using a custom domain.
Any idea how do I limit my API to TLS V1.1 and V1.2 protocols only in API Gateway?
I have just been working on this extensively and through a great deal of trial & error, I can document what I believe is the current optimal solution to this. The answer from suman j was the best solution back in Oct 2017 however it does have a limitation and also AWS has evolved since then.
So what is the limitation?
If you are using Lambda with the API Gateway and delete the Custom Domain Name, then manually creating a CloudFront distribution and associating a Lambda Function requires a specific Lambda version number. That is, it does not support aliases. This is problematic with CI/CD where the version numbers can continually change. However, the API Gateway Custom Domain Name Base Path Mappings do support aliases so it can be better to continue using these.
So how has AWS evolved?
As of November 2017, API Gateway supports creating Regional Endpoints in Custom Domain Names. These endpoints do not create a CloudFront distribution, thus optimising the strategy of placing your own CloudFront distribution in front of them that prevents the use of TLS v1.0.
So how do I set it all up?
The steps I used to do this (via the console) are as follows. Note that you may need to change some settings to support your particular application. For the purposes of this documentation let’s say your api is named api.example.com.
In API Gateway, edit your Custom Domain Name, add a Regional Configuration, select your certificate and click on Save. Note: for regional APIs, you need to use an ACM certificate from the same region as the API. More info here: https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-regional-api-custom-domain-migrate.html
Copy the Target Domain Name of the newly created Regional Endpoint. (Eg d-abcdefg123.execute-api.us-east-1.amazonaws.com)
In Route 53 or your DNS Provider, change the mapping of your API from the Edge Optimized CloudFront target domain name to the newly created Regional Endpoint target domain name (ie d-abcdefg123.execute-api.us-east-1.amazonaws.com).
Once the DNS change has propagated, edit the Custom Domain name and delete the Edge Optimized Endpoint by clicking on the x icon. This should then enable you to then create a new CloudFront distribution with the same CNAME of your API without AWS blocking you.
In API Gateway, create a new Custom Domain Name with Domain Name = regional-api.example.com, Endpoint Configuration = Regional and select the ACM Certificate. Click on Save and then Edit and add the Base Path Mapping as per your current API and click on Save. Copy the Target Domain Name of the newly created Regional Endpoint. (Eg d-xyzabcd456.execute-api.us-east-1.amazonaws.com)
In Route 53 or your DNS Provider, create a new CNAME record mapping regional-api.example.com to the newly created Regional Endpoint target domain name. (Ie d-xyzabcd456.execute-api.us-east-1.amazonaws.com)
In CloudFront create a new Distribution with the following settings:
ORIGIN SETTINGS:
Origin Domain Name = regional-api.example.com
After entering the above the following hidden fields should then be displayed:
Origin SSL Protocols = TLSv1.2 & TLSv1.1
Origin Protocol Policy = HTTPS Only
DEFAULT CACHE BEHAVIOUR SETTINGS:
(these values are what I needed for the app that calls the API to work properly)
Viewer Protocol Policy = Redirect HTTP to HTTPS
Allowed HTTP Methods = GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE
Cached HTTP Methods = OPTIONS
Cache Based on Selected Request Headers = Whitelist
Whitelist Headers = Authorization
Object Caching = Customize
Minimum TTL = 0
Maximum TTL = 0
Default TTL = 0
Forward Cookies = All
Query String Forwarding and Caching = Forward all, cache based on all
Smooth Streaming = No
Restrict Viewer Access (Use Signed URLs or Signed Cookies) = No
Compress Objects Automatically = No
Lambda Function Associations = None
DISTRIBUTION SETTINGS:
Price Class = Use All Edge Locations
AWS WAF Web ACL = None
Alternate Domain Names (CNAMEs) = test-api.example.com
SSL Certificate = Custom SSL Certificate (example.com)
Custom SSL Client Support = Only Clients that Support Server Name Indication (SNI)
Security Policy = TLSv1.1_2016 (recommended)
Supported HTTP Versions = HTTP/2, HTTP/1.1, HTTP/1.0
While waiting for the CloudFront Distribution creation to complete (average 40 mins), in Route 53 or your DNS Provider create a new CNAME record mapping test-api.example.com to the newly created CloudFront Domain Name (eg d123abcdefg.cloudfront.net)
Once the Distribution creation has completed fully test your application against test-api.example.com
If the testing is all good then update the Alternate Domain Names (CNAMEs) of your new CloudFront to be = api.example.com. (note - this will not make it ‘live’, the DNS change below is required for this)
Once the distribution update is complete (average 40 mins) then in Route 53 or your DNS Provider, update the CNAME record mapping of api.example.com to the newly created CloudFront Domain Name (ie d123abcdefg.cloudfront.net)
If all is working well you can now delete the Route 53 / DNS CNA test-api.example.com record plus delete the api.example.com API Gateway Custom Domain Name.
For bonus points, if using Route 53 it is recommended to use A and AAAA record aliases instead of CNAME’s for the Route 53 steps above (which I have done). This reduces cost slightly, hides the underlying CloudFront distribution somewhat and also enables IPv6 support.
I hope this helps! :-)
In order for Gateway API with additional cloud front distribution to work, we need to
From AWS Console, under API Gateway go to Custom Domain Name and delete the mapped entry.
Create a new cloudfront distribution with
Cloudfront settings
Origin Domain Name as your Gate API endpoint https://abcdfefg.execute-api.us-east-1.amazonaws.com
Viewer Protocol Policy as HTTPS Only
Origin SSL Protocols as TLSv1.2, TLSv1.1 (Uncheck TLSv1)
Add a CNAME entry under Alternate Domain Name to refer to custom domain name
and few other defaults
After the above changes are completed, accessing the custom domain name on https will enforce the TLS security settings as defined in Cloudfront distribution.
AWS has announced that this is now configurable for edge-optimised API Gateway implementations:
https://aws.amazon.com/about-aws/whats-new/2019/06/amazon-api-gateway-adds-configurable-transport-layer-security-version-custom-domains/
https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-custom-domain-tls-version.html
For anyone seeing this, you can now set a minimum TLS version of 1.2 for your custom domain in API Gateway: https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-custom-domain-tls-version.html
You may create API gateway distribution in your CloudFront distribution list. If your API gateway origin has HTTPS, you can specify what type of TLS protocols to be use between cloudfront and API gateway. Between viewer/client and cloudfront, you can specify the TLS protocols and suites in General section > Security Policy of CloudFront configuration. This configuration only visible if you use custom SSL with SNI. You may choose between:
TLSv1
TLSv1_2016
TLSv1.1_2016
TLSv1.2_2018
As of now, You can enforce minimum TLS version by specifying a security policy for your custom domain. Supported security policies are TLS1.0 and TLS 1.2
Minimum version of TLS version API Gateway security policy support is TLSv1.0
Recommended security policy through Custom Domain Name is TLSv1.2
For greater control over TLS version, Create a CloudFront distribution in your account and use Regional API Gateway endpoint as origin.
Minimum TLS version 1.3 is only possible through Custom CloudFront distribution.
Refer : https://cloudnamaste.com/minimum-tls-version-for-api-gateway/