Skip to content

Commit

Permalink
Create infrastructure network ACL
Browse files Browse the repository at this point in the history
* Creates a network ACL with ocnfigurable rules
* If egress/ingress lockdown is enabled, all traffic will be blocked by
  default
* The ACL is applied to all the subnets created in the infrastructure
* Custom rules can be added to the egress / ingress rules. These will be
  evaluated before the allow all rule (if lockdown is not set). The
  allow all rule is set to rule number 100, because an ACL can only have
  maximum 80 rules, and all of the custom rules will have there rule
  numbers managed automatically by using their index in the list as the
  rule number.
  • Loading branch information
Stretch96 committed Nov 24, 2023
1 parent 453808d commit 4181b0d
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 0 deletions.
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ This project creates and manages resources within an AWS account for infrastruct
|------|------|
| [aws_athena_workgroup.infrastructure_vpc_flow_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/athena_workgroup) | resource |
| [aws_cloudwatch_log_group.infrastructure_vpc_flow_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource |
| [aws_default_network_acl.infrastructure](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/default_network_acl) | resource |
| [aws_eip.infrastructure_nat](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eip) | resource |
| [aws_flow_log.infrastructure_vpc_flow_logs_cloudwatch](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/flow_log) | resource |
| [aws_flow_log.infrastructure_vpc_flow_logs_s3](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/flow_log) | resource |
Expand All @@ -36,6 +37,13 @@ This project creates and manages resources within an AWS account for infrastruct
| [aws_kms_alias.infrastructure](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_alias) | resource |
| [aws_kms_key.infrastructure](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_key) | resource |
| [aws_nat_gateway.infrastructure](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/nat_gateway) | resource |
| [aws_network_acl.infrastructure](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/network_acl) | resource |
| [aws_network_acl_association.infrastructure_private_subnets](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/network_acl_association) | resource |
| [aws_network_acl_association.infrastructure_public_subnets](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/network_acl_association) | resource |
| [aws_network_acl_rule.egress](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/network_acl_rule) | resource |
| [aws_network_acl_rule.egress_allow_all](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/network_acl_rule) | resource |
| [aws_network_acl_rule.ingress](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/network_acl_rule) | resource |
| [aws_network_acl_rule.ingress_allow_all](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/network_acl_rule) | resource |
| [aws_route.infrustructure_public_internet_gateway](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route) | resource |
| [aws_route.private_nat_gateway](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route) | resource |
| [aws_route_table.infrastructure_private](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table) | resource |
Expand Down Expand Up @@ -74,6 +82,10 @@ This project creates and manages resources within an AWS account for infrastruct
| <a name="input_infrastructure_vpc_flow_logs_s3_with_athena"></a> [infrastructure\_vpc\_flow\_logs\_s3\_with\_athena](#input\_infrastructure\_vpc\_flow\_logs\_s3\_with\_athena) | Enable VPC flow logs in infrastructure VPC to the S3 logs bucket. A compatible Glue table/database and Athena workgroup will also be created to allow querying the logs. | `bool` | n/a | yes |
| <a name="input_infrastructure_vpc_flow_logs_traffic_type"></a> [infrastructure\_vpc\_flow\_logs\_traffic\_type](#input\_infrastructure\_vpc\_flow\_logs\_traffic\_type) | Infrastructure VPC flow logs traffic type | `string` | n/a | yes |
| <a name="input_infrastructure_vpc_instance_tenancy"></a> [infrastructure\_vpc\_instance\_tenancy](#input\_infrastructure\_vpc\_instance\_tenancy) | Infrastructure VPC instance tenancy | `string` | n/a | yes |
| <a name="input_infrastructure_vpc_network_acl_egress_custom_rules"></a> [infrastructure\_vpc\_network\_acl\_egress\_custom\_rules](#input\_infrastructure\_vpc\_network\_acl\_egress\_custom\_rules) | Infrastructure vpc egress custom rules. These will be evaluated before any automatically added rules. | <pre>list(object({<br> protocol = string<br> from_port = number<br> to_port = number<br> action = string<br> cidr_block = string<br> ipv6_cidr_block = optional(string, null)<br> icmp_type = optional(number, null)<br> icmp_code = optional(number, null)<br> }))</pre> | n/a | yes |
| <a name="input_infrastructure_vpc_network_acl_egress_lockdown"></a> [infrastructure\_vpc\_network\_acl\_egress\_lockdown](#input\_infrastructure\_vpc\_network\_acl\_egress\_lockdown) | Creates a network ACL which blocks all egress traffic, except the ports required for services that are launched. | `bool` | n/a | yes |
| <a name="input_infrastructure_vpc_network_acl_ingress_custom_rules"></a> [infrastructure\_vpc\_network\_acl\_ingress\_custom\_rules](#input\_infrastructure\_vpc\_network\_acl\_ingress\_custom\_rules) | Infrastructure vpc ingress custom rules. These will be evaluated before any automatically added rules. | <pre>list(object({<br> protocol = string<br> from_port = number<br> to_port = number<br> action = string<br> cidr_block = string<br> ipv6_cidr_block = optional(string, null)<br> icmp_type = optional(number, null)<br> icmp_code = optional(number, null)<br> }))</pre> | n/a | yes |
| <a name="input_infrastructure_vpc_network_acl_ingress_lockdown"></a> [infrastructure\_vpc\_network\_acl\_ingress\_lockdown](#input\_infrastructure\_vpc\_network\_acl\_ingress\_lockdown) | Creates a network ACL which blocks all ingress traffic, except the ports required for services that are launched. | `bool` | n/a | yes |
| <a name="input_infrastructure_vpc_network_availability_zones"></a> [infrastructure\_vpc\_network\_availability\_zones](#input\_infrastructure\_vpc\_network\_availability\_zones) | A list of availability zone characters (eg. ["a", "b", "c"]) | `list(string)` | n/a | yes |
| <a name="input_infrastructure_vpc_network_enable_private"></a> [infrastructure\_vpc\_network\_enable\_private](#input\_infrastructure\_vpc\_network\_enable\_private) | Enable private networking on Infrastructure VPC. This will create subnets with a route to a NAT Gateway (If Public networking has been enabled) | `bool` | n/a | yes |
| <a name="input_infrastructure_vpc_network_enable_public"></a> [infrastructure\_vpc\_network\_enable\_public](#input\_infrastructure\_vpc\_network\_enable\_public) | Enable public networking on Infrastructure VPC. This will create subnets with a route to an Internet Gateway | `bool` | n/a | yes |
Expand Down
4 changes: 4 additions & 0 deletions locals.tf
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ locals {
infrastructure_vpc_network_private_cidr = cidrsubnet(local.infrastructure_vpc_cidr_block, 1, 1)
infrastructure_vpc_network_private_cidr_prefix = basename(local.infrastructure_vpc_network_private_cidr)
infrastructure_vpc_network_private_cidr_newbits = 24 - local.infrastructure_vpc_network_private_cidr_prefix
infrastructure_vpc_network_acl_egress_lockdown = var.infrastructure_vpc_network_acl_egress_lockdown
infrastructure_vpc_network_acl_egress_custom_rules = var.infrastructure_vpc_network_acl_egress_custom_rules
infrastructure_vpc_network_acl_ingress_lockdown = var.infrastructure_vpc_network_acl_ingress_lockdown
infrastructure_vpc_network_acl_ingress_custom_rules = var.infrastructure_vpc_network_acl_ingress_custom_rules
infrastructure_vpc_flow_logs_cloudwatch_logs = var.infrastructure_vpc_flow_logs_cloudwatch_logs && local.infrastructure_vpc
infrastructure_vpc_flow_logs_s3_with_athena = var.infrastructure_vpc_flow_logs_s3_with_athena && local.infrastructure_vpc
infrastructure_vpc_flow_logs_s3_key_prefix = trim(var.infrastructure_vpc_flow_logs_s3_key_prefix, "/")
Expand Down
38 changes: 38 additions & 0 deletions variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,41 @@ variable "infrastructure_vpc_network_availability_zones" {
description = "A list of availability zone characters (eg. [\"a\", \"b\", \"c\"])"
type = list(string)
}

variable "infrastructure_vpc_network_acl_egress_lockdown" {
description = "Creates a network ACL which blocks all egress traffic, except the ports required for services that are launched."
type = bool
}

variable "infrastructure_vpc_network_acl_egress_custom_rules" {
description = "Infrastructure vpc egress custom rules. These will be evaluated before any automatically added rules."
type = list(object({
protocol = string
from_port = number
to_port = number
action = string
cidr_block = string
ipv6_cidr_block = optional(string, null)
icmp_type = optional(number, null)
icmp_code = optional(number, null)
}))
}

variable "infrastructure_vpc_network_acl_ingress_lockdown" {
description = "Creates a network ACL which blocks all ingress traffic, except the ports required for services that are launched."
type = bool
}

variable "infrastructure_vpc_network_acl_ingress_custom_rules" {
description = "Infrastructure vpc ingress custom rules. These will be evaluated before any automatically added rules."
type = list(object({
protocol = string
from_port = number
to_port = number
action = string
cidr_block = string
ipv6_cidr_block = optional(string, null)
icmp_type = optional(number, null)
icmp_code = optional(number, null)
}))
}
89 changes: 89 additions & 0 deletions vpc-infrastructure-network-acl.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
resource "aws_default_network_acl" "infrastructure" {
count = local.infrastructure_vpc ? 1 : 0

default_network_acl_id = aws_vpc.infrastructure[0].default_network_acl_id
}

resource "aws_network_acl" "infrastructure" {
count = local.infrastructure_vpc ? 1 : 0

vpc_id = aws_vpc.infrastructure[0].id

tags = {
Name = "${local.resource_prefix}-infrastructure"
}
}

resource "aws_network_acl_association" "infrastructure_public_subnets" {
for_each = local.infrastructure_vpc && local.infrastructure_vpc_network_enable_public ? aws_subnet.infrastructure_public : {}

network_acl_id = aws_network_acl.infrastructure[0].id
subnet_id = each.value.id
}

resource "aws_network_acl_association" "infrastructure_private_subnets" {
for_each = local.infrastructure_vpc && local.infrastructure_vpc_network_enable_private ? aws_subnet.infrastructure_private : {}

network_acl_id = aws_network_acl.infrastructure[0].id
subnet_id = each.value.id
}

#This will only be used if `infrastructure_vpc_network_acl_egress_lockdown` isn't set
#We would want to allow all traffic for debugging purposes
#tfsec:ignore:aws-ec2-no-excessive-port-access
resource "aws_network_acl_rule" "egress_allow_all" {
count = local.infrastructure_vpc && !local.infrastructure_vpc_network_acl_egress_lockdown ? 1 : 0

network_acl_id = aws_network_acl.infrastructure[0].id
egress = true
rule_number = 100

rule_action = "allow"
protocol = "-1"
from_port = 0
to_port = 0
cidr_block = "0.0.0.0/0"
}

resource "aws_network_acl_rule" "egress" {
count = local.infrastructure_vpc ? length(local.infrastructure_vpc_network_acl_egress_custom_rules) : 0

network_acl_id = aws_network_acl.infrastructure[0].id
rule_number = count.index + 1
egress = true
protocol = local.infrastructure_vpc_network_acl_egress_custom_rules[count.index]["protocol"]
rule_action = local.infrastructure_vpc_network_acl_egress_custom_rules[count.index]["action"]
cidr_block = local.infrastructure_vpc_network_acl_egress_custom_rules[count.index]["cidr_block"]
from_port = local.infrastructure_vpc_network_acl_egress_custom_rules[count.index]["from_port"]
to_port = local.infrastructure_vpc_network_acl_egress_custom_rules[count.index]["to_port"]
}

#This will only be used if `infrastructure_vpc_network_acl_ingress_lockdown` isn't set
#We would want to allow all traffic for debugging purposes
#tfsec:ignore:aws-ec2-no-excessive-port-access tfsec:ignore:aws-ec2-no-public-ingress-acl
resource "aws_network_acl_rule" "ingress_allow_all" {
count = local.infrastructure_vpc && !local.infrastructure_vpc_network_acl_ingress_lockdown ? 1 : 0

network_acl_id = aws_network_acl.infrastructure[0].id
egress = false
rule_number = 100

rule_action = "allow"
protocol = "-1"
from_port = 0
to_port = 0
cidr_block = "0.0.0.0/0"
}

resource "aws_network_acl_rule" "ingress" {
count = local.infrastructure_vpc ? length(local.infrastructure_vpc_network_acl_ingress_custom_rules) : 0

network_acl_id = aws_network_acl.infrastructure[0].id
rule_number = count.index + 1
egress = false
protocol = local.infrastructure_vpc_network_acl_ingress_custom_rules[count.index]["protocol"]
rule_action = local.infrastructure_vpc_network_acl_ingress_custom_rules[count.index]["action"]
cidr_block = local.infrastructure_vpc_network_acl_ingress_custom_rules[count.index]["cidr_block"]
from_port = local.infrastructure_vpc_network_acl_ingress_custom_rules[count.index]["from_port"]
to_port = local.infrastructure_vpc_network_acl_ingress_custom_rules[count.index]["to_port"]
}

0 comments on commit 4181b0d

Please sign in to comment.