Skip to content

Commit

Permalink
Enable country selection filter for AWS WAF (#180)
Browse files Browse the repository at this point in the history
  • Loading branch information
OlamideOl1 authored Mar 12, 2024
1 parent 6b035e5 commit 23d5d4a
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 48 deletions.
2 changes: 1 addition & 1 deletion aws/ingress/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ module "ingress" {
| <a name="input_waf_allowed_ip_list"></a> [waf\_allowed\_ip\_list](#input\_waf\_allowed\_ip\_list) | Applicable if WAF is enabled. List of allowed IP addresses, these IP addresses will be exempted from any configured rules | `list(string)` | `[]` | no |
| <a name="input_waf_aws_managed_rule_groups"></a> [waf\_aws\_managed\_rule\_groups](#input\_waf\_aws\_managed\_rule\_groups) | Applicable if WAF is enabled. Rule statement values used to run the rules that are defined in a managed rule group. You may review this list for the available AWS managed rule groups - https://docs.aws.amazon.com/waf/latest/developerguide/aws-managed-rule-groups-list.html | <pre>map(object({<br> name = string # Name of the Managed rule group<br> priority = number # Relative processing order for rules processed by AWS WAF. All rules are processed from lowest priority to the highest.<br> count_override = optional(bool, true) # If true, this will override the rule action setting to `count`, if false, the rule action will be set to `block`.<br> }))</pre> | <pre>{<br> "rule_five": {<br> "name": "AWSManagedRulesUnixRuleSet",<br> "priority": 60<br> },<br> "rule_four": {<br> "name": "AWSManagedRulesLinuxRuleSet",<br> "priority": 50<br> },<br> "rule_one": {<br> "name": "AWSManagedRulesAmazonIpReputationList",<br> "priority": 20<br> },<br> "rule_six": {<br> "name": "AWSManagedRulesBotControlRuleSet",<br> "priority": 70<br> },<br> "rule_three": {<br> "name": "AWSManagedRulesSQLiRuleSet",<br> "priority": 40<br> },<br> "rule_two": {<br> "name": "AWSManagedRulesKnownBadInputsRuleSet",<br> "priority": 30<br> }<br>}</pre> | no |
| <a name="input_waf_block_ip_list"></a> [waf\_block\_ip\_list](#input\_waf\_block\_ip\_list) | Applicable if WAF is enabled. List of IP addresses to be blocked and denied access to the ingress / cloudfront. | `list(string)` | `[]` | no |
| <a name="input_waf_rate_limit"></a> [waf\_rate\_limit](#input\_waf\_rate\_limit) | Applicable if WAF is enabled. Rule statement to track and rate limits requests when they are coming at too fast a rate.. For more details, visit - https://docs.aws.amazon.com/waf/latest/developerguide/aws-managed-rule-groups-list.html | <pre>object({<br> Priority = number # Relative processing order for rate limit rule relative to other rules processed by AWS WAF.<br> Limit = optional(number, 1000) # This is the limit on requests from any single IP address within a 5 minute period<br> count_override = optional(bool, false) # If true, this will override the rule action setting to `count`, if false, the rule action will be set to `block`. Default value is false.<br> })</pre> | <pre>{<br> "Limit": 1000,<br> "Priority": 10<br>}</pre> | no |
| <a name="input_waf_rate_limit"></a> [waf\_rate\_limit](#input\_waf\_rate\_limit) | Applicable if WAF is enabled. Rule statement to track and rate limits requests when they are coming at too fast a rate.. For more details, visit - https://docs.aws.amazon.com/waf/latest/developerguide/aws-managed-rule-groups-list.html | <pre>map(object({<br> name = string # Name of the Rate limit rule group<br> priority = number # Relative processing order for rate limit rule relative to other rules processed by AWS WAF.<br> limit = optional(number, 2000) # This is the limit on requests from any single IP address within a 5 minute period<br> count_override = optional(bool, false) # If true, this will override the rule action setting to `count`, if false, the rule action will be set to `block`. Default value is false.<br> country_list = optional(list(string), []) # List of countries to apply the rate limit to. If populated, from other countries will be ignored by this rule. IF empty, the rule will apply to all traffic.<br> exempt_country_list = optional(list(string), []) # List of countries to exempt from the rate limit. If populated, the selected countries will be ignored by this rule. IF empty, the rule will apply to all traffic.<br> }))</pre> | <pre>{<br> "default_rule": {<br> "limit": 2000,<br> "name": "General",<br> "priority": 10<br> }<br>}</pre> | no |

## Outputs

Expand Down
2 changes: 1 addition & 1 deletion aws/ingress/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ module "waf" {
name = "${var.name}-waf"
resource_arn = module.alb.instance.arn
aws_managed_rule_groups = var.waf_aws_managed_rule_groups
rate_limit = var.waf_rate_limit
rate_limit_rules = var.waf_rate_limit

allowed_ip_list = var.waf_allowed_ip_list
block_ip_list = var.waf_block_ip_list
Expand Down
20 changes: 13 additions & 7 deletions aws/ingress/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -158,13 +158,19 @@ variable "waf_aws_managed_rule_groups" {

variable "waf_rate_limit" {
description = "Applicable if WAF is enabled. Rule statement to track and rate limits requests when they are coming at too fast a rate.. For more details, visit - https://docs.aws.amazon.com/waf/latest/developerguide/aws-managed-rule-groups-list.html"
type = object({
Priority = number # Relative processing order for rate limit rule relative to other rules processed by AWS WAF.
Limit = optional(number, 1000) # This is the limit on requests from any single IP address within a 5 minute period
count_override = optional(bool, false) # If true, this will override the rule action setting to `count`, if false, the rule action will be set to `block`. Default value is false.
})
type = map(object({
name = string # Name of the Rate limit rule group
priority = number # Relative processing order for rate limit rule relative to other rules processed by AWS WAF.
limit = optional(number, 2000) # This is the limit on requests from any single IP address within a 5 minute period
count_override = optional(bool, false) # If true, this will override the rule action setting to `count`, if false, the rule action will be set to `block`. Default value is false.
country_list = optional(list(string), []) # List of countries to apply the rate limit to. If populated, from other countries will be ignored by this rule. IF empty, the rule will apply to all traffic.
exempt_country_list = optional(list(string), []) # List of countries to exempt from the rate limit. If populated, the selected countries will be ignored by this rule. IF empty, the rule will apply to all traffic.
}))
default = {
Priority = 10
Limit = 1000
default_rule = {
name = "General"
priority = 10
limit = 2000
}
}
}
34 changes: 27 additions & 7 deletions aws/waf/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,36 @@ module "aws_waf" {
aws_managed_rule_groups = {
rule_one = {
name = "AWSManagedRulesAmazonIpReputationList"
priority = 20
priority = 30
}
rule_two = {
name = "AWSManagedRulesAmazonIpReputationList"
priority = 40
count_override = false # Set count override to false to enable rule in blocking mode
}
rule_three = {
name = "AWSManagedRulesAmazonIpReputationList"
priority = 50
country_list = ["US"] # Set rule for only traffic from the US
count_override = false # Set count override to false to enable rule in blocking mode
}
rule_four = {
name = "AWSManagedRulesKnownBadInputsRuleSet"
priority = 30
priority = 60
}
}
rate_limit = {
Priority = 10
Limit = 1000 # The limit on requests from any single IP address within a 5 minute period
rate_limit_rules = {
country_specific_rate_limit = {
name = "Country-Specific"
priority = 10
limit = 2000 # The limit on requests from any single IP address within a 5 minute period
country_list = ["US"] # Set rate limit rule for only traffic from the US
}
general_rate_limit = {
name = "General"
priority = 20
limit = 1000 # The limit on requests from any single IP address within a 5 minute period
}
}
}
```
Expand Down Expand Up @@ -58,10 +78,10 @@ module "aws_waf" {
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_allowed_ip_list"></a> [allowed\_ip\_list](#input\_allowed\_ip\_list) | List of allowed IP addresses, these IP addresses will be exempted from any configured rules | `list(string)` | `[]` | no |
| <a name="input_aws_managed_rule_groups"></a> [aws\_managed\_rule\_groups](#input\_aws\_managed\_rule\_groups) | Rule statement values used to run the rules that are defined in a managed rule group. You may review this list for the available AWS managed rule groups - https://docs.aws.amazon.com/waf/latest/developerguide/aws-managed-rule-groups-list.html | <pre>map(object({<br> name = string # Name of the Managed rule group<br> priority = number # Relative processing order for rules processed by AWS WAF. All rules are processed from lowest priority to the highest.<br> count_override = optional(bool, true) # If true, this will override the rule action setting to `count`, if false, the rule action will be set to `block`.<br> }))</pre> | n/a | yes |
| <a name="input_aws_managed_rule_groups"></a> [aws\_managed\_rule\_groups](#input\_aws\_managed\_rule\_groups) | Rule statement values used to run the rules that are defined in a managed rule group. You may review this list for the available AWS managed rule groups - https://docs.aws.amazon.com/waf/latest/developerguide/aws-managed-rule-groups-list.html | <pre>map(object({<br> name = string # Name of the Managed rule group<br> priority = number # Relative processing order for rules processed by AWS WAF. All rules are processed from lowest priority to the highest.<br> count_override = optional(bool, true) # If true, this will override the rule action setting to `count`, if false, the rule action will be set to `block`.<br> country_list = optional(list(string), []) # List of countries to apply the managed rule to. If populated, from other countries will be ignored by this rule. IF empty, the rule will apply to all traffic.<br> exempt_country_list = optional(list(string), []) # List of countries to exempt from the managed rule. If populated, the selected countries will be ignored by this rule. IF empty, the rule will apply to all traffic.<br> }))</pre> | n/a | yes |
| <a name="input_block_ip_list"></a> [block\_ip\_list](#input\_block\_ip\_list) | List of IP addresses to be blocked and denied access to the ingress / cloudfront. | `list(string)` | `[]` | no |
| <a name="input_name"></a> [name](#input\_name) | Friendly name of the WebACL. | `string` | n/a | yes |
| <a name="input_rate_limit"></a> [rate\_limit](#input\_rate\_limit) | Rule statement to track and rate limits requests when they are coming at too fast a rate.. For more details, visit - https://docs.aws.amazon.com/waf/latest/developerguide/aws-managed-rule-groups-list.html | <pre>object({<br> Priority = number # Relative processing order for rate limit rule relative to other rules processed by AWS WAF.<br> Limit = optional(number, 1000) # This is the limit on requests from any single IP address within a 5 minute period<br> count_override = optional(bool, false) # If true, this will override the rule action setting to `count`, if false, the rule action will be set to `block`. Default value is false.<br> })</pre> | n/a | yes |
| <a name="input_rate_limit_rules"></a> [rate\_limit\_rules](#input\_rate\_limit\_rules) | Rule statement to track and rate limits requests when they are coming at too fast a rate.. For more details, visit - https://docs.aws.amazon.com/waf/latest/developerguide/aws-managed-rule-groups-list.html | <pre>map(object({<br> name = string # Name of the Rate limit rule group<br> priority = number # Relative processing order for rate limit rule relative to other rules processed by AWS WAF.<br> limit = optional(number, 2000) # This is the limit on requests from any single IP address within a 5 minute period<br> count_override = optional(bool, false) # If true, this will override the rule action setting to `count`, if false, the rule action will be set to `block`. Default value is false.<br> country_list = optional(list(string), []) # List of countries to apply the rate limit to. If populated, from other countries will be ignored by this rule. IF empty, the rule will apply to all traffic.<br> exempt_country_list = optional(list(string), []) # List of countries to exempt from the rate limit. If populated, the selected countries will be ignored by this rule. IF empty, the rule will apply to all traffic.<br> }))</pre> | n/a | yes |
| <a name="input_resource_arn"></a> [resource\_arn](#input\_resource\_arn) | The Amazon Resource Name (ARN) of the resource to associate with the web ACL. This must be an ARN of an Application Load Balancer or an Amazon API Gateway stage. Value is required if scope is REGIONAL | `string` | `null` | no |
| <a name="input_waf_scope"></a> [waf\_scope](#input\_waf\_scope) | Specifies whether this is for an AWS CloudFront distribution or for a regional application. Valid values are CLOUDFRONT or REGIONAL. | `string` | `"REGIONAL"` | no |

Expand Down
105 changes: 82 additions & 23 deletions aws/waf/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -13,33 +13,63 @@ resource "aws_wafv2_web_acl" "main" {
metric_name = "${var.name}-cloudfront-web-acl"
}

rule {
name = "${var.name}-IP-Ratelimit"
priority = var.rate_limit["Priority"]
dynamic "rule" {
for_each = var.rate_limit_rules
content {
name = "${rule.value["name"]}-IP-Ratelimit"
priority = rule.value["priority"]

dynamic "action" {
for_each = var.rate_limit["count_override"] == true ? [1] : []
content {
count {}
dynamic "action" {
for_each = rule.value["count_override"] == true ? [1] : []
content {
count {}
}
}
}
dynamic "action" {
for_each = var.rate_limit["count_override"] == false ? [1] : []
content {
block {}
dynamic "action" {
for_each = rule.value["count_override"] == false ? [1] : []
content {
block {}
}
}
}

statement {
rate_based_statement {
limit = var.rate_limit["Limit"]
aggregate_key_type = "IP"
statement {
rate_based_statement {
limit = rule.value["limit"]
aggregate_key_type = "IP"

dynamic "scope_down_statement" {
for_each = length(concat(rule.value["country_list"], rule.value["exempt_country_list"])) > 0 ? [1] : []
content {
and_statement {
dynamic "statement" {
for_each = length(rule.value["country_list"]) > 0 ? [1] : []
content {
geo_match_statement {
country_codes = rule.value["country_list"]
}
}
}
dynamic "statement" {
for_each = length(rule.value["exempt_country_list"]) > 0 ? [1] : []
content {
not_statement {
statement {
geo_match_statement {
country_codes = rule.value["exempt_country_list"]
}
}
}
}
}
}
}
}
}
}
visibility_config {
cloudwatch_metrics_enabled = true
sampled_requests_enabled = true
metric_name = "${rule.value["name"]}-IP-Ratelimit"
}
}
visibility_config {
cloudwatch_metrics_enabled = true
sampled_requests_enabled = true
metric_name = "${var.name}-IP-Ratelimit"
}
}

Expand Down Expand Up @@ -105,8 +135,37 @@ resource "aws_wafv2_web_acl" "main" {
managed_rule_group_statement {
name = rule.value["name"]
vendor_name = "AWS"

dynamic "scope_down_statement" {
for_each = length(concat(rule.value["country_list"], rule.value["exempt_country_list"])) > 0 ? [1] : []
content {
and_statement {
dynamic "statement" {
for_each = length(rule.value["country_list"]) > 0 ? [1] : []
content {
geo_match_statement {
country_codes = rule.value["country_list"]
}
}
}
dynamic "statement" {
for_each = length(rule.value["exempt_country_list"]) > 0 ? [1] : []
content {
not_statement {
statement {
geo_match_statement {
country_codes = rule.value["exempt_country_list"]
}
}
}
}
}
}
}
}
}
}

visibility_config {
cloudwatch_metrics_enabled = true
sampled_requests_enabled = true
Expand Down
Loading

0 comments on commit 23d5d4a

Please sign in to comment.