Lambda URL CloudFront WAF
You can associate an existing WAF ACL with the CloudFront distribution.
Here’s an example of how to configure an existing WAF.
config/jets/deploy.rb
Jets.deploy.configure do
config.lambda.url.cloudfront.enable = true
config.lambda.url.cloudfront.cert.arn = acm_cert_arn(domain: "example.com", region: "us-east-1")
config.lambda.url.cloudfront.web_acl_id = web_acl_arn("prod")
end
Note that web_acl_id
expects the arn, so the code above is correct. The setting is called web_acl_id
because it matches with the CloudFormation property. Here’s a useful cheatsheet command to list your WAFs that CloudFront can use.
aws wafv2 list-web-acls --scope=CLOUDFRONT --region=us-east-1 | jq
Assigning an existing WAF ALC allows you to reuse and associate its Rules with multiple CloudFront Distributions. It can help keep your WAF rules DRY. IE: Change the rules once, and they protect all the CloudFront traffic across all apps.
WAF Web ACL must be a global WAF. Regional WAFs do not work with CloudFront. The web_acl_arn
helper can take in an options hash at the end. It defaults {scope: "CLOUDFRONT"}
.
Also note that CloudFront provides built-in baseline protection called AWS Sheild Standard. You get it just by using it.
AWS Shield Standard provides protection for all AWS customers against common and most frequently occurring infrastructure (layer 3 and 4) attacks like SYN/UDP floods, reflection attacks, and others to support high availability of your applications on AWS.
A WAF provides additional protection measures.
Standalone Jets WAF
Jets provides a waf command to create a standalone WAF ACL that is decoupled from the Jets app.
jets waf:deploy
You may want to consider creating a “shared” or “infra” project with the standalone WAF config since the WAF can to be shared by multiple projects.
Some advantages for this:
- It allows you to use the standalone WAF as a shared resource to among all your CloudFront distributions. So if you add a WAF rule, it applies everywhere.
- Each WAF costs money. The WAF ACL has a $5 baseline cost and $1 per rule per ACL. A CloudFront WAF with 3 rules costs $8. Sharing a WAF can save you money.
That being said, you can configure different WAFs for each Jets app if needed.
Configure Jets WAF
You can adjust the default rules or add additional custom-user rules. Example:
config/jets/waf.rb
Jets.deploy.configure do
# change the default rules
config.waf.default_rules = %w[
AWSManagedRulesAmazonIpReputationList
AWSManagedRulesCommonRuleSet
AWSManagedRulesKnownBadInputsRuleSet
]
# add additional custom-user rules
config.waf.rules = []
config.waf.monitoring = false # When true, adjust rules to use Count action to all rules
# config.waf.name = Jets.env # IE: dev
end
Note: The config.waf
can be in config/waf.rb
or config/deploy.rb
. It’s recommended to use waf.rb
to make it clear that it’s for WAF settings. Similarly, you can also have config/waf/dev.rb
and config/waf/prod.rb
.
Jets WAF us-east-1
The Jets WAF is always deployed to us-east-1 because that’s a CloudFront requirement. CloudFront operates out of us-east-1
, so the WAF must also reside there. Regardless of what you set your AWS_REGION to, Jets will switch and use us-east-1
for the WAF standalone stack. The CloudFormation stack can be found in us-east-1
.
Reference
The table below covers each setting. Each option is configured with config.OPTION
. The config.
portion is not shown for conciseness. IE: logger.level
vs config.logger.level
.
Name | Default | Description |
---|---|---|
waf.block_ips.enable | false | Enable blocking of IP addresses. |
waf.block_ips.list | [] | List of IP addresses to block. |
waf.custom_rules.blanket_rate_limiter.action | Block | Action to take when rate limit is reached. |
waf.custom_rules.blanket_rate_limiter.aggregate_key_type | IP | Type of IP address to use. Can be IP or FORWARDED_IP . See: WAF Rule Rate Based Statement |
waf.custom_rules.blanket_rate_limiter.enable | true | Enable blanker rate limiter. Fundamental defense against DDOS. |
waf.custom_rules.blanket_rate_limiter.evaluation_window_sec | 300 | How far back in seconds the WAF should look when it checks how many times the IP address sent a request. |
waf.custom_rules.blanket_rate_limiter.limit | 1000 | Number of requests by the same IP before being rate limited. |
waf.custom_rules.uri_rate_limiter.action | Block | Action to take when rate limit is reached. |
waf.custom_rules.uri_rate_limiter.aggregate_key_type | IP | Type of IP address to use. Can be IP or FORWARDED_IP . See: WAF Rule Rate Based Statement |
waf.custom_rules.uri_rate_limiter.enable | false | Enable url rate limiter. Can be useful to restrict specific URLs to a lower rate limit. IE: /login |
waf.custom_rules.uri_rate_limiter.evaluation_window_sec | 300 | How far back in seconds the WAF should look when it checks how many times the IP address sent a request. |
waf.custom_rules.uri_rate_limiter.limit | 100 | Number of requests by the same IP before being rate limited. |
waf.custom_rules.uri_rate_limiter.logical_statement | “Or” | Logical statement to join the paths logical together. |
waf.custom_rules.uri_rate_limiter.paths | [”/”] | The paths to apply the rate limit rule. IE: ["/logins", "/signgup"] |
waf.custom_rules.uri_rate_limiter.string_match_condition | “STARTS_WITH” | Match condition expression. Examples: CONTAINS EXACTLY STARTS_WITH . See: CloudFormation PositionalConstraint Docs for more |
waf.default_rules | see desc | The default rules curated AWS managed rules and Jets Blanket Rate Limiter rule. See: WAF Default Rules |
waf.logging.enable | false | Turn on WAF logging to CloudWatch. Useful to determine whether or not WAF is blocking a request. |
waf.monitoring | false | When true, adjust rules to use Count action to all rules. This is “Monitoring” mode. |
waf.name | Jets.env | Name of the waf. Defaults to Jets.env. IE: dev or prod |
waf.properties | {} | Properties to override WebACL |
waf.rules | [] | Additional Custom User WAF rules to add. |