Skip to content

Commit

Permalink
Merge pull request #122 from dxw/add-allow-list-to-waf
Browse files Browse the repository at this point in the history
Add more lists to WAFs
  • Loading branch information
rjw1 authored Jul 1, 2024
2 parents a503c44 + bb6b7bf commit fdf64c6
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 13 deletions.
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,10 @@ This project creates and manages resources within an AWS account for infrastruct
| [aws_subnet.infrastructure_private](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet) | resource |
| [aws_subnet.infrastructure_public](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet) | resource |
| [aws_vpc.infrastructure](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc) | resource |
| [aws_wafv2_ip_set.infrastructure_ecs_cluster_ip_deny_list](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/wafv2_ip_set) | resource |
| [aws_wafv2_ip_set.infrastructure_ecs_cluster_ipv4_allow_list](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/wafv2_ip_set) | resource |
| [aws_wafv2_ip_set.infrastructure_ecs_cluster_ipv4_deny_list](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/wafv2_ip_set) | resource |
| [aws_wafv2_ip_set.infrastructure_ecs_cluster_ipv6_allow_list](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/wafv2_ip_set) | resource |
| [aws_wafv2_ip_set.infrastructure_ecs_cluster_ipv6_deny_list](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/wafv2_ip_set) | resource |
| [aws_wafv2_web_acl.infrastructure_ecs_cluster](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/wafv2_web_acl) | resource |
| [null_resource.infrastructure_ecs_cluster_service_blue_green_create_codedeploy_deployment](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource |
| [random_password.infrastructure_ecs_cluster_service_cloudfront_bypass_protection_secret](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/password) | resource |
Expand Down Expand Up @@ -416,7 +419,7 @@ This project creates and manages resources within an AWS account for infrastruct
| <a name="input_infrastructure_ecs_cluster_services_alb_ip_allow_list"></a> [infrastructure\_ecs\_cluster\_services\_alb\_ip\_allow\_list](#input\_infrastructure\_ecs\_cluster\_services\_alb\_ip\_allow\_list) | IP allow list for ingress traffic to the infrastructure ECS cluster services ALB | `list(string)` | n/a | yes |
| <a name="input_infrastructure_ecs_cluster_services_alb_logs_retention"></a> [infrastructure\_ecs\_cluster\_services\_alb\_logs\_retention](#input\_infrastructure\_ecs\_cluster\_services\_alb\_logs\_retention) | Retention in days for the infrasrtucture ecs cluster ALB logs | `number` | n/a | yes |
| <a name="input_infrastructure_ecs_cluster_termination_timeout"></a> [infrastructure\_ecs\_cluster\_termination\_timeout](#input\_infrastructure\_ecs\_cluster\_termination\_timeout) | The timeout for the terminiation lifecycle hook | `number` | n/a | yes |
| <a name="input_infrastructure_ecs_cluster_wafs"></a> [infrastructure\_ecs\_cluster\_wafs](#input\_infrastructure\_ecs\_cluster\_wafs) | Map of WAF ACLs to craete, which can be used with service CloudFront distributions | <pre>map(object({<br> ip_deny_list = optional(list(string), null)<br> aws_managed_rules = optional(list(object({<br> name = string<br> action = string<br> exclude_rules = optional(list(string), null)<br> excluded_path_patterns = optional(list(string), null)<br> })), null)<br> }))</pre> | n/a | yes |
| <a name="input_infrastructure_ecs_cluster_wafs"></a> [infrastructure\_ecs\_cluster\_wafs](#input\_infrastructure\_ecs\_cluster\_wafs) | Map of WAF ACLs to craete, which can be used with service CloudFront distributions | <pre>map(object({<br> ipv4_deny_list = optional(list(string), null)<br> ipv4_allow_list = optional(list(string), null)<br> ipv6_deny_list = optional(list(string), null)<br> ipv6_allow_list = optional(list(string), null)<br> aws_managed_rules = optional(list(object({<br> name = string<br> action = string<br> exclude_rules = optional(list(string), null)<br> excluded_path_patterns = optional(list(string), null)<br> })), null)<br> }))</pre> | n/a | yes |
| <a name="input_infrastructure_elasticache"></a> [infrastructure\_elasticache](#input\_infrastructure\_elasticache) | Map of Elasticaches (The key will be the elasticache name). Values in here will override `infrastructure_elasticache_defaults` values if set."<br> {<br> elasticache-name = {<br> type: Choose either `cluster` or `serverless`<br> engine: ElastiCache engine (Only `redis` is currently supported)<br> engine\_version: ElastiCache Engine version (For serverless, Specify the major version only)<br> parameters: Map of Parameters for the ElastiCache parameter group ({ parameter-name = parameter-value, ... })<br> cluster\_node\_type: ElastiCache Cluster node type<br> cluster\_node\_count: ElastiCache Cluster node count<br> serverless\_max\_storage: Serverless maximum storage<br> serverless\_max\_ecpu: Serverless maximum number of ECPUs the cache can consume per second (1000 - 15000000)<br> snapshot\_retention\_limit: Snapshot retention limit<br> }<br> } | <pre>map(object({<br> type = optional(string, null)<br> engine = optional(string, null)<br> engine_version = optional(string, null)<br> parameters = optional(map(string), null)<br> cluster_node_type = optional(string, null)<br> cluster_node_count = optional(number, null)<br> serverless_max_storage = optional(string, null)<br> serverless_max_ecpu = optional(number, null)<br> snapshot_retention_limit = optional(number, null)<br> }))</pre> | n/a | yes |
| <a name="input_infrastructure_elasticache_defaults"></a> [infrastructure\_elasticache\_defaults](#input\_infrastructure\_elasticache\_defaults) | Default values for ElastiCaches | <pre>object({<br> type = optional(string, null)<br> engine = optional(string, null)<br> engine_version = optional(string, null)<br> parameters = optional(map(string), null)<br> cluster_node_type = optional(string, null)<br> cluster_node_count = optional(number, null)<br> serverless_max_storage = optional(number, null)<br> serverless_max_ecpu = optional(number, null)<br> snapshot_retention_limit = optional(number, null)<br> })</pre> | n/a | yes |
| <a name="input_infrastructure_kms_encryption"></a> [infrastructure\_kms\_encryption](#input\_infrastructure\_kms\_encryption) | Enable infrastructure KMS encryption. This will create a single KMS key to be used across all resources that support KMS encryption. | `bool` | n/a | yes |
Expand Down
131 changes: 121 additions & 10 deletions ecs-cluster-infrastructure-waf.tf
Original file line number Diff line number Diff line change
@@ -1,15 +1,54 @@
resource "aws_wafv2_ip_set" "infrastructure_ecs_cluster_ip_deny_list" {
resource "aws_wafv2_ip_set" "infrastructure_ecs_cluster_ipv4_deny_list" {
for_each = {
for k, v in local.infrastructure_ecs_cluster_wafs : k => v if v["ip_deny_list"] != null
for k, v in local.infrastructure_ecs_cluster_wafs : k => v if v["ipv4_deny_list"] != null
}

name = "${local.resource_prefix}-${each.key}-ip-deny-list"
description = "IP addresses to block on ${local.resource_prefix}-${each.key}"
name = "${local.resource_prefix}-${each.key}-ipv4-deny-list"
description = "IPv4 addresses to block on ${local.resource_prefix}-${each.key}"
provider = aws.useast1
scope = "CLOUDFRONT"
ip_address_version = "IPV4"
addresses = each.value["ip_deny_list"]
addresses = each.value["ipv4_deny_list"]
}

resource "aws_wafv2_ip_set" "infrastructure_ecs_cluster_ipv4_allow_list" {
for_each = {
for k, v in local.infrastructure_ecs_cluster_wafs : k => v if v["ipv4_allow_list"] != null
}

name = "${local.resource_prefix}-${each.key}-ip-allow-list"
description = "IP addresses to allow on ${local.resource_prefix}-${each.key}"
provider = aws.useast1
scope = "CLOUDFRONT"
ip_address_version = "IPV4"
addresses = each.value["ipv4_allow_list"]
}

resource "aws_wafv2_ip_set" "infrastructure_ecs_cluster_ipv6_deny_list" {
for_each = {
for k, v in local.infrastructure_ecs_cluster_wafs : k => v if v["ipv6_deny_list"] != null
}

name = "${local.resource_prefix}-${each.key}-ipv6-deny-list"
description = "IPv6 addresses to block on ${local.resource_prefix}-${each.key}"
provider = aws.useast1
scope = "CLOUDFRONT"
ip_address_version = "IPV6"
addresses = each.value["ipv6_deny_list"]
}

resource "aws_wafv2_ip_set" "infrastructure_ecs_cluster_ipv6_allow_list" {
for_each = {
for k, v in local.infrastructure_ecs_cluster_wafs : k => v if v["ipv6_allow_list"] != null
}

name = "${local.resource_prefix}-${each.key}-ipv6-allow-list"
description = "IPv6 addresses to allow on ${local.resource_prefix}-${each.key}"
provider = aws.useast1
scope = "CLOUDFRONT"
ip_address_version = "IPV6"
addresses = each.value["ipv6_allow_list"]
}
resource "aws_wafv2_web_acl" "infrastructure_ecs_cluster" {
for_each = local.infrastructure_ecs_cluster_wafs

Expand All @@ -24,10 +63,10 @@ resource "aws_wafv2_web_acl" "infrastructure_ecs_cluster" {
}

dynamic "rule" {
for_each = each.value["ip_deny_list"] != null ? [1] : []
for_each = each.value["ipv4_deny_list"] != null ? [1] : []

content {
name = "CustomDalmatianBlockIPSet"
name = "CustomDalmatianBlockIPv4Set"
priority = 0 # Always process this rule before any others if it is defined

action {
Expand All @@ -36,24 +75,96 @@ resource "aws_wafv2_web_acl" "infrastructure_ecs_cluster" {

statement {
ip_set_reference_statement {
arn = aws_wafv2_ip_set.infrastructure_ecs_cluster_ip_deny_list[each.key].arn
arn = aws_wafv2_ip_set.infrastructure_ecs_cluster_ipv4_deny_list[each.key].arn
}
}

visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "${local.resource_prefix}-${each.key}-ip-deny"
metric_name = "${local.resource_prefix}-${each.key}-ipv4-deny"
sampled_requests_enabled = true
}
}
}
dynamic "rule" {
for_each = each.value["ipv4_allow_list"] != null ? [1] : []

content {
name = "CustomDalmatianAllowIPv4Set"
priority = 1 # Always process this rule before any others if it is defined

action {
allow {}
}

statement {
ip_set_reference_statement {
arn = aws_wafv2_ip_set.infrastructure_ecs_cluster_ipv4_allow_list[each.key].arn
}
}

visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "${local.resource_prefix}-${each.key}-ipv4-allow"
sampled_requests_enabled = true
}
}
}

dynamic "rule" {
for_each = each.value["ipv6_deny_list"] != null ? [1] : []

content {
name = "CustomDalmatianBlockIPv6Set"
priority = 3 # Always process this rule before any others if it is defined

action {
block {}
}

statement {
ip_set_reference_statement {
arn = aws_wafv2_ip_set.infrastructure_ecs_cluster_ipv6_deny_list[each.key].arn
}
}

visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "${local.resource_prefix}-${each.key}-ipv6-deny"
sampled_requests_enabled = true
}
}
}
dynamic "rule" {
for_each = each.value["ipv6_allow_list"] != null ? [1] : []

content {
name = "CustomDalmatianAllowIPv6Set"
priority = 4 # Always process this rule before any others if it is defined

action {
allow {}
}

statement {
ip_set_reference_statement {
arn = aws_wafv2_ip_set.infrastructure_ecs_cluster_ipv6_allow_list[each.key].arn
}
}

visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "${local.resource_prefix}-${each.key}-ipv6-allow"
sampled_requests_enabled = true
}
}
}
dynamic "rule" {
for_each = each.value["aws_managed_rules"] != null ? each.value["aws_managed_rules"] : []

content {
name = rule.value["name"]
priority = rule.key + 1
priority = rule.key + 4

override_action {
dynamic "count" {
Expand Down
5 changes: 4 additions & 1 deletion variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,10 @@ variable "infrastructure_ecs_cluster_ecs_asg_diff_alert_opsgenie" {
variable "infrastructure_ecs_cluster_wafs" {
description = "Map of WAF ACLs to craete, which can be used with service CloudFront distributions"
type = map(object({
ip_deny_list = optional(list(string), null)
ipv4_deny_list = optional(list(string), null)
ipv4_allow_list = optional(list(string), null)
ipv6_deny_list = optional(list(string), null)
ipv6_allow_list = optional(list(string), null)
aws_managed_rules = optional(list(object({
name = string
action = string
Expand Down

0 comments on commit fdf64c6

Please sign in to comment.