Integrating AWS API Gateway with Cloud Front without exposing origin - amazon-web-services

I'm working on a project with serverless architecture.
I've found that though AWS said API Gateway can protect your resources from DDoS attack.
But if there is a bad user which keep sending spam to your service,
API gateway can't provide an appropriate way to handle this kind of issues.
So I start to figure out what I can do:
AWS WAF is an obviously solution.
I've found this post on stackoverflow: API gateway with aws waf
Then in order to setup WAF,
I put a Cloud Front distribution in front of the API Gateway.
I realized this might be a workaround solution, but is it?
Here's the issue I found:
I have a cloud front distribution, its domain name is cdn.net
I set the origin path to my api gateway: https://sampleagigw.amazon.com, and set path to its stage dev.
When I call GET http://cdn.net/posts, it will return a result which I expect.
Then if you put the http://cdn.net/posts on browser, it surprised you more than your expect, it also expose the API gateway's url on the url bar of browser: https://sampleagigw.amazon.com/dev/posts
It means all of the work with WAF and Cloud front is meaningless.
Is there anything I misunderstood?

Check the "Viewer Protocol Policy" on your additional CloudFront distribution (cdn.net) and ensure that it is set to either "Redirect HTTP to HTTPS" or "HTTPS Only". Alterntively, you can edit your origin and set "Origin Protocol Policy" to "HTTPS Only".
If you have "Viewer Protocol Policy" set to "HTTP and HTTPS" and "Origin Protocol Policy" set to "Match Viewer", then I can see how you would get this result. You enter http://cdn.net/posts on browser, then the cdn.net distribution attempts to connect to http://sampleagigw.amazon.com (no https/tls/ssl). This goes to the CloudFront distribution created by API Gateway which is set to "Redirect HTTP to HTTPS". Since it got an HTTP request, it returns a 302 redirect to http://sampleagigw.amazon.com. This is returned by the cdn.net distribution to the browser. The browser then follows the 302 redirect, leaving the http://sampleagigw.amazon.com URL in the browser's URL bar.

Related

AWS Cloud Front. How To pass alternative dns name to backend code e.g. lambda function

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.

Can I get an example of how to connect a lambda function to a domain name?

I've been wasting about 12 hours going in circles in what seems like this:
I am trying to just make a simple static landing page in lambda and hook the root of a domain to it.
The landing page works, but api gateway didn't because AWS doesn't seem to set permissions properly by default ("internal server error" with API gateway and lambda on AWS) but now the gateway link works.
So the next steps were the following:
add a custom domain name in the api gateway
add the api mapping in the custom domain name
in route 53, create a wildcard certificate with *.domain.com and domain.com
create an A record that points to the api gateway with domain.com
create a CNAME record that points to the A record
and I get an error 403 with absolutely nothing in the log. I log both 'default' and '$default' stages in the api gateway.
I read https://aws.amazon.com/premiumsupport/knowledge-center/api-gateway-403-error-lambda-authorizer/ which is all about looking at what's in the logs...
and I find the doc is both everywhere and nowhere because it's built as chunks of 'do this' and 'do that' without ever painting a whole picture of how each piece is connected to the other, or any graph with the hierarchy of services, etc. Reminds me of code that works only when you follow the example documented and breaks otherwise.
I'm sure I'm doing something wrong, but given the lack of logs and lack of cohesive documentation, I have no idea about the problem.
Not to mention that http doesn't even connect, just https.
Can anyone outline the steps needed to achieve this? essentially: [http|https]://(www).domain.com -> one lambda function
You cannot use API Gateway for an HTTP request; it only supports HTTPS.
From the Amazon API Gateway FAQs (emphasis mine):
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. By default, Amazon API Gateway assigns an internal domain to the API that automatically uses the Amazon API Gateway certificate. When configuring your APIs to run under a custom domain name, you can provide your own certificate for the domain.
You can use CloudFront to automatically redirect HTTP to HTTPS. How do I set up API Gateway with my own CloudFront distribution? provides a pretty simple walkthrough of connecting an API Gateway to CloudFront (you can skip the API Gateway portion and use the one you created). The important thing you'll need to do that is not in that document is to select Redirect HTTP to HTTPS.
If you truly need HTTP traffic you're probably going to need to go with an ALB.

Cloudfront X-Cache shows "Miss from cloudfront" all the time

I have a cloudfront web distribution setup for an API Gateway proxy. The API is IAM enabled mock call. I also created a customer name which has the same domain name as the cname I used to create cloudfront distribution. I set cloudfront up to pass Authorization and Host only for headers. And set Query String forwarding and Forward Cookie to None.
When I hit the cloud front using Postman, I kept on getting Miss from cloudfront from the response. I also disabled Postman's send no-cache header and I also tried manually adding a Cache-Control: max-age=3600.
But None of these works.
Anyone knows why?
I just solved a similar issue by noticing that the API Gateway domain name for my RestAPI was always returning an X-Cache header with "Miss from cloudfront"; but after realizing I had used the execute api domain, my second request to my *.cloudfront.net domain resulted in a Cloudfront cache hit.

Disable http redirection to https

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.

Redirect http:// requests to https:// on AWS API Gateway (using Custom Domains)

I'm using AWS API Gateway with a custom domain. When I try to access https://www.mydomain.com it works perfectly, but when i try http://www.mydomain.com it can't connect.
Is there a way to redirect the http -> https with the custom domain in API Gateway? If not, is there a way to get the http:// links to work just like the https:// links?
API Gateway doesn't directly support http without TLS, presumably as a security feature, as well as for some practical considerations.
There is not a particularly good way to do this for APIs in general, because redirection of a POST request from HTTP to HTTPS is actually a little bit pointless -- the data is has already been sent insecurely by the time the redirect is generated, unless the client has asked the server to inspect the request headers before the body is sent, with Expect: 100-continue.
You can create a CloudFront distribution, and configure it to redirect GET and HEAD requests from HTTP to HTTPS... but if you send a POST request to such a distribution, CloudFront doesn't redirect -- it just throws an error, since (as noted) such a redirection would be more harmful than helpful.
However... if GET is your application, then it's pretty straightforward: first, deploy your API with a Regional (not Edge-Optimized) API endpoint with a system-assigned hostname, not a custom domain.
Then, create a CloudFront distribution that uses this regional API endpoint as its origin server, and configure the CloudFront distribution's behavior to redirect HTTP to HTTPS. Associate your custom domain name with the CloudFront distribution, rather than with API Gateway directly.