Skip to content

Commit f16dd80

Browse files
authored
Merge pull request #1 from oozou/develop
Support Default Rule & Rate Limit Rule
2 parents 729ac8c + 71279f8 commit f16dd80

File tree

7 files changed

+303
-61
lines changed

7 files changed

+303
-61
lines changed

CHANGELOG.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Change Log
2+
3+
All notable changes to this module will be documented in this file.
4+
5+
## [1.0.1] - 2022-07-20
6+
7+
### Added
8+
9+
- support monitoring
10+
11+
- variables
12+
- `is_enable_default_rule`
13+
- `is_enable_cloudwatch_metrics`
14+
- `is_enable_sampled_requests`
15+
- `ip_rate_based_rule`
16+
- `is_create_logging_configuration`
17+
- `redacted_fields`
18+
- `logging_filter`
19+
20+
### Changed
21+
22+
- move default rule from variables to locals.tf
23+
24+
## [1.0.0] - 2022-05-31
25+
26+
### Added
27+
28+
- init terraform-aws-waf module

README.md

Lines changed: 63 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,21 @@ module "waf" {
99
source = "git::ssh://git@github.com:oozou/terraform-aws-waf.git"
1010
name = "test-waf"
1111
prefix = "oozou"
12+
is_enable_default_rule = true
13+
is_enable_sampled_requests = false
14+
is_enable_cloudwatch_metrics = false
15+
is_create_logging_configuration = true
1216
scope = "CLOUDFRONT"
1317
environment = "dev"
14-
ip_sets_rule = [
18+
managed_rules = [
1519
{
16-
name = "count-ip-set"
17-
priority = 5
18-
action = "count"
19-
ip_address_version = "IPV4"
20-
ip_set = ["1.2.3.4/32", "5.6.7.8/32"]
21-
},
20+
name = "AWSManagedRulesAdminProtectionRuleSet",
21+
priority = 60
22+
override_action = "none"
23+
excluded_rules = []
24+
}
25+
]
26+
ip_sets_rule = [
2227
{
2328
name = "block-ip-set"
2429
priority = 6
@@ -27,21 +32,55 @@ module "waf" {
2732
ip_set = ["10.0.1.1/32"]
2833
}
2934
]
35+
ip_rate_based_rule = {
36+
name : "ip-rate-limit",
37+
priority : 7,
38+
action : "block",
39+
limit : 100
40+
}
41+
redacted_fields = [
42+
{
43+
single_header = {
44+
name = "user-agent"
45+
}
46+
}
47+
]
48+
49+
logging_filter = {
50+
default_behavior = "DROP"
51+
filter = [
52+
{
53+
behavior = "KEEP"
54+
requirement = "MEETS_ANY"
55+
condition = [
56+
{
57+
action_condition = {
58+
action = "ALLOW"
59+
}
60+
},
61+
]
62+
}
63+
]
64+
}
65+
association_resources = "arn:xxxxx"
3066
tags = {
3167
"Custom-Tag" = "1"
3268
}
3369
}
3470
```
3571

72+
<!-- BEGIN_TF_DOCS -->
3673
## Requirements
3774

38-
No requirements.
75+
| Name | Version |
76+
|------|---------|
77+
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | 4.8.0 |
3978

4079
## Providers
4180

4281
| Name | Version |
4382
|------|---------|
44-
| <a name="provider_aws"></a> [aws](#provider\_aws) | n/a |
83+
| <a name="provider_aws"></a> [aws](#provider\_aws) | 4.8.0 |
4584

4685
## Modules
4786

@@ -51,21 +90,30 @@ No modules.
5190

5291
| Name | Type |
5392
|------|------|
54-
| [aws_wafv2_ip_set.ipset](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/wafv2_ip_set) | resource |
55-
| [aws_wafv2_web_acl.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/wafv2_web_acl) | resource |
56-
| [aws_wafv2_web_acl_association.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/wafv2_web_acl_association) | resource |
93+
| [aws_cloudwatch_log_group.this](https://registry.terraform.io/providers/hashicorp/aws/4.8.0/docs/resources/cloudwatch_log_group) | resource |
94+
| [aws_wafv2_ip_set.ipset](https://registry.terraform.io/providers/hashicorp/aws/4.8.0/docs/resources/wafv2_ip_set) | resource |
95+
| [aws_wafv2_web_acl.this](https://registry.terraform.io/providers/hashicorp/aws/4.8.0/docs/resources/wafv2_web_acl) | resource |
96+
| [aws_wafv2_web_acl_association.this](https://registry.terraform.io/providers/hashicorp/aws/4.8.0/docs/resources/wafv2_web_acl_association) | resource |
97+
| [aws_wafv2_web_acl_logging_configuration.main](https://registry.terraform.io/providers/hashicorp/aws/4.8.0/docs/resources/wafv2_web_acl_logging_configuration) | resource |
5798

5899
## Inputs
59100

60101
| Name | Description | Type | Default | Required |
61102
|------|-------------|------|---------|:--------:|
62103
| <a name="input_association_resources"></a> [association\_resources](#input\_association\_resources) | ARN of the ALB, CloudFront, Etc to be associated with the WAFv2 ACL. | `list(string)` | `[]` | no |
63-
| <a name="input_default_action"></a> [default\_action](#input\_default\_action) | The action to perform if none of the rules contained in the WebACL match. | `string` | `"allow"` | no |
104+
| <a name="input_default_action"></a> [default\_action](#input\_default\_action) | The action to perform if none of the rules contained in the WebACL match. | `string` | `"block"` | no |
64105
| <a name="input_environment"></a> [environment](#input\_environment) | Environment Variable used as a prefix | `string` | n/a | yes |
106+
| <a name="input_ip_rate_based_rule"></a> [ip\_rate\_based\_rule](#input\_ip\_rate\_based\_rule) | A rate-based rule tracks the rate of requests for each originating IP address, and triggers the rule action when the rate exceeds a limit that you specify on the number of requests in any 5-minute time span | <pre>object({<br> name = string<br> priority = number<br> action = string<br> limit = number<br> })</pre> | `null` | no |
65107
| <a name="input_ip_sets_rule"></a> [ip\_sets\_rule](#input\_ip\_sets\_rule) | A rule to detect web requests coming from particular IP addresses or address ranges. | <pre>list(object({<br> name = string<br> priority = number<br> ip_set = list(string)<br> action = string<br> ip_address_version = string<br> }))</pre> | `[]` | no |
66-
| <a name="input_managed_rules"></a> [managed\_rules](#input\_managed\_rules) | List of Managed WAF rules. | <pre>list(object({<br> name = string<br> priority = number<br> override_action = string<br> excluded_rules = list(string)<br> }))</pre> | <pre>[<br> {<br> "excluded_rules": [],<br> "name": "AWSManagedRulesCommonRuleSet",<br> "override_action": "none",<br> "priority": 10<br> },<br> {<br> "excluded_rules": [],<br> "name": "AWSManagedRulesAmazonIpReputationList",<br> "override_action": "none",<br> "priority": 20<br> },<br> {<br> "excluded_rules": [],<br> "name": "AWSManagedRulesKnownBadInputsRuleSet",<br> "override_action": "none",<br> "priority": 30<br> },<br> {<br> "excluded_rules": [],<br> "name": "AWSManagedRulesSQLiRuleSet",<br> "override_action": "none",<br> "priority": 40<br> },<br> {<br> "excluded_rules": [],<br> "name": "AWSManagedRulesLinuxRuleSet",<br> "override_action": "none",<br> "priority": 50<br> },<br> {<br> "excluded_rules": [],<br> "name": "AWSManagedRulesUnixRuleSet",<br> "override_action": "none",<br> "priority": 60<br> }<br>]</pre> | no |
108+
| <a name="input_is_create_logging_configuration"></a> [is\_create\_logging\_configuration](#input\_is\_create\_logging\_configuration) | Whether to create logging configuration in order start logging from a WAFv2 Web ACL to CloudWatch | `bool` | `true` | no |
109+
| <a name="input_is_enable_cloudwatch_metrics"></a> [is\_enable\_cloudwatch\_metrics](#input\_is\_enable\_cloudwatch\_metrics) | The action to perform if none of the rules contained in the WebACL match. | `bool` | `true` | no |
110+
| <a name="input_is_enable_default_rule"></a> [is\_enable\_default\_rule](#input\_is\_enable\_default\_rule) | If true with enable default rule (detail in locals.tf) | `bool` | `true` | no |
111+
| <a name="input_is_enable_sampled_requests"></a> [is\_enable\_sampled\_requests](#input\_is\_enable\_sampled\_requests) | Whether AWS WAF should store a sampling of the web requests that match the rules. You can view the sampled requests through the AWS WAF console. | `bool` | `true` | no |
112+
| <a name="input_logging_filter"></a> [logging\_filter](#input\_logging\_filter) | A configuration block that specifies which web requests are kept in the logs and which are dropped. You can filter on the rule action and on the web request labels that were applied by matching rules during web ACL evaluation. | `any` | `{}` | no |
113+
| <a name="input_managed_rules"></a> [managed\_rules](#input\_managed\_rules) | List of Managed WAF rules. | <pre>list(object({<br> name = string<br> priority = number<br> override_action = string<br> excluded_rules = list(string)<br> }))</pre> | `[]` | no |
67114
| <a name="input_name"></a> [name](#input\_name) | A friendly name of the WebACL. | `string` | n/a | yes |
68115
| <a name="input_prefix"></a> [prefix](#input\_prefix) | The prefix name of customer to be displayed in AWS console and resource | `string` | n/a | yes |
116+
| <a name="input_redacted_fields"></a> [redacted\_fields](#input\_redacted\_fields) | The parts of the request that you want to keep out of the logs. Up to 100 `redacted_fields` blocks are supported. | `any` | `[]` | no |
69117
| <a name="input_scope"></a> [scope](#input\_scope) | The scope of this Web ACL. Valid options: CLOUDFRONT, REGIONAL. | `string` | n/a | yes |
70118
| <a name="input_tags"></a> [tags](#input\_tags) | A mapping of tags to assign to the WAFv2 ACL. | `map(string)` | `{}` | no |
71119

@@ -74,3 +122,4 @@ No modules.
74122
| Name | Description |
75123
|------|-------------|
76124
| <a name="output_web_acl_id"></a> [web\_acl\_id](#output\_web\_acl\_id) | The ARN of the WAF WebACL. |
125+
<!-- END_TF_DOCS -->

locals.tf

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,43 @@ locals {
77
},
88
var.tags
99
)
10+
default_rules = [
11+
{
12+
name = "AWSManagedRulesCommonRuleSet",
13+
priority = 10
14+
override_action = "none"
15+
excluded_rules = []
16+
},
17+
{
18+
name = "AWSManagedRulesAmazonIpReputationList",
19+
priority = 20
20+
override_action = "none"
21+
excluded_rules = []
22+
},
23+
{
24+
name = "AWSManagedRulesKnownBadInputsRuleSet",
25+
priority = 30
26+
override_action = "none"
27+
excluded_rules = []
28+
},
29+
{
30+
name = "AWSManagedRulesSQLiRuleSet",
31+
priority = 40
32+
override_action = "none"
33+
excluded_rules = []
34+
},
35+
{
36+
name = "AWSManagedRulesLinuxRuleSet",
37+
priority = 50
38+
override_action = "none"
39+
excluded_rules = []
40+
},
41+
{
42+
name = "AWSManagedRulesUnixRuleSet",
43+
priority = 60
44+
override_action = "none"
45+
excluded_rules = []
46+
}
47+
]
48+
managed_rules = concat(var.is_enable_default_rule ? local.default_rules : [], var.managed_rules)
1049
}

logs.tf

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
resource "aws_cloudwatch_log_group" "this" {
2+
count = var.is_create_logging_configuration ? 1 : 0
3+
name = format("aws-waf-logs-%s", var.name)
4+
5+
tags = merge(
6+
local.tags,
7+
{ "Name" = format("aws-waf-logs-%s", var.name) }
8+
)
9+
}
10+
11+
resource "aws_wafv2_web_acl_logging_configuration" "main" {
12+
count = var.is_create_logging_configuration ? 1 : 0
13+
14+
log_destination_configs = [aws_cloudwatch_log_group.this[0].arn]
15+
resource_arn = aws_wafv2_web_acl.this.arn
16+
17+
dynamic "redacted_fields" {
18+
for_each = var.redacted_fields
19+
content {
20+
dynamic "single_header" {
21+
for_each = length(lookup(redacted_fields.value, "single_header", {})) == 0 ? [] : [lookup(redacted_fields.value, "single_header", {})]
22+
content {
23+
name = lookup(single_header.value, "name", null)
24+
}
25+
}
26+
27+
dynamic "single_query_argument" {
28+
for_each = length(lookup(redacted_fields.value, "single_query_argument", {})) == 0 ? [] : [lookup(redacted_fields.value, "single_query_argument", {})]
29+
content {
30+
name = lookup(single_query_argument.value, "name", null)
31+
}
32+
}
33+
}
34+
}
35+
36+
dynamic "logging_filter" {
37+
for_each = length(var.logging_filter) == 0 ? [] : [var.logging_filter]
38+
content {
39+
default_behavior = lookup(logging_filter.value, "default_behavior", "KEEP")
40+
41+
dynamic "filter" {
42+
for_each = length(lookup(logging_filter.value, "filter", {})) == 0 ? [] : toset(lookup(logging_filter.value, "filter"))
43+
content {
44+
behavior = lookup(filter.value, "behavior")
45+
requirement = lookup(filter.value, "requirement", "MEETS_ANY")
46+
47+
dynamic "condition" {
48+
for_each = length(lookup(filter.value, "condition", {})) == 0 ? [] : toset(lookup(filter.value, "condition"))
49+
content {
50+
dynamic "action_condition" {
51+
for_each = length(lookup(condition.value, "action_condition", {})) == 0 ? [] : [lookup(condition.value, "action_condition", {})]
52+
content {
53+
action = lookup(action_condition.value, "action")
54+
}
55+
}
56+
57+
dynamic "label_name_condition" {
58+
for_each = length(lookup(condition.value, "label_name_condition", {})) == 0 ? [] : [lookup(condition.value, "label_name_condition", {})]
59+
content {
60+
label_name = lookup(label_name_condition.value, "label_name")
61+
}
62+
}
63+
}
64+
}
65+
}
66+
}
67+
}
68+
}
69+
}

variables.tf

Lines changed: 49 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,13 @@ variable "scope" {
2626
}
2727
}
2828

29+
variable "is_enable_default_rule" {
30+
type = bool
31+
description = "If true with enable default rule (detail in locals.tf)"
32+
default = true
33+
}
34+
35+
# https://docs.aws.amazon.com/waf/latest/developerguide/aws-managed-rule-groups-baseline.html
2936
variable "managed_rules" {
3037
type = list(object({
3138
name = string
@@ -34,44 +41,7 @@ variable "managed_rules" {
3441
excluded_rules = list(string)
3542
}))
3643
description = "List of Managed WAF rules."
37-
default = [
38-
{
39-
name = "AWSManagedRulesCommonRuleSet",
40-
priority = 10
41-
override_action = "none"
42-
excluded_rules = []
43-
},
44-
{
45-
name = "AWSManagedRulesAmazonIpReputationList",
46-
priority = 20
47-
override_action = "none"
48-
excluded_rules = []
49-
},
50-
{
51-
name = "AWSManagedRulesKnownBadInputsRuleSet",
52-
priority = 30
53-
override_action = "none"
54-
excluded_rules = []
55-
},
56-
{
57-
name = "AWSManagedRulesSQLiRuleSet",
58-
priority = 40
59-
override_action = "none"
60-
excluded_rules = []
61-
},
62-
{
63-
name = "AWSManagedRulesLinuxRuleSet",
64-
priority = 50
65-
override_action = "none"
66-
excluded_rules = []
67-
},
68-
{
69-
name = "AWSManagedRulesUnixRuleSet",
70-
priority = 60
71-
override_action = "none"
72-
excluded_rules = []
73-
}
74-
]
44+
default = []
7545
}
7646

7747
variable "ip_sets_rule" {
@@ -101,5 +71,45 @@ variable "association_resources" {
10171
variable "default_action" {
10272
type = string
10373
description = "The action to perform if none of the rules contained in the WebACL match."
104-
default = "allow"
74+
default = "block"
75+
}
76+
77+
variable "is_enable_cloudwatch_metrics" {
78+
type = bool
79+
description = "The action to perform if none of the rules contained in the WebACL match."
80+
default = true
81+
}
82+
83+
variable "is_enable_sampled_requests" {
84+
type = bool
85+
description = "Whether AWS WAF should store a sampling of the web requests that match the rules. You can view the sampled requests through the AWS WAF console."
86+
default = true
87+
}
88+
89+
variable "ip_rate_based_rule" {
90+
type = object({
91+
name = string
92+
priority = number
93+
action = string
94+
limit = number
95+
})
96+
description = "A rate-based rule tracks the rate of requests for each originating IP address, and triggers the rule action when the rate exceeds a limit that you specify on the number of requests in any 5-minute time span"
97+
default = null
98+
}
99+
100+
variable "is_create_logging_configuration" {
101+
description = "Whether to create logging configuration in order start logging from a WAFv2 Web ACL to CloudWatch"
102+
default = true
103+
}
104+
105+
variable "redacted_fields" {
106+
description = "The parts of the request that you want to keep out of the logs. Up to 100 `redacted_fields` blocks are supported."
107+
type = any
108+
default = []
109+
}
110+
111+
variable "logging_filter" {
112+
type = any
113+
description = "A configuration block that specifies which web requests are kept in the logs and which are dropped. You can filter on the rule action and on the web request labels that were applied by matching rules during web ACL evaluation."
114+
default = {}
105115
}

versions.tf

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
terraform {
2+
required_providers {
3+
aws = {
4+
source = "hashicorp/aws"
5+
version = "4.8.0"
6+
}
7+
}
8+
}

0 commit comments

Comments
 (0)