How to set up a rate-based rule in AWS WAF

This blog post explains how to set up a rate-based rule in AWS WAF with screenshots.
2022.03.27

This is an English translated version of the following article.

Question

I have been experiencing a DDoS attack on our service for several days. In the attack, hackers use hundreds of spoofed IP addresses. How can I automatically add the IP addresses to a blocklist?

Solution

You can set up a rate-based rule in AWS WAF. Alternatively, you could use AWS WAF Security Automations for fine-grained controls.

What is a rate-based rule in AWS WAF?

Once you set a rate-based rule, AWS WAF counts the number of source IP addresses requests and blocks the IPs automatically if their numbers exceed a threshold value you specified. As of now, the minimum threshold is 100 requests per 5 minutes.

How to make a Web ACL and a rate-based rule

On AWS WAF console page, click "Create web ACL".

AWS WAF コンソール画面

You need to input details of the web ACL. At first, you choose the name of the web ACL(CloudWatch Metric name will be automatically added as the same name). Then, for Resource Type, select an option you would like to associate with this web ACL.

At this time, I selected "Regional resources" for ALB in the Oregon region. Also, I chose "US West(Oregon)" as the region.

WebACL詳細

Next, you need to choose associated AWS resources. Click "Add AWS resources".

Associated AWS resources

After choosing "Application Load Balancer", check a box next to the ALB you would like to associate with the web ACL. Then click "Add" and "Next".

Add AWS resources

For the next step, set up a rule. Choose "Add my own rules and rule groups".

Add rules and rule group

As a rule type, select "Rule Builder". Name the rule and choose "Rate-based rule" as the type.

Rule Builder

About this time, I set "100" for the rate limit in order to be blocked more than 100 accesses per 5 minutes and chose "source IP address" as the IP address to use for rate limiting. Also, chose "Only consider requests that match the criteria in a rule statement" since I would like to set this rule for the path "/admin".

レート制限詳細

Then, configure a statement. Choose "matches the statement" to be matched only the statement. After that, select "URI path" as the inspect and "Starts with string" as the match type. The string to match is set "/admin" that I mentioned above.

一致条件

Choose "Block" action and click "Add rule".

Add rule

On the next page, the capacity to be used by this rate-based rule is shown. For this rule, it's "4". Set the default web ACL action for requests that don't match any rules to "Allow" and click "Next".

rule-capacity

After that, you can set rule priority, but there is only one rule so click "Next".

ルールの優先度

Then, configure metrics. If needed, you can change the corresponding CloudWatch metric name. Also, there is an option to show sampled requests matched with the rule on WAF console. Choose "Enable sampled requests" this time.

WAFメトリクスの設定

Finally, review every configuration. Click "Create web ACL" if it is acceptable. Once Web ACL is created, test if the rate-based rule works fine without any issues.

Web ACL 完成しました

Test the rate-based rule

Launch a Linux EC2 instance on Tokyo region and connect to the instance using SSH. Then, run curl command on the instance to the ALB associated with the WAF. I confirmed that 200 OK was returned as follows.

$ curl -I http://ttttt-403999499.us-west-2.elb.amazonaws.com/admin/
HTTP/1.1 200 OK
Date: Thu, 20 May 2021 17:43:16 GMT
Content-Type: text/html;charset=ISO-8859-1
Connection: keep-alive
Server: Apache/2.4.46 ()
Upgrade: h2,h2c

Next, send 200 requests to the ALB using ApacheBench. For the first attempt, no request was not blocked, as you can see.

$ ab -n 200 -c 1 http://ttttt-403999499.us-west-2.elb.amazonaws.com/admin/
(Omitted)
Complete requests:      200
Failed requests:        0
Total transferred:      209851 bytes

Right after the attempt, I executed the same command again. 187 requests were blocked as a result.

$ ab -n 200 -c 1 http://ttttt-403999499.us-west-2.elb.amazonaws.com/admin/
(Omitted)
Complete requests:      200
Failed requests:        187
   (Connect: 0, Receive: 0, Length: 187, Exceptions: 0)
Non-2xx responses:      187

For the third test, All requests were blocked successfully!

$ ab -n 200 -c 1 http://ttttt-403999499.us-west-2.elb.amazonaws.com/admin/
(Omitted)
Complete requests:      200
Failed requests:        0
Non-2xx responses:      200

I executed the curl command again from the EC2 instance and confirmed 403 error was returned.

$ curl -I http://ttttt-403999499.us-west-2.elb.amazonaws.com/admin/
HTTP/1.1 403 Forbidden
Server: awselb/2.0
Date: Thu, 20 May 2021 17:50:11 GMT
Content-Type: text/html
Content-Length: 118
Connection: keep-alive

You can see which IP address is blocked by the rate-based rule using AWS CLI get-rate-based-statement-managed-keys command as follows. Set "REGIONAL" as scope since Web ACL is associated with ALB. Then, input other options and run the command.

$ aws wafv2 get-rate-based-statement-managed-keys \
    --scope REGIONAL \
    --web-acl-name waf0512 \
    --web-acl-id b0171f79-7455-4533-94fd-e2463392512f \
    --rule-name Rate-based-rule-with-address
    
{
    "ManagedKeysIPV4": {
        "IPAddressVersion": "IPV4",
        "Addresses": [
            "13.113.177.16/32"
        ]
    },
    "ManagedKeysIPV6": {
        "IPAddressVersion": "IPV6",
        "Addresses": []
    }
}

After a certain time, I ran get-rate-based-statement-managed-keys command again. The IP address was released from the denylist.

Reference