Restrict Lambda function URL access to CloudFront - amazon-web-services

AWS have recently released the Lambda function URLs feature which allows a function to be invoked via a URL.
I would like to allow my function to be invoked via a URL but only via CloudFront.
I don't want people to be able to bypass CloudFront and invoke the function directly.
Is there a way to configure this? I am aware that I can restrict the function URL by setting the auth type to AWS_IAM but am not clear on how I then allow CloudFront to call it.

Currently, the only option I see is quite similar to how you would protect an ALB in a way that access is restricted to CloudFront:
Configure CloudFront to add a custom HTTP header to requests that it sends to the Application Load Balancer lambda function URL.
Configure the Application Load Balancer Lambda to only forward process requests that contain the custom HTTP header.
My thoughts on approaches that may not work when using lambda function URLs:
IAM auth (since I see no way to sign these requests origination from CloudFront, maybe that will change in the future when lambda function URLs become a first class citizen like S3-origins)
restricting access via security groups (because there are no SGs for lambda func URLs)

Confirmed with AWS support that there is currently no way to do this: "[with the] current design of CloudFront, it is not possible for CloudFront to relay IAM authenticated requests to Lambda URL origin.." There is a feature request for this (but they did not provide a timeframe for implementation and release) but hopefully they provide a solution similar to, and as straight forward as, the CloudFront integration with S3 via the Origin Access Identity.

Here's what I did to make it work on my side :
go to the CloudFront page
click on create a new distribution
In section Origin domain you have to paste in your lambda function URL
Make sure to adjust the caching policy depending on what your lambda function consumes
You might want to create a dedicated policy in you want the cache key to depend on the query string, the cookies, etc...
For my use case I created a new policy to take into account the query string

Related

secure aws s3 objects (control access with authorizer like jwt, how a web app would normally do)

I need to secure my s3 bucket objects. In my web application I'm using aws-sdk to upload media to s3 bucket and get an http link back to access that object. This http link is public by default and I want to make it secure so that only authorized users can access the media. aws s3 allows to make the object private but it wont let anyone with the link access the object.
This link will be accessed from a mobile app where I dont want to use aws-sdk, Instead I want to execute some logic on aws side whenever someone tries to access the http link for the object.
What I would like to happen is, before the user gets access to s3 object, Some authorizer code would execute (like a jwt token authorizer) and depending on it user would be granted/denied access.
I'm currently looking into Amazon API Gateways, I believe they can be accessed as an http link and AWS Lambda could be used to secure them(where i would execute my jwt authorizer). Then these apis would have access to s3 internally.
If someone could point me in the right direction, If this is at all possible.
If I could use the same jwt token issued from my web-application to send along the request to Amazon API Gateway, that would be great.
I would make the bucket private, and place a CloudFront distribution in front of it. Using an Origin access identity to allow only CloudFront to directly access the S3 bucket.
Then to provide security I would use either CloudFront signed cookies, or Lambda#Edge with a custom JWT token validation.
The easiest solution to expose private objects in an S3 bucket is to create a pre-signed URL. Pre-signed URLs use the permissions from the service (which pre-signs the URL) to determine access and have only a limited duration in which they can be used. They can also be used to upload an object directly to S3 instead of having to proxy the upload through a lambda function.
For a download functionality and a smooth user experience, you can - for example - have a lambda function that generates a pre-signed URL and returns it as an HTTP 302 response, which should instruct the browser to automatically download the file from the new URL.
(Edit)
Following on what I've stated in the comments on this answer, if you're proxying the upload/download of the objects through services such as API Gateway or Lambda, you will be severely limited in the size of files that you are able to upload to S3. The payload size limit on an API Gateway is 10 MB and for requests to lambda your payload is capped at 6MB for synchronous invocations. If you want to upload something larger than 10 MB, you will need to use direct upload to S3 for which pre-signed URLs are the safest solution.
I know I am bit late here, but I wanted to give my opinion in case someone has the same problems.
Your mobile app should communicate with a server app (backend app) for authentication and authorization. let's say you are deploying your server app on AWS VPC. Now, it's simple to manage the files access by creating a policy which allow just your server app (IP, or VPC) to access the bucket. the authorization part will be managed on your application.

AWS Lambda (URL) rewrite flow

