diff --git a/terraform/environments/delius-jitbit/waf.tf b/terraform/environments/delius-jitbit/waf.tf index 70ab903f3ed..79f9d68a34a 100644 --- a/terraform/environments/delius-jitbit/waf.tf +++ b/terraform/environments/delius-jitbit/waf.tf @@ -1,67 +1,74 @@ -resource "aws_wafv2_web_acl" "this" { - name = "${local.application_name}-acl" - description = "Web ACL for ${local.application_name}" - scope = "REGIONAL" - default_action { - allow {} +module "shield" { + source = "../../modules/shield_advanced" + + providers = { + aws.modernisation-platform = aws.modernisation-platform } - rule { - name = "AWSManagedRulesCommonRuleSet" - priority = 0 - override_action { - # Dont do anything but count requests that match the rules in the ruleset - count {} - } - statement { - managed_rule_group_statement { - name = "AWSManagedRulesCommonRuleSet" - vendor_name = "AWS" - } - } - visibility_config { - cloudwatch_metrics_enabled = true - metric_name = "${local.application_name}-common-ruleset" - sampled_requests_enabled = true + + application_name = local.application_name + + enable_logging = true + + resources = { + alb = { + arn = aws_lb.external.arn } } - rule { - name = "AWSManagedRulesSQLiRuleSet" - priority = 1 - override_action { - # Dont do anything but count requests that match the rules in the ruleset - count {} - } - statement { - managed_rule_group_statement { - name = "AWSManagedRulesSQLiRuleSet" - vendor_name = "AWS" + + waf_acl_rules = { + AWSManagedRulesCommonRuleSet = { + "action" = "count" + "name" = "AWSManagedRulesCommonRuleSet" + "priority" = 0 + "threshold" = 1000 + "statement" = { + "managed_rule_group_statement" = { + "name" = "AWSManagedRulesCommonRuleSet" + "vendor_name" = "AWS" + } + } + "visibility_config" = { + "cloudwatch_metrics_enabled" = true + "metric_name" = "${local.application_name}-common-ruleset" + "sampled_requests_enabled" = true } } - visibility_config { - cloudwatch_metrics_enabled = true - metric_name = "${local.application_name}-SQLi-ruleset" - sampled_requests_enabled = true + AWSManagedRulesSQLiRuleSet = { + "action" = "count" + "name" = "AWSManagedRulesSQLiRuleSet" + "priority" = 1 + "threshold" = 1000 + "statement" = { + "managed_rule_group_statement" = { + "name" = "AWSManagedRulesSQLiRuleSet" + "vendor_name" = "AWS" + } + } + "visibility_config" = { + "cloudwatch_metrics_enabled" = true + "metric_name" = "${local.application_name}-SQLi-ruleset" + "sampled_requests_enabled" = true + } } } - tags = local.tags - visibility_config { - cloudwatch_metrics_enabled = true - metric_name = "${local.application_name}-waf-metrics" - sampled_requests_enabled = true - } } -resource "aws_wafv2_web_acl_association" "this" { - resource_arn = aws_lb.external.arn - web_acl_arn = aws_wafv2_web_acl.this.arn + +data "external" "shield_waf" { + program = [ + "bash", "-c", + "aws wafv2 list-web-acls --scope REGIONAL --output json | jq -c '{arn: .WebACLs[] | select(.Name | contains(\"FMManagedWebACL\")) | .ARN, name: .WebACLs[] | select(.Name | contains(\"FMManagedWebACL\")) | .Name}'" + ] } -resource "aws_cloudwatch_log_group" "waf" { - name = "aws-waf-logs-${local.application_name}" - retention_in_days = 60 - tags = local.tags +locals { + split_arn = split("regional/webacl/", data.external.shield_waf.result["arn"])[1] + name = data.external.shield_waf.result["name"] + id = split("/", local.split_arn)[1] + scope = "REGIONAL" + } -resource "aws_wafv2_web_acl_logging_configuration" "waf" { - log_destination_configs = [aws_cloudwatch_log_group.waf.arn] - resource_arn = aws_wafv2_web_acl.this.arn +import { + id = "${local.id}/${local.name}/${local.scope}" + to = module.shield.aws_wafv2_web_acl.main } diff --git a/terraform/modules/shield_advanced/README.md b/terraform/modules/shield_advanced/README.md index 4a72f3f18b1..ae605b53f77 100644 --- a/terraform/modules/shield_advanced/README.md +++ b/terraform/modules/shield_advanced/README.md @@ -8,13 +8,37 @@ without needing to resort to ClickOps processes. Because the `aws_wafv2_web_acl` is pre-created by AWS Firewall Manager via the MOJ Root Account code, it needs to be imported as part of the module setup. This can be done via the command line, or with an import block. -```shell +```hcl import { id = "c6f3ba81-c457-40f6-bd1f-30e777f60c27/FMManagedWebACLV2-shield_advanced_auto_remediate-1652297838425/REGIONAL" to = module.shield.aws_wafv2_web_acl.main } ``` +Using a data source it is possible to create an import block that is dynamic, allowing you to import for all environments in one run. + +```hcl +data "external" "shield_waf" { + program = [ + "bash", "-c", + "aws wafv2 list-web-acls --scope REGIONAL --output json | jq -c '{arn: .WebACLs[] | select(.Name | contains(\"FMManagedWebACL\")) | .ARN, name: .WebACLs[] | select(.Name | contains(\"FMManagedWebACL\")) | .Name}'" + ] +} + +locals { + split_arn = split("regional/webacl/", data.external.shield_waf.result["arn"])[1] + name = data.external.shield_waf.result["name"] + id = split("/", local.split_arn)[1] + scope = "REGIONAL" +} + +import { + id = "${local.id}/${local.name}/${local.scope}" + to = module.shield.aws_wafv2_web_acl.main +} +``` + + ## Requirements | Name | Version | @@ -41,12 +65,14 @@ import { | Name | Type | |------|------| +| [aws_cloudwatch_log_group.waf](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource | | [aws_cloudwatch_metric_alarm.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_metric_alarm) | resource | | [aws_shield_application_layer_automatic_response.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/shield_application_layer_automatic_response) | resource | | [aws_shield_drt_access_role_arn_association.main](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/shield_drt_access_role_arn_association) | resource | | [aws_sns_topic.module_ddos_alarm](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic) | resource | | [aws_wafv2_web_acl.main](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/wafv2_web_acl) | resource | | [aws_wafv2_web_acl_association.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/wafv2_web_acl_association) | resource | +| [aws_wafv2_web_acl_logging_configuration.waf](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/wafv2_web_acl_logging_configuration) | resource | | [aws_iam_role.srt_access](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_role) | data source | | [aws_kms_key.sns](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/kms_key) | data source | | [aws_secretsmanager_secret.pagerduty_integration_keys](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/secretsmanager_secret) | data source | @@ -59,10 +85,13 @@ import { | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| | [application\_name](#input\_application\_name) | Name of application being protected. | `string` | n/a | yes | -| [excluded\_protections](#input\_excluded\_protections) | A list of strings to not associate with the AWS Shield WAF ACL. | `set(string)` | n/a | yes | +| [enable\_logging](#input\_enable\_logging) | Enable logging for the WAF ACL. | `bool` | `false` | no | +| [excluded\_protections](#input\_excluded\_protections) | A list of strings to not associate with the AWS Shield WAF ACL. | `set(string)` | `[]` | no | +| [log\_retention\_in\_days](#input\_log\_retention\_in\_days) | Number of days to retain logs in CloudWatch Logs. | `number` | `60` | no | | [resources](#input\_resources) | Map of resource ARNs and optional automatic response actions. | `map(any)` | n/a | yes | | [waf\_acl\_rules](#input\_waf\_acl\_rules) | A map of values to be used in a dynamic WAF ACL rule block. | `map(any)` | n/a | yes | ## Outputs No outputs. + diff --git a/terraform/modules/shield_advanced/shield.tf b/terraform/modules/shield_advanced/shield.tf index 04ee6cf4541..7e9a6af2d0a 100644 --- a/terraform/modules/shield_advanced/shield.tf +++ b/terraform/modules/shield_advanced/shield.tf @@ -90,3 +90,15 @@ resource "aws_wafv2_web_acl" "main" { } } } + +resource "aws_cloudwatch_log_group" "waf" { + count = var.enable_logging ? 1 : 0 + name = "aws-waf-logs-${data.external.shield_waf.result["name"]}" + retention_in_days = var.log_retention_in_days +} + +resource "aws_wafv2_web_acl_logging_configuration" "waf" { + count = var.enable_logging ? 1 : 0 + log_destination_configs = try([aws_cloudwatch_log_group.waf[0].arn], []) + resource_arn = aws_wafv2_web_acl.main.arn +} diff --git a/terraform/modules/shield_advanced/variables.tf b/terraform/modules/shield_advanced/variables.tf index 654613e0ef3..b4a2f3f5a91 100644 --- a/terraform/modules/shield_advanced/variables.tf +++ b/terraform/modules/shield_advanced/variables.tf @@ -17,4 +17,16 @@ variable "resources" { variable "waf_acl_rules" { type = map(any) description = "A map of values to be used in a dynamic WAF ACL rule block." -} \ No newline at end of file +} + +variable "enable_logging" { + type = bool + default = false + description = "Enable logging for the WAF ACL." +} + +variable "log_retention_in_days" { + type = number + default = 60 + description = "Number of days to retain logs in CloudWatch Logs." +}