Lambda URL CloudFront WAF Jets Blanket Rate Limiter Testing
Here’s an example of the Jets Blanket Rate Limiter in action. We’ve configured the limit to 100, the lowest permitted WAF setting for testing.
config/jets/waf.rb
Jets.deploy.configure do
config.waf.custom_rules.blanket_rate_limiter.enable = true # default is true
config.waf.custom_rules.blanket_rate_limiter.limit = 100 # default 1000
end
Test with ab and curl
You can use a simple tool like ab to get the rate limit.
ab -n 100 -c 10 https://demo-dev.example.com/
After running it twice, you should have exceeded the WAF rate limit of 100.
Then, if you hit it again with curl, you’ll get a 403 response.
$ curl -sv "https://demo-dev.example.com/"
# ...
< HTTP/2 403
< server: CloudFront
< date: Sun, 19 May 2024 22:51:51 GMT
< content-type: text/html
< content-length: 919
< x-cache: Error from cloudfront
< via: 1.1 2e87eef03ab555daefa684d946e111b4.cloudfront.net (CloudFront)
< x-amz-cf-pop: HIO52-P2
< x-amz-cf-id: oLkHaOtOhO4fjqdHAgN-T0YHrtv-C7tAGPT_j05znkYW7Zu8L4kPSw==
<
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<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: oLkHaOtOhO4fjqdHAgN-T0YHrtv-C7tAGPT_j05znkYW7Zu8L4kPSw==
</PRE>
<ADDRESS>
</ADDRESS>
</BODY></HTML>
After a few minutes, your IP’s rate limit resets, and the request will again return 200 response codes.
$ curl -sv "https://demo-dev.example.com/" 2>&1 | grep '< HTTP'
< HTTP/2 200
Check CloudWatch Logs
You can also use CloudWatch Insights to confirm that the WAF blocked the request using the Jets-BlanketRateLimit
rule. Here’s a query to help.
fields @timestamp, @message, action, httpRequest.clientIp, httpRequest.uri, httpRequest.httpMethod, webaclId
| filter action = 'BLOCK'
| sort @timestamp desc
| limit 20
You should find a log entry that looks like this:
{
"timestamp": 1716159111979,
"formatVersion": 1,
"webaclId": "arn:aws:wafv2:us-east-1:112233445566:global/webacl/dev/d8ebd2fe-ddcd-4830-a240-6ac3786a9407",
"terminatingRuleId": "Jets-BlanketRateLimit",
"terminatingRuleType": "RATE_BASED",
"action": "BLOCK",
"terminatingRuleMatchDetails": [],
"httpSourceName": "CF",
"httpSourceId": "E2PPB3A2M07L7U",
"ruleGroupList": [],
"rateBasedRuleList": [
{
"rateBasedRuleId": "arn:aws:wafv2:us-east-1:112233445566_MANAGED:global/ipset/d8ebd2fe-ddcd-4830-a240-6ac3786a9407_0439adc1-7fc6-4e79-ba7f-2baa80ef7e8e_IPV4/0439adc1-7fc6-4e79-ba7f-2baa80ef7e8e",
"rateBasedRuleName": "Jets-BlanketRateLimit",
"limitKey": "IP",
"maxRateAllowed": 100,
"evaluationWindowSec": 300,
"limitValue": "52.34.100.192"
}
],
"nonTerminatingMatchingRules": [],
"requestHeadersInserted": null,
"responseCodeSent": null,
"httpRequest": {
"clientIp": "52.34.100.192",
"country": "US",
"headers": [
{
"name": "host",
"value": "demo-dev.example.com"
},
{
"name": "user-agent",
"value": "curl/7.81.0"
},
{
"name": "accept",
"value": "*/*"
}
],
"uri": "/",
"args": "",
"httpVersion": "HTTP/2.0",
"httpMethod": "GET",
"requestId": "oLkHaOtOhO4fjqdHAgN-T0YHrtv-C7tAGPT_j05znkYW7Zu8L4kPSw=="
},
"ja3Fingerprint": "4ea056e63b7910cbf543f0c095064dfe"
}