I am new to Lambda so I would like to understand how the following scenario can be deployed:
Lambda connected to API gateway ( which in turn connected to a reverse proxy)
The request from API gateway to lambda needs to be routed to 3 different ALBs each in different (VPCs) private subnets.
What configuration changes I need to bring in to achieve this apart from writing Lambda function (using python) to rewrite the urls?
It would be nice if someone can explain the message flow here.
Thanks
Abhijit
This to me seems to be multiple issues, I'll try to break down whats trying to be achieved.
Using ALBs with API Gateway
There are many options for how API Gateway can use load balancers to serve http traffic. The solution really depends on which type of API Gateway you are trying to use.
Assuming your API is either REST or WebSockets you are left with 2 choices for enabling HTTP traffic inbound to a load balancer:
Directly as a HTTP or HTTP_PROXY request, listing publicly accessible hostnames to which API Gateway will forward the traffic.
If you want to keep transit private then your only option is to create a network load balancer and make use of VPCLink to create a private connection between API Gateway and your Network resource.
If you're creating a HTTP API (sometimes referred to as API Gateway v2) then you can make use of direct connection to a private ALB, however be aware that at this time HTTP API does not support all the features of REST APIs so you would want to compare feature sets before doing this.
Using multiple load balancers to direct traffic
You determine the value per each resource/method combo, for example POST /example would be assigned its target endpoint, but only one.
My suggestion would be to make use of stage variables if you're using a REST API to specify any endpoints that you're forwarding traffic for the following reasons:
Prevents mistyping of domain names
Allows quick replacement of a hostname
Provides functionality for canary deployments to shift traffic proportionally between 2 variable names (these could be anything as long as the type is the same e.g. Lambda to another Lambda, not Lambda to a load balancer).
Using a Lambda to redirect
Technically a Lambda can perform a redirect by return a response using the below syntax
{
statusCode: 302,
headers: {
Location: 'https://api.example.com/new/path',
}
}
However be aware this will change the request to become a GET request, this will also remove the payload of the body request when the redirect occurs. Additionally you would need to set this up for every resource/method combo that you wanted to redirect.
There are 2 options that you have available to get around these issues, both involve using CloudFront combined with a Lambda#Edge function.
The first solution can act as a workaround for the request type changing, in the Origin Request event you could modify the Request URI property to match the new URI structure. By doing this your clients would be able to use the API still, whilst you would notify them of the depercations to certain paths that you were migrating.
The second solution acts as a workaround for the need to add redirects to each resource/method combo which can create a lot of mess of methods just for redirects. You could create a Lambda#Edge function to perform the same redirect on an Origin Response event. You could create mappings in your Lambda function to work out which URL it should redirect to.
There are many great examples on the Lambda#Edge example functions page

How to secure the APIGateway generated URL?

I have a serverless backend that operates with APIGateway and Lambda. Here is my architecture:
Currently, anyone with my APIGateway's URL can query or mutate the data. How do I protect the URL, so that only the client(react app) can access it. So, here is my concern, anyone can open the network tab in chrome console and get my APIGateway's URL and can use it using curl or postman. I want to prevent that.
Solutions I had in my mind:
Set up a CORS, so that only the origin can access it. But, I have a different lambda that invokes this URL. So, CORS wont work out.
I am sure there are some methods with the APIGateway itself. I am not getting right search term to get it from AWS documentation. I would also like to know what are the best practices to prevent accessing the backend URL apart from the Client(React App)
Update after #Ashan answer:
Thank you #Ashan for the answer. In my case, I use Auth0, so custom authoriser should work for me. I just came across this https://www.youtube.com/watch?v=n4hsWVXCuVI, which pretty much explains all the authorization and authentication possible with APIGateway. I am aware that authentication is possible either by Cognito/Auth0, but I have some simple websites, that has form, whose backend is handled by APIGateway. I can prevent the abuse from scraping bots using captcha, but once the attacker has got the URL, header and request parameters, he can invoke that million times. One thing, we can do is having an API-Key, but it is a static string with no expiration. Once the headers are with him, he can abuse it. So, any idea, how to prevent this in APIGateway. If not any other service apart from AWS that I can look for? Would be glad, If I get an answer for this.
Currently API Gateway does not support private urls, so it will be publicly available.
To restrict access you need to use a authorizer to authenticate and authorize the request using IAM policies. There are two options available at the moment.
IAM authorizer
Custom authorizer
If your authentication flow can directly (AWS STS, IAM user access keys or roles) or indirectly(Using AWS Cognito Userpools or any other SSO provider) can get temporary security credentials, then you can use IAM authorizer. From API Gateway side no code involved and its a matter of selecting the IAM check box for each API Gateway resource. You can use the API Gateway SDKs to invoke API Gateway requests where the SDK will handle the heavy liftings in setting up authentication headers.
If you use your own authentication mechanism, then you can write a seperate Lambda function to validate the tokens. This Lambda function name can be specified at API Gateway with the http hearder name to access the custom token to verify the requests.
To control API usage by authorized consumers, using API Key is the only way native to AWS at the moment.
Since you are using S3 for the react app hosting, you can further reduce the attack surface by using AWS WAF and CloudFront infront your application stack. The API Key can be added to CloudFront headers to forward to your APIGateway origin and since CloudFront and APIGateway communication happens using SSL, its nearly impossible for someone to find the API key. Using AWS WAF you can limit malicious access for common attacks. This includes rate based blocking to limit someone from repeatedly invoking the API.

Routing POST requests from CloudFront to Lambda

I have a website distributed with CloudFront, with S3 as an origin. I've written a Lambda function that takes a contact form submission and sends the email along with SES. The Lambda test out just fine : )
But, I'm clueless when it comes to routing POST requests from CloudFront to that backend Lambda function. How do I do this?
Update: Okay, I've got the API Gateway test triggering the Lambda function just fine, but I can't seem to call it from CloudFront (or rather using a curl command to my domain set up with CloudFront).
Do I need to list my domain as a custom domain in API Gateway?
If I list the path /api/* in my CloudFront Behaviors, do I have to mirror that in my API Gateway set up? So, does my API Gateway need to start with /api before I add specific resources?
Update 2 I think I needed to leave or remove the /dev off the end of the API Gateway URL. dev being my stage.
Update 3 Okay, it feels one step away now. I've got everything hooked up. The test request hits cloudfront, it forwards to api gateway, gateway calls lambda (at this point I'm shaking my head at how complicated we've made all this), and lambda sends back success or failure to api gateway, and we're peachy. Except, I get MethodNotAllowed when I do it from curl or the browser. Do I need to add an IAM role to CloudFront to access API Gateway?
Update 4 Still not working. And now, I'm back to getting my usual 404 error page that my Default Origin (S3). Seems like serverless is a fading dream.
Update 5 Trying a different approach, recommended here: https://serverfault.com/a/839368 The idea is to use API Gateway's Custom Domain name features with a subdomain like api.example.com and then use a Route53 Alias record to direct subdomain traffic to API Gateway. This could work. Then CloudFront would handle traffic to example.com and www.example.com, and API Gateway would get requests to api.example.com. Now the challenging bit is that in HTML forms the action attribute will have to go to a different subdomain. Let's see what kinds of errors and crazy behavior we get : (
First you would setup API Gateway in front of your Lambda function so it can be called via a POST request. It sounds like you may already have that part done?
Then if you want the POST to go through CloudFront you would add a second origin in CloudFront that points to your API Gateway.
This is all possible, but its tricky to configure. To help I created an open-source boilerplate app that correctly sets up:
A static site with CloudFront and S3
An API with API Gateway and Lambda
CORS between the static site and API
Optional OAuth 2.0 and JWT cookie for the static site
See this static JavaScript app for an example of a static site POSTing to an API backed by Lambda.
Depends on what you're using as your backend (which language, framework, etc.), there are different ways, but 'em all about one thing: Invoke
Kind-of the most generic call - HTTP is right there, the API call examples by language are referenced at the end of the doc.

Use a sub-domain for a API-gateway/Lambda

I am integrating a web-relay into AWS-service which makes call-outs to a predefined path (
/some-fixed-path and it can not be configured) and I want to intercept it using a lambda on dedicated sub-domain, to keep this separated from the rest of our service, so I want the call-out to be http://subdomain.example.com/some-fixed-path.
I have a domain (lets call it example.com) registered and I have a hosted-zone defined. How can i create a record-set in the hosted-zone and use it in the API-gateway definition? (The url must not contain the stage...)
In the API-gateway definition, there is a "Custom domain name" option, but I can't figure out how to point to a record from my hosted-zone.
You should simply be able to follow the instructions for using a custom domain and then adding an alias record in your hosted zone to the CloudFront distribution provided by the API Gateway console.
You'll want to configure your custom domain with the base path pointing to your deployed stage. At that point you can than create your resource at some-fixed-path.
Note: API Gateway currently requires all APIs to be HTTPS, so if your call out can't be changed to support HTTPS, API Gateway will not work for this use case.
AWS has a detailed guide about how to do that exactly.
A few more things to pay attention to are:
Make sure you remember to re-deploy when you make any change to the API.
When you set up Base Path Mapping, make sure double check the API resource path and method. (For example, if you create the API gateway through lambda template, the API resource will be created under /{API name} instead of /).
A lot of people see Missing Authentication Token when they use API gateway for the first time due to those reasons.