From e9f65688b95b9ea24823cfab899267adcd95d534 Mon Sep 17 00:00:00 2001 From: Matt Calhoun Date: Tue, 1 Oct 2024 10:36:09 -0400 Subject: [PATCH] add additional waf features (#791) Co-authored-by: Dan Miller Co-authored-by: Igor Rodionov --- modules/waf/README.md | 4 ++ modules/waf/alb.tf | 15 +++++ modules/waf/main.tf | 2 +- modules/waf/remote-state.tf | 0 modules/waf/variables.tf | 113 ++++++++++++++++++++++++++++++++++++ 5 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 modules/waf/alb.tf mode change 100644 => 100755 modules/waf/remote-state.tf diff --git a/modules/waf/README.md b/modules/waf/README.md index 0e09e4014..71b523357 100644 --- a/modules/waf/README.md +++ b/modules/waf/README.md @@ -76,6 +76,8 @@ components: | Name | Type | |------|------| | [aws_ssm_parameter.acl_arn](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssm_parameter) | resource | +| [aws_alb.alb](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/alb) | data source | +| [aws_lbs.alb_by_tags](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/lbs) | data source | ## Inputs @@ -83,6 +85,8 @@ components: |------|-------------|------|---------|:--------:| | [acl\_name](#input\_acl\_name) | Friendly name of the ACL. The ACL ARN will be stored in SSM under {ssm\_path\_prefix}/{acl\_name}/arn | `string` | n/a | yes | | [additional\_tag\_map](#input\_additional\_tag\_map) | Additional key-value pairs to add to each map in `tags_as_list_of_maps`. Not added to `tags` or `id`.
This is for some rare cases where resources want additional configuration of tags
and therefore take a list of maps with tag key, value, and additional configuration. | `map(string)` | `{}` | no | +| [alb\_names](#input\_alb\_names) | list of ALB names to associate with the web ACL. | `list(string)` | `[]` | no | +| [alb\_tags](#input\_alb\_tags) | list of tags to match one or more ALBs to associate with the web ACL. | `list(map(string))` | `[]` | no | | [association\_resource\_arns](#input\_association\_resource\_arns) | A list of ARNs of the resources to associate with the web ACL.
This must be an ARN of an Application Load Balancer, Amazon API Gateway stage, or AWS AppSync.

Do not use this variable to associate a Cloudfront Distribution.
Instead, you should use the `web_acl_id` property on the `cloudfront_distribution` resource.
For more details, refer to https://docs.aws.amazon.com/waf/latest/APIReference/API_AssociateWebACL.html | `list(string)` | `[]` | no | | [association\_resource\_component\_selectors](#input\_association\_resource\_component\_selectors) | A list of Atmos component selectors to get from the remote state and associate their ARNs with the web ACL.
The components must be Application Load Balancers, Amazon API Gateway stages, or AWS AppSync.

component:
Atmos component name
component\_arn\_output:
The component output that defines the component ARN

Set `tenant`, `environment` and `stage` if the components are in different OUs, regions or accounts.

Do not use this variable to select a Cloudfront Distribution component.
Instead, you should use the `web_acl_id` property on the `cloudfront_distribution` resource.
For more details, refer to https://docs.aws.amazon.com/waf/latest/APIReference/API_AssociateWebACL.html |
list(object({
component = string
namespace = optional(string, null)
tenant = optional(string, null)
environment = optional(string, null)
stage = optional(string, null)
component_arn_output = string
}))
| `[]` | no | | [attributes](#input\_attributes) | ID element. Additional attributes (e.g. `workers` or `cluster`) to add to `id`,
in the order they appear in the list. New attributes are appended to the
end of the list. The elements of the list are joined by the `delimiter`
and treated as a single ID element. | `list(string)` | `[]` | no | diff --git a/modules/waf/alb.tf b/modules/waf/alb.tf new file mode 100644 index 000000000..a978df085 --- /dev/null +++ b/modules/waf/alb.tf @@ -0,0 +1,15 @@ +locals { + alb_arns = concat(local.alb_name_arns, local.alb_tag_arns) + alb_name_arns = [for alb_instance in data.aws_alb.alb : alb_instance.arn] + alb_tag_arns = flatten([for alb_instance in data.aws_lbs.alb_by_tags : alb_instance.arns]) +} + +data "aws_alb" "alb" { + for_each = toset(var.alb_names) + name = each.key +} + +data "aws_lbs" "alb_by_tags" { + for_each = { for i, v in var.alb_tags : i => v } + tags = each.value +} diff --git a/modules/waf/main.tf b/modules/waf/main.tf index 4f05ce854..04ef1ae2a 100644 --- a/modules/waf/main.tf +++ b/modules/waf/main.tf @@ -6,7 +6,7 @@ locals { if local.enabled ] - association_resource_arns = concat(var.association_resource_arns, local.association_resource_component_selectors_arns) + association_resource_arns = toset(concat(var.association_resource_arns, local.association_resource_component_selectors_arns, local.alb_arns)) log_destination_component_selectors = [ for i, v in var.log_destination_component_selectors : module.log_destination_components[i].outputs[v.component_output] diff --git a/modules/waf/remote-state.tf b/modules/waf/remote-state.tf old mode 100644 new mode 100755 diff --git a/modules/waf/variables.tf b/modules/waf/variables.tf index 11a46a60c..ac04944f9 100644 --- a/modules/waf/variables.tf +++ b/modules/waf/variables.tf @@ -107,6 +107,119 @@ variable "token_domains" { default = null } +# Logging configuration +# https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/wafv2_web_acl_logging_configuration.html +variable "log_destination_configs" { + type = list(string) + default = [] + description = "The Amazon Kinesis Data Firehose, CloudWatch Log log group, or S3 bucket Amazon Resource Names (ARNs) that you want to associate with the web ACL" +} + +variable "redacted_fields" { + type = map(object({ + method = optional(bool, false) + uri_path = optional(bool, false) + query_string = optional(bool, false) + single_header = optional(list(string), null) + })) + default = {} + description = <<-DOC + The parts of the request that you want to keep out of the logs. + You can only specify one of the following: `method`, `query_string`, `single_header`, or `uri_path` + + method: + Whether to enable redaction of the HTTP method. + The method indicates the type of operation that the request is asking the origin to perform. + uri_path: + Whether to enable redaction of the URI path. + This is the part of a web request that identifies a resource. + query_string: + Whether to enable redaction of the query string. + This is the part of a URL that appears after a `?` character, if any. + single_header: + The list of names of the query headers to redact. + DOC + nullable = false +} + +variable "logging_filter" { + type = object({ + default_behavior = string + filter = list(object({ + behavior = string + requirement = string + condition = list(object({ + action_condition = optional(object({ + action = string + }), null) + label_name_condition = optional(object({ + label_name = string + }), null) + })) + })) + }) + default = null + description = <<-DOC + 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. + DOC +} + +# Association resources +variable "association_resource_arns" { + type = list(string) + default = [] + description = <<-DOC + A list of ARNs of the resources to associate with the web ACL. + This must be an ARN of an Application Load Balancer, Amazon API Gateway stage, or AWS AppSync. + + Do not use this variable to associate a Cloudfront Distribution. + Instead, you should use the `web_acl_id` property on the `cloudfront_distribution` resource. + For more details, refer to https://docs.aws.amazon.com/waf/latest/APIReference/API_AssociateWebACL.html + DOC + nullable = false +} + +variable "alb_names" { + description = "list of ALB names to associate with the web ACL." + type = list(string) + default = [] + nullable = false +} + +variable "alb_tags" { + description = "list of tags to match one or more ALBs to associate with the web ACL." + type = list(map(string)) + default = [] + nullable = false +} + +variable "association_resource_component_selectors" { + type = list(object({ + component = string + namespace = optional(string, null) + tenant = optional(string, null) + environment = optional(string, null) + stage = optional(string, null) + component_arn_output = string + })) + default = [] + description = <<-DOC + A list of Atmos component selectors to get from the remote state and associate their ARNs with the web ACL. + The components must be Application Load Balancers, Amazon API Gateway stages, or AWS AppSync. + + component: + Atmos component name + component_arn_output: + The component output that defines the component ARN + + Do not use this variable to select a Cloudfront Distribution component. + Instead, you should use the `web_acl_id` property on the `cloudfront_distribution` resource. + For more details, refer to https://docs.aws.amazon.com/waf/latest/APIReference/API_AssociateWebACL.html + DOC + nullable = false +} + # Rules variable "byte_match_statement_rules" { type = list(object({