Skip to content

Commit

Permalink
Merge pull request #2442 from mamrane/mamrane-feature-protect-httpapi…
Browse files Browse the repository at this point in the history
…-waf-cloudfront

New serverless pattern - apigw-http-api-waf-cloudfront-terraform
  • Loading branch information
julianwood authored Oct 7, 2024
2 parents 7e62f92 + b90538a commit e902537
Show file tree
Hide file tree
Showing 20 changed files with 1,645 additions and 0 deletions.
125 changes: 125 additions & 0 deletions apigw-http-api-waf-cloudfront-terraform/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
# Protect Amazon API Gateway with Amazon CloudFront and AWS WAF

This pattern demonstrates how to increase the security posture of Amazon API Gateway against common attack patterns such as SQL injection, cross-site scripting (XSS) or DDOS attacks

Learn more about this pattern at Serverless Land Patterns: https://serverlessland.com/patterns/apigw-http-api-waf-cloudfront-terraform

Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example.

## Requirements

* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources.
* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured
* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
* [Python 3.12](https://www.python.org/downloads/release/python-3120/) installed
* [Terraform](https://developer.hashicorp.com/terraform/install) (Terraform) installed

## Deployment Instructions

1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository:
```
git clone https://github.com/aws-samples/serverless-patterns
```
2. Change directory to the pattern directory:
```
cd apigw-http-api-waf-cloudfront-terraform
```
3. Install the Terraform dependencies
```bash
terraform init -input=false
```
4. Preview the resources that will be deployed with Terraform in your AWS account
```bash
terraform plan \
-var "region=#your-region#" \ #region where resources will be deployed (default is us-east-1)
-var "cloudfront_domain_name=#your-cloudfront-domain-name#" \ #domain name for your CloudFront distribution (optional)
-var "cloudfront_certificate_arn=arn:aws:acm:#region#:#accountid#:certificate/#certificateid#" \ #cloudfront certificate arn (required only if cloudfront_domain_name is set)
-var "domain_name=#your-api-domain-name#" \ #domain name for your API Gateway (optional)
-var "certificate_arn=arn:aws:acm:#region#:#accountid#:certificate/#certificateid#" \ #certificate arn (required only if domain_name is set)
-var "zone_id=#zoneid#" #hosted zone id (required only if domain_name is set)
```
5. Provision resources with Terraform in your AWS account
```bash
terraform apply -auto-approve \
-var "region=#your-region#" \ #region where resources will be deployed (default is us-east-1)
-var "cloudfront_domain_name=#your-cloudfront-domain-name#" \ #domain name for your CloudFront distribution (optional)
-var "cloudfront_certificate_arn=arn:aws:acm:#region#:#accountid#:certificate/#certificateid#" \ #cloudfront certificate arn (required only if cloudfront_domain_name is set)
-var "domain_name=#your-api-domain-name#" \ #domain name for your API Gateway (optional)
-var "certificate_arn=arn:aws:acm:#region#:#accountid#:certificate/#certificateid#" \ #certificate arn (required only if domain_name is set)
-var "zone_id=#zoneid#" #hosted zone id (required only if domain_name is set)
```
## How it works
Requests sent to a HTTP API Gateway are protected by an Amazon CloudFront distribution and AWS WAF's web ACL.
The below diagram illustrates the solution.
![Solution diagram](/apigw-http-api-waf-cloudfront-terraform/img/solution_overview.png)
When a request is sent to an Amazon CloudFront distribution (Step 1), it is evaluated by a AWS WAF's web ACL (Step 2).
Then a unique key is retrieved from Secrets Manager by an Amazon CloudFront's Lambda@Edge function (Step 3) and is added as a header (x-origin-verify) in the request (Step 4) before being passed to the API Gateway.
Once the HTTP API Gateway receives the request (Step 5), a Lambda authorizer checks the header (Step 6 & Step 7) and confirm its validity (checks if this is the same value as the one in Secrets Manager).
Finally, the request is sent to a backend Lambda function (Step 8) and a response is returned back to the requester.
If the request is sent directly to the HTTP API Gateway, the request fails as the request is not coming from the Amazon CloudFront distribution (the request doesn't include the header x-origin-verify with the unique key from Secrets Manager).
Note: The unique key in Secrets Manager is rotated periodically as shown on the above diagram (Step A, B and C)
## Testing
After deployment, enter the URL of the CloudFront distribution in your navigator: this should return a successful response ("Hello from Lambda!").
If you enter the URL of the API Gateway: this should return a failure response.
## Cleanup
In order to delete the solution, the follow the steps below.
1. Destroy the resources via Terraform
```bash
terraform destroy -auto-approve \
-var "region=#your-region#" \ #region where resources will be deployed (default is us-east-1)
-var "cloudfront_domain_name=#your-cloudfront-domain-name#" \ #domain name for your CloudFront distribution (optional)
-var "cloudfront_certificate_arn=arn:aws:acm:#region#:#accountid#:certificate/#certificateid#" \ #cloudfront certificate arn (required only if cloudfront_domain_name is set)
-var "domain_name=#your-api-domain-name#" \ #domain name for your API Gateway (optional)
-var "certificate_arn=arn:aws:acm:#region#:#accountid#:certificate/#certificateid#" \ #certificate arn (required only if domain_name is set)
-var "zone_id=#zoneid#" #hosted zone id (required only if domain_name is set)
```

2. Re-run terraform destroy if you the Lambda@Edge function failed to be deleted

(Note: When the Lambda@Edge function fails to be deleted, you may see the following error message: "Lambda was unable to delete arn:aws:lambda:#region#:#account-id#:function:http-edge-lambda:#version# because it is a replicated function. Please see our documentation for Deleting Lambda@Edge Functions and Replicas")

```bash
terraform destroy -auto-approve \
-var "region=#your-region#" \ #region where resources will be deployed (default is us-east-1)
-var "cloudfront_domain_name=#your-cloudfront-domain-name#" \ #domain name for your CloudFront distribution (optional)
-var "cloudfront_certificate_arn=arn:aws:acm:#region#:#accountid#:certificate/#certificateid#" \ #cloudfront certificate arn (required only if cloudfront_domain_name is set)
-var "domain_name=#your-api-domain-name#" \ #domain name for your API Gateway (optional)
-var "certificate_arn=arn:aws:acm:#region#:#accountid#:certificate/#certificateid#" \ #certificate arn (required only if domain_name is set)
-var "zone_id=#zoneid#" #hosted zone id (required only if domain_name is set)
```

3. Delete the secret

(Note: The secret will not be immediately deleted after Step 1. It will only be scheduled for deletion. To fully delete the secret, you will need to use the below command)

```bash
aws secretsmanager delete-secret --secret-id waf-http-api --force-delete-without-recovery
```

----
Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.

SPDX-License-Identifier: MIT-0
49 changes: 49 additions & 0 deletions apigw-http-api-waf-cloudfront-terraform/apigateway.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# create http api gateway
resource "aws_apigatewayv2_api" "http_api" {
description = var.http_api_description
name = var.http_api_name
protocol_type = "HTTP"
tags = {
Name = var.http_api_name
}
cors_configuration {
allow_origins = ["*"]
allow_methods = ["*"]
allow_headers = ["*"]
}
disable_execute_api_endpoint = local.domain_enabled
}

# create http api gateway route
resource "aws_apigatewayv2_route" "http_api_route" {
api_id = aws_apigatewayv2_api.http_api.id
authorization_type = "CUSTOM"
authorizer_id = aws_apigatewayv2_authorizer.http_api_auth_authorizer.id
route_key = "ANY /"
target = "integrations/${aws_apigatewayv2_integration.http_api_integration.id}"
}

# create http api gateway integration
resource "aws_apigatewayv2_integration" "http_api_integration" {
api_id = aws_apigatewayv2_api.http_api.id
integration_type = "AWS_PROXY"
description = "Mock Integration for ${var.http_api_name}"
integration_method = "POST"
integration_uri = aws_lambda_function.http_api_lambda.invoke_arn
passthrough_behavior = "WHEN_NO_MATCH"
payload_format_version = "2.0"
}

# create http api gateway stage
resource "aws_apigatewayv2_stage" "http_api_stage" {
api_id = aws_apigatewayv2_api.http_api.id
name = var.http_api_stage
auto_deploy = true
default_route_settings {
throttling_burst_limit = var.throttling_burst_limit
throttling_rate_limit = var.throttling_rate_limit
}
tags = {
Name = var.http_api_name
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
{
"title": "Protect Amazon API Gateway with Amazon CloudFront and AWS WAF",
"description": "Provision an Amazon API Gateway (with Amazon CloudFront and AWS WAF) using Terraform",
"language": "Python",
"level": "300",
"framework": "Terraform",
"introBox": {
"headline": "How it works",
"text": [
"This pattern demonstrates how to increase the security posture of HTTP API Gateways against common attack patterns such as SQL injection, cross-site scripting (XSS) or DDOS attacks"
]
},
"gitHub": {
"template": {
"repoURL": "https://serverlessland.com/patterns/apigw-http-api-waf-cloudfront-terraform",
"templateURL": "serverless-patterns/apigw-http-api-waf-cloudfront-terraform",
"projectFolder": "apigw-http-api-waf-cloudfront-terraform",
"templateFile": "main.tf"
}
},
"resources": {
"bullets": [
{
"text": "HTTP API Gateway",
"link": "https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api.html"
},
{
"text": "How AWS WAF works with Amazon CloudFront features",
"link": "https://docs.aws.amazon.com/waf/latest/developerguide/cloudfront-features.html"
}
]
},
"deploy": {
"text": ["See the GitHub repo for detailed deployment instructions."]
},
"testing": {
"text": ["See the GitHub repo for detailed testing instructions."]
},
"cleanup": {
"text": ["See the GitHub repo for detailed cleanup instructions."]
},
"authors": [
{
"name": "Mehdi Amrane",
"bio": "Senior Solutions Architect at AWS"
}
],
"patternArch": {
"icon4": {
"x": 10,
"y": 70,
"service": "waf",
"label": "AWS WAF"
},
"icon1": {
"x": 37,
"y": 70,
"service": "cloudfront",
"label": "Amazon CloudFront"
},
"icon2": {
"x": 64,
"y": 70,
"service": "apigw",
"label": "Amazon API Gateway"
},
"icon3": {
"x": 90,
"y": 70,
"service": "lambda",
"label": "AWS Lambda"
},
"icon5": {
"x": 64,
"y": 20,
"service": "lambda",
"label": "Lambda@Edge"
},
"icon6": {
"x": 90,
"y": 20,
"service": "lambda",
"label": "Authorizer"
},
"line1": {
"from": "icon4",
"to": "icon1",
"label": ""
},
"line2": {
"from": "icon1",
"to": "icon2",
"label": ""
},
"line3": {
"from": "icon2",
"to": "icon3",
"label": "Get unique key"
},
"line4": {
"from": "icon1",
"to": "icon5",
"label": "Retrieve unique key for header"
},
"line5": {
"from": "icon2",
"to": "icon6",
"label": "Validate key"
}
}
}
Loading

0 comments on commit e902537

Please sign in to comment.