Protect resource with WAF rate-based rules

Protect resource with WAF rate-based rules. SOA_EN

Protecting Resources (CloudFront) with WAF Rate-Based Rules

This is one of the AWS SOA topics related to networking and content delivery.

The WAF Web ACL allows you to define a variety of rules, one of which is a rate-based rule.

A rate-based rule tracks the rate of requests for each originating IP address, and triggers the rule action on IPs with rates that go over a limit. You set the limit as the number of requests per 5-minute time span.

Rate-based rule statement

By creating rate-based rules, you can protect your resources from DoS attacks.

In this case, CloudFront is the target for applying WAF.
However, other resources (ALB, API Gateway, AppSync) can also be configured in the same way.


Diagram of protect resource with WAF rate-based rules.

Create a WAF Web ACL.
Create rate-based rules.
Apply the created Web ACL to CloudFront.

Create a CloudFront distribution.
Specify an S3 bucket as the origin.
Create an OAI in CloudFront and restrict the S3 bucket policy to allow access only from the OAI.

Create a Lambda function.
Configure this function as a CloudFormation custom resource.
The function’s function is to automatically create and delete objects in the bucket when creating and deleting stacks.
The object to be created is index.html for static website hosting.
The runtime for the function is Python 3.8.

Create a VPC and prepare two subnets.
One is a private subnet, and an EC2 instance is placed inside.
The other is a public subnet, and a NAT gateway is deployed.
The instance will be created on the latest version of Amazon Linux 2 and used as a client to access the CloudFront distribution.
Apache Bench will be used to test rate-based rules.

CloudFormation template files

The above configuration is built with CloudFormation.
The CloudFormation templates are located at the following URL

awstut-soa/05/002 at main · awstut-an-r/awstut-soa
Contribute to awstut-an-r/awstut-soa development by creating an account on GitHub.

Explanation of key points of template files

This page focuses on how to define rate-based rules for WAF Web ACLs.

For more information on WAF basics, please refer to the following page

For information on how to apply WAF to CloudFront, please refer to the following page

For information on custom resources in CloudFormation, please refer to the following page

Rate-based WAF Web ACL Rules

Resources: RuleGroup: Type: AWS::WAFv2::RuleGroup Properties: Capacity: 10 Name: !Sub "${Prefix}-RateRestrictionRuleGroup" Rules: - Action: Block: {} Name: !Sub "${Prefix}-RateRestrictionRule" Priority: 0 Statement: RateBasedStatement: AggregateKeyType: IP Limit: !Ref Limit VisibilityConfig: CloudWatchMetricsEnabled: true MetricName: !Sub "${Prefix}-RateRestrictionRule" SampledRequestsEnabled: true Scope: CLOUDFRONT VisibilityConfig: CloudWatchMetricsEnabled: true MetricName: !Sub "${Prefix}-RateRestrictionRuleGroup" SampledRequestsEnabled: true
Code language: YAML (yaml)

This time we define a rate-based rule in the rule group.
The key point is the RateBasedStatement property.
Use this property when creating a rate-based rule.

The AggregateKeyType property sets the target for counting requests.
Specify “IP” to count based on the IP address of the request originator, or “FORWARDED_IP” to count based on the IP address included in the HTTP header.
In this case, the former is selected.

Set the upper limit of the count with the “Limit” property.
Set the minimum value of this property to “100”.
In other words, if the number of requests from a specific IP address exceeds 100 during a 5-minute period, access from that IP address will be blocked.

(Reference) EC2 instance for verification

Resources: Instance: Type: AWS::EC2::Instance Properties: IamInstanceProfile: !Ref InstanceProfile ImageId: !Ref ImageId InstanceType: !Ref InstanceType NetworkInterfaces: - DeviceIndex: 0 SubnetId: !Ref PrivateSubnet GroupSet: - !Ref InstanceSecurityGroup UserData: !Base64 | #!/bin/bash -xe yum update -y yum install -y httpd
Code language: YAML (yaml)

No special configuration is required.
Define the initialization process for the instance in the user data.
By installing Apache, Apache Bench will also be installed together.

For more information on the instance initialization process, please refer to the following page


Use CloudFormation to build this environment and check the actual behavior.

Create CloudFormation stacks and check resources in stacks

Create CloudFormation stacks.
For information on how to create stacks and check each stack, please refer to the following page

This time, be careful which region you create the stack in.
When creating a stack with the AWS CLI, specify the us-east-1 region in the region option as shown in the command below.
This is because in order to apply WAF to CloudFront, WAF must be created in the Virginia region.

$ aws cloudformation create-stack \ --stack-name soa-05-002 \ --template-url https://[bucket-name].s3.[region][folder-name]/soa-05-002.yaml \ --capabilities CAPABILITY_IAM \ --region us-east-1
Code language: Bash (bash)

After checking the resources in each stack, information on the main resources created in this case is as follows

  • S3 bucket: soa-05-002
  • CloudFront distribution domain:
  • Name of WAF Web ACL: soa-05-002-WebACL
  • ID of WAF Web ACL: 2cec36bc-ec03-4e00-b70a-aab7cb984c7a
  • Name of the rule in the WAF Web ACL: soa-05-002-WebACL-RateRestriction
  • Name of the WAF rule group: soa-05-002-RateRestrictionRuleGroup
  • Name of the rule created in the WAF rule group: soa-05-002-RateRestrictionRule
  • ID of the EC2 instance: i-0e5e54ddc051b5f55
  • EIP attached to the NAT gateway:

We also check the resources from the AWS Management Console.
First is CloudFront.

WAF is applied to CloudFront.

We can see that a CloudFront distribution has been created and AWS WAF has been applied.

Next, check the Web ACL.

WAF is applied to CloudFront.

From the Web ACL side, we can also see that this ACL has been applied to CloudFront.

Next, check the rules created in the Web ACL.

Detail of WAF Web ACL.

The rules are defined in the form of references to rule groups.

We also check the contents of the rule.

Details of WAF rate-based rules.

It is a rate-based rule, counting requests based on IP addresses, and the limit is set to 100.

Checking Action

Now that everything is ready, access the EC2 instance that is the client for verification.
Use SSM Session Manager for access.

% aws ssm start-session \ --target i-0e5e54ddc051b5f55 \ --region us-east-1 Starting session with SessionId: root-09545bd848d2d2fae sh-4.2$
Code language: Bash (bash)

For more information on SSM Session Manager, please visit

Access the CloudFront distribution in this situation.

sh-4.2$ curl <html> <head></head> <body> <h1>index.html</h1> <p>soa-05-002</p> </body> </html>
Code language: Bash (bash)

The index.html placed in the S3 bucket, which is the origin of CloudFront, is returned.

Check the status of WAF at this time.

Traffic from the EIP is allowed.

Requests from EIP attached to the NAT gateway are allowed.
At this point, there are no restrictions enabled by rate-based rules, which means that access is allowed as usual.

Next, use Apache Bench to reproduce the spike in the number of requests.

sh-4.2$ ab -n 100
Code language: JavaScript (javascript)

Perform 100 requests to the CloudFront distribution URL.

Check the status of the WAF.

Traffic is allowed from EIPs generated by Apache bench.

You can see that it is receiving a large number of requests from the EIP attached to the NAT gateway.
This means that the rate-based rule limit has been reached.

Access CloudFront again.

sh-4.2$ curl <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" ""> <HTML><HEAD><META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1"> <TITLE>ERROR: The request could not be satisfied</TITLE> </HEAD><BODY> <H1>403 ERROR</H1> <H2>The request could not be satisfied.</H2> <HR noshade size="1px"> Request blocked. 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. <BR clear="all"> If you provide content to customers through CloudFront, you can find steps to troubleshoot and help prevent this error by reviewing the CloudFront documentation. <BR clear="all"> <HR noshade size="1px"> <PRE> Generated by cloudfront (CloudFront) Request ID: 1ULqdHi16O_YVBoiAcGSy6_cc8_Lhw7TyWlxsJ0OEEk5Tb7CTRsrNA== </PRE> <ADDRESS> </ADDRESS> </BODY></HTML>
Code language: Bash (bash)

This time an error page is returned.

We also check the WAF.

Traffic from EIP is blocked.

This time we see that it was blocked.
The rate-based rule means that the EIP attached to the NAT gateway has blocked the request.

You can check the blocked IP addresses from the AWS CLI.

$ aws wafv2 get-rate-based-statement-managed-keys \ --scope CLOUDFRONT \ --region us-east-1 \ --web-acl-name soa-05-002-WebACL \ --web-acl-id 2cec36bc-ec03-4e00-b70a-aab7cb984c7a \ --rule-group-rule-name soa-05-002-WebACL-RateRestriction \ --rule-name soa-05-002-RateRestrictionRule { "ManagedKeysIPV4": { "IPAddressVersion": "IPV4", "Addresses": [ "" ] }, "ManagedKeysIPV6": { "IPAddressVersion": "IPV6", "Addresses": [] } }
Code language: Bash (bash)

Blocked IP addresses can be checked from the AWS CLI.

Listing IP addresses blocked by rate-based rules - AWS WAF, AWS Firewall Manager, and AWS Shield Advanced
Find out how to access the list of IP addresses that are currently blocked by a rate-based rule by using the CLI, the API, or any of the SDKs.

The details are detailed in the following page, there are three key points.

get-rate-based-statement-managed-keys — AWS CLI 1.25.17 Command Reference

The first is the region option.
If “CLOUDFRONT” is specified for the scope option, the region option must also be specified and its value must be “us-east-1”.

The second is the rule-group-rule-name option.
This option is mandatory when using rule groups to create Web ACLs.
The value of this option specifies the rule name defined within the WAF Web ACL.

The third is the rule-name option.
If you are using rule groups, the value of this option specifies the rule name as defined within the rule group.

Rate-based groups are actioned based on the number of requests per 5 minutes.
So allow some time and check the list of blocked IP addresses.

$ aws wafv2 get-rate-based-statement-managed-keys \ --scope CLOUDFRONT \ --region us-east-1 \ --web-acl-name soa-05-002-WebACL \ --web-acl-id 2cec36bc-ec03-4e00-b70a-aab7cb984c7a \ --rule-group-rule-name soa-05-002-WebACL-RateRestriction \ --rule-name soa-05-002-RateRestrictionRule { "ManagedKeysIPV4": { "IPAddressVersion": "IPV4", "Addresses": [] }, "ManagedKeysIPV6": { "IPAddressVersion": "IPV6", "Addresses": [] } }
Code language: Bash (bash)

EIP has disappeared.
This means that the EIP is no longer subject to rate-based rules.

Finally, access CloudFront again.

sh-4.2$ curl <html> <head></head> <body> <h1>index.html</h1> <p>soa-05-002</p> </body> </html>
Code language: Bash (bash)

index.hml was returned.
This means that the restriction by the rate-based rule has been lifted and the file is now accessible again.


We have reviewed how to create rate-based rules in the WAF Web ACL.