diff --git a/README.md b/README.md index c5f7f2175..1f1663a7c 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ These types of resources are supported: * [Route](https://www.terraform.io/docs/providers/aws/r/route.html) * [Route table](https://www.terraform.io/docs/providers/aws/r/route_table.html) * [Internet Gateway](https://www.terraform.io/docs/providers/aws/r/internet_gateway.html) +* [Network ACL](https://www.terraform.io/docs/providers/aws/r/network_acl.html) * [NAT Gateway](https://www.terraform.io/docs/providers/aws/r/nat_gateway.html) * [VPN Gateway](https://www.terraform.io/docs/providers/aws/r/vpn_gateway.html) * [VPC Endpoint](https://www.terraform.io/docs/providers/aws/r/vpc_endpoint.html): @@ -21,6 +22,7 @@ These types of resources are supported: * [Redshift Subnet Group](https://www.terraform.io/docs/providers/aws/r/redshift_subnet_group.html) * [DHCP Options Set](https://www.terraform.io/docs/providers/aws/r/vpc_dhcp_options.html) * [Default VPC](https://www.terraform.io/docs/providers/aws/r/default_vpc.html) +* [Default Network ACL](https://www.terraform.io/docs/providers/aws/r/default_network_acl.html) Sponsored by [Cloudcraft - the best way to draw AWS diagrams](https://cloudcraft.co/?utm_source=terraform-aws-vpc) @@ -165,6 +167,14 @@ Sometimes it is handy to have public access to RDS instances (it is not recommen enable_dns_support = true ``` +## Network Access Control Lists (ACL or NACL) + +This module can manage network ACL and rules. Once VPC is created, AWS creates the default network ACL, which can be controlled using this module (`manage_default_network_acl = true`). + +Also, each type of subnet may have its own network ACL with custom rules per subnet. Eg, set `public_dedicated_network_acl = true` to use dedicated network ACL for the public subnets; set values of `public_inbound_acl_rules` and `public_outbound_acl_rules` to specify all the NACL rules you need to have on public subnets (see `variables.tf` for default values and structures). + +By default, all subnets are associated with the default network ACL. + ## Public access to Redshift cluster Sometimes it is handy to have public access to Redshift clusters (for example if you need to access it by Kinesis - VPC endpoint for Kinesis is not yet supported by Redshift) by specifying these arguments: @@ -182,6 +192,7 @@ Terraform version 0.10.3 or newer is required for this module to work. * [Simple VPC](https://github.com/terraform-aws-modules/terraform-aws-vpc/tree/master/examples/simple-vpc) * [Complete VPC](https://github.com/terraform-aws-modules/terraform-aws-vpc/tree/master/examples/complete-vpc) * [Manage Default VPC](https://github.com/terraform-aws-modules/terraform-aws-vpc/tree/master/examples/manage-default-vpc) +* [Network ACL](https://github.com/terraform-aws-modules/terraform-aws-vpc/tree/master/examples/network-acls) * Few tests and edge cases examples: [#46](https://github.com/terraform-aws-modules/terraform-aws-vpc/tree/master/examples/issue-46-no-private-subnets), [#44](https://github.com/terraform-aws-modules/terraform-aws-vpc/tree/master/examples/issue-44-asymmetric-private-subnets), [#108](https://github.com/terraform-aws-modules/terraform-aws-vpc/tree/master/examples/issue-108-route-already-exists) @@ -205,11 +216,19 @@ Terraform version 0.10.3 or newer is required for this module to work. | create\_redshift\_subnet\_group | Controls if redshift subnet group should be created | string | `"true"` | no | | create\_redshift\_subnet\_route\_table | Controls if separate route table for redshift should be created | string | `"false"` | no | | create\_vpc | Controls if VPC should be created (it affects almost all resources) | string | `"true"` | no | +| database\_acl\_tags | Additional tags for the database subnets network ACL | map | `{}` | no | +| database\_dedicated\_network\_acl | Whether to use dedicated network ACL (not default) and custom rules for database subnets | string | `"false"` | no | +| database\_inbound\_acl\_rules | Database subnets inbound network ACL rules | list | `[ { "cidr_block": "0.0.0.0/0", "from_port": 0, "protocol": "-1", "rule_action": "allow", "rule_number": 100, "to_port": 0 } ]` | no | +| database\_outbound\_acl\_rules | Database subnets outbound network ACL rules | list | `[ { "cidr_block": "0.0.0.0/0", "from_port": 0, "protocol": "-1", "rule_action": "allow", "rule_number": 100, "to_port": 0 } ]` | no | | database\_route\_table\_tags | Additional tags for the database route tables | map | `{}` | no | | database\_subnet\_group\_tags | Additional tags for the database subnet group | map | `{}` | no | | database\_subnet\_suffix | Suffix to append to database subnets name | string | `"db"` | no | | database\_subnet\_tags | Additional tags for the database subnets | map | `{}` | no | | database\_subnets | A list of database subnets | list | `[]` | no | +| default\_network\_acl\_egress | List of maps of egress rules to set on the Default Network ACL | list | `[ { "action": "allow", "cidr_block": "0.0.0.0/0", "from_port": 0, "protocol": "-1", "rule_no": 100, "to_port": 0 }, { "action": "allow", "from_port": 0, "ipv6_cidr_block": "::/0", "protocol": "-1", "rule_no": 101, "to_port": 0 } ]` | no | +| default\_network\_acl\_ingress | List of maps of ingress rules to set on the Default Network ACL | list | `[ { "action": "allow", "cidr_block": "0.0.0.0/0", "from_port": 0, "protocol": "-1", "rule_no": 100, "to_port": 0 }, { "action": "allow", "from_port": 0, "ipv6_cidr_block": "::/0", "protocol": "-1", "rule_no": 101, "to_port": 0 } ]` | no | +| default\_network\_acl\_name | Name to be used on the Default Network ACL | string | `""` | no | +| default\_network\_acl\_tags | Additional tags for the Default Network ACL | map | `{}` | no | | default\_vpc\_enable\_classiclink | Should be true to enable ClassicLink in the Default VPC | string | `"false"` | no | | default\_vpc\_enable\_dns\_hostnames | Should be true to enable DNS hostnames in the Default VPC | string | `"false"` | no | | default\_vpc\_enable\_dns\_support | Should be true to enable DNS support in the Default VPC | string | `"true"` | no | @@ -233,6 +252,10 @@ Terraform version 0.10.3 or newer is required for this module to work. | ecr\_dkr\_endpoint\_private\_dns\_enabled | Whether or not to associate a private hosted zone with the specified VPC for ECR DKR endpoint | string | `"false"` | no | | ecr\_dkr\_endpoint\_security\_group\_ids | The ID of one or more security groups to associate with the network interface for ECR DKR endpoint | list | `[]` | no | | ecr\_dkr\_endpoint\_subnet\_ids | The ID of one or more subnets in which to create a network interface for ECR dkr endpoint. If omitted, private subnets will be used. | list | `[]` | no | +| elasticache\_acl\_tags | Additional tags for the elasticache subnets network ACL | map | `{}` | no | +| elasticache\_dedicated\_network\_acl | Whether to use dedicated network ACL (not default) and custom rules for elasticache subnets | string | `"false"` | no | +| elasticache\_inbound\_acl\_rules | Elasticache subnets inbound network ACL rules | list | `[ { "cidr_block": "0.0.0.0/0", "from_port": 0, "protocol": "-1", "rule_action": "allow", "rule_number": 100, "to_port": 0 } ]` | no | +| elasticache\_outbound\_acl\_rules | Elasticache subnets outbound network ACL rules | list | `[ { "cidr_block": "0.0.0.0/0", "from_port": 0, "protocol": "-1", "rule_action": "allow", "rule_number": 100, "to_port": 0 } ]` | no | | elasticache\_route\_table\_tags | Additional tags for the elasticache route tables | map | `{}` | no | | elasticache\_subnet\_suffix | Suffix to append to elasticache subnets name | string | `"elasticache"` | no | | elasticache\_subnet\_tags | Additional tags for the elasticache subnets | map | `{}` | no | @@ -255,26 +278,43 @@ Terraform version 0.10.3 or newer is required for this module to work. | external\_nat\_ip\_ids | List of EIP IDs to be assigned to the NAT Gateways (used in combination with reuse_nat_ips) | list | `[]` | no | | igw\_tags | Additional tags for the internet gateway | map | `{}` | no | | instance\_tenancy | A tenancy option for instances launched into the VPC | string | `"default"` | no | +| intra\_acl\_tags | Additional tags for the intra subnets network ACL | map | `{}` | no | +| intra\_dedicated\_network\_acl | Whether to use dedicated network ACL (not default) and custom rules for intra subnets | string | `"false"` | no | +| intra\_inbound\_acl\_rules | Intra subnets inbound network ACLs | list | `[ { "cidr_block": "0.0.0.0/0", "from_port": 0, "protocol": "-1", "rule_action": "allow", "rule_number": 100, "to_port": 0 } ]` | no | +| intra\_outbound\_acl\_rules | Intra subnets outbound network ACLs | list | `[ { "cidr_block": "0.0.0.0/0", "from_port": 0, "protocol": "-1", "rule_action": "allow", "rule_number": 100, "to_port": 0 } ]` | no | | intra\_route\_table\_tags | Additional tags for the intra route tables | map | `{}` | no | | intra\_subnet\_suffix | Suffix to append to intra subnets name | string | `"intra"` | no | | intra\_subnet\_tags | Additional tags for the intra subnets | map | `{}` | no | | intra\_subnets | A list of intra subnets | list | `[]` | no | +| manage\_default\_network\_acl | Should be true to adopt and manage Default Network ACL | string | `"false"` | no | | manage\_default\_vpc | Should be true to adopt and manage Default VPC | string | `"false"` | no | | map\_public\_ip\_on\_launch | Should be false if you do not want to auto-assign public IP on launch | string | `"true"` | no | | name | Name to be used on all the resources as identifier | string | `""` | no | | nat\_eip\_tags | Additional tags for the NAT EIP | map | `{}` | no | | nat\_gateway\_tags | Additional tags for the NAT gateways | map | `{}` | no | | one\_nat\_gateway\_per\_az | Should be true if you want only one NAT Gateway per availability zone. Requires `var.azs` to be set, and the number of `public_subnets` created to be greater than or equal to the number of availability zones specified in `var.azs`. | string | `"false"` | no | +| private\_acl\_tags | Additional tags for the private subnets network ACL | map | `{}` | no | +| private\_dedicated\_network\_acl | Whether to use dedicated network ACL (not default) and custom rules for private subnets | string | `"false"` | no | +| private\_inbound\_acl\_rules | Private subnets inbound network ACLs | list | `[ { "cidr_block": "0.0.0.0/0", "from_port": 0, "protocol": "-1", "rule_action": "allow", "rule_number": 100, "to_port": 0 } ]` | no | +| private\_outbound\_acl\_rules | Private subnets outbound network ACLs | list | `[ { "cidr_block": "0.0.0.0/0", "from_port": 0, "protocol": "-1", "rule_action": "allow", "rule_number": 100, "to_port": 0 } ]` | no | | private\_route\_table\_tags | Additional tags for the private route tables | map | `{}` | no | | private\_subnet\_suffix | Suffix to append to private subnets name | string | `"private"` | no | | private\_subnet\_tags | Additional tags for the private subnets | map | `{}` | no | | private\_subnets | A list of private subnets inside the VPC | list | `[]` | no | | propagate\_private\_route\_tables\_vgw | Should be true if you want route table propagation | string | `"false"` | no | | propagate\_public\_route\_tables\_vgw | Should be true if you want route table propagation | string | `"false"` | no | +| public\_acl\_tags | Additional tags for the public subnets network ACL | map | `{}` | no | +| public\_dedicated\_network\_acl | Whether to use dedicated network ACL (not default) and custom rules for public subnets | string | `"false"` | no | +| public\_inbound\_acl\_rules | Public subnets inbound network ACLs | list | `[ { "cidr_block": "0.0.0.0/0", "from_port": 0, "protocol": "-1", "rule_action": "allow", "rule_number": 100, "to_port": 0 } ]` | no | +| public\_outbound\_acl\_rules | Public subnets outbound network ACLs | list | `[ { "cidr_block": "0.0.0.0/0", "from_port": 0, "protocol": "-1", "rule_action": "allow", "rule_number": 100, "to_port": 0 } ]` | no | | public\_route\_table\_tags | Additional tags for the public route tables | map | `{}` | no | | public\_subnet\_suffix | Suffix to append to public subnets name | string | `"public"` | no | | public\_subnet\_tags | Additional tags for the public subnets | map | `{}` | no | | public\_subnets | A list of public subnets inside the VPC | list | `[]` | no | +| redshift\_acl\_tags | Additional tags for the redshift subnets network ACL | map | `{}` | no | +| redshift\_dedicated\_network\_acl | Whether to use dedicated network ACL (not default) and custom rules for redshift subnets | string | `"false"` | no | +| redshift\_inbound\_acl\_rules | Redshift subnets inbound network ACL rules | list | `[ { "cidr_block": "0.0.0.0/0", "from_port": 0, "protocol": "-1", "rule_action": "allow", "rule_number": 100, "to_port": 0 } ]` | no | +| redshift\_outbound\_acl\_rules | Redshift subnets outbound network ACL rules | list | `[ { "cidr_block": "0.0.0.0/0", "from_port": 0, "protocol": "-1", "rule_action": "allow", "rule_number": 100, "to_port": 0 } ]` | no | | redshift\_route\_table\_tags | Additional tags for the redshift route tables | map | `{}` | no | | redshift\_subnet\_group\_tags | Additional tags for the redshift subnet group | map | `{}` | no | | redshift\_subnet\_suffix | Suffix to append to redshift subnets name | string | `"redshift"` | no | @@ -299,6 +339,7 @@ Terraform version 0.10.3 or newer is required for this module to work. | Name | Description | |------|-------------| | azs | A list of availability zones specified as argument to this module | +| database\_network\_acl\_id | ID of the database network ACL | | database\_route\_table\_ids | List of IDs of database route tables | | database\_subnet\_group | ID of database subnet group | | database\_subnets | List of IDs of database subnets | @@ -315,24 +356,29 @@ Terraform version 0.10.3 or newer is required for this module to work. | default\_vpc\_id | The ID of the VPC | | default\_vpc\_instance\_tenancy | Tenancy of instances spin up within VPC | | default\_vpc\_main\_route\_table\_id | The ID of the main route table associated with this VPC | +| elasticache\_network\_acl\_id | ID of the elasticache network ACL | | elasticache\_route\_table\_ids | List of IDs of elasticache route tables | | elasticache\_subnet\_group | ID of elasticache subnet group | | elasticache\_subnet\_group\_name | Name of elasticache subnet group | | elasticache\_subnets | List of IDs of elasticache subnets | | elasticache\_subnets\_cidr\_blocks | List of cidr_blocks of elasticache subnets | | igw\_id | The ID of the Internet Gateway | +| intra\_network\_acl\_id | ID of the intra network ACL | | intra\_route\_table\_ids | List of IDs of intra route tables | | intra\_subnets | List of IDs of intra subnets | | intra\_subnets\_cidr\_blocks | List of cidr_blocks of intra subnets | | nat\_ids | List of allocation ID of Elastic IPs created for AWS NAT Gateway | | nat\_public\_ips | List of public Elastic IPs created for AWS NAT Gateway | | natgw\_ids | List of NAT Gateway IDs | +| private\_network\_acl\_id | ID of the private network ACL | | private\_route\_table\_ids | List of IDs of private route tables | | private\_subnets | List of IDs of private subnets | | private\_subnets\_cidr\_blocks | List of cidr_blocks of private subnets | +| public\_network\_acl\_id | ID of the public network ACL | | public\_route\_table\_ids | List of IDs of public route tables | | public\_subnets | List of IDs of public subnets | | public\_subnets\_cidr\_blocks | List of cidr_blocks of public subnets | +| redshift\_network\_acl\_id | ID of the redshift network ACL | | redshift\_route\_table\_ids | List of IDs of redshift route tables | | redshift\_subnet\_group | ID of redshift subnet group | | redshift\_subnets | List of IDs of redshift subnets | diff --git a/examples/network-acls/README.md b/examples/network-acls/README.md new file mode 100644 index 000000000..79305c7cd --- /dev/null +++ b/examples/network-acls/README.md @@ -0,0 +1,39 @@ +# Simple VPC with Network ACLs + +Configuration in this directory creates set of VPC resources along with network ACLs for public subnets. + +There is a public and private subnet created per availability zone in addition to single NAT Gateway shared between all 3 availability zones. + +Network ACL rules for inbound and outbound traffic are defined as the following: +1. Public subnets will have network ACL rules provided +1. Private subnets will be associated with the default network ACL rules (IPV4-only ingress and egress is open for all) +1. Elasticache subnets will use the default network ACL (created and managed by AWS) + +## Usage + +To run this example you need to execute: + +```bash +$ terraform init +$ terraform plan +$ terraform apply +``` + +Note that this example may create resources which can cost money (AWS Elastic IP, for example). Run `terraform destroy` when you don't need these resources. + + +## Outputs + +| Name | Description | +|------|-------------| +| default\_network\_acl\_id | The ID of the default network ACL | +| elasticache\_network\_acl\_id | ID of the elasticache network ACL | +| nat\_public\_ips | List of public Elastic IPs created for AWS NAT Gateway | +| private\_network\_acl\_id | ID of the private network ACL | +| private\_subnets | List of IDs of private subnets | +| public\_network\_acl\_id | ID of the public network ACL | +| public\_subnets | List of IDs of public subnets | +| vpc\_cidr\_block | The CIDR block of the VPC | +| vpc\_id | The ID of the VPC | + + diff --git a/examples/network-acls/main.tf b/examples/network-acls/main.tf new file mode 100644 index 000000000..0e3f6a29c --- /dev/null +++ b/examples/network-acls/main.tf @@ -0,0 +1,136 @@ +provider "aws" { + region = "eu-west-1" +} + +module "vpc" { + source = "../../" + + name = "network-acls-example" + + cidr = "10.0.0.0/16" + + azs = ["eu-west-1a", "eu-west-1b", "eu-west-1c"] + private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"] + public_subnets = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"] + elasticache_subnets = ["10.0.201.0/24", "10.0.202.0/24", "10.0.203.0/24"] + + public_dedicated_network_acl = true + public_inbound_acl_rules = "${concat(local.network_acls["default_inbound"], local.network_acls["public_inbound"])}" + public_outbound_acl_rules = "${concat(local.network_acls["default_outbound"], local.network_acls["public_outbound"])}" + + private_dedicated_network_acl = true + + assign_generated_ipv6_cidr_block = true + + enable_nat_gateway = false + single_nat_gateway = true + + public_subnet_tags = { + Name = "overridden-name-public" + } + + tags = { + Owner = "user" + Environment = "dev" + } + + vpc_tags = { + Name = "vpc-name" + } +} + +locals { + network_acls = { + default_inbound = [ + { + rule_number = 900 + rule_action = "allow" + from_port = 1024 + to_port = 65535 + protocol = "tcp" + cidr_block = "0.0.0.0/0" + }, + ] + + default_outbound = [ + { + rule_number = 900 + rule_action = "allow" + from_port = 32768 + to_port = 65535 + protocol = "tcp" + cidr_block = "0.0.0.0/0" + }, + ] + + public_inbound = [ + { + rule_number = 100 + rule_action = "allow" + from_port = 80 + to_port = 80 + protocol = "tcp" + cidr_block = "0.0.0.0/0" + }, + { + rule_number = 110 + rule_action = "allow" + from_port = 443 + to_port = 443 + protocol = "tcp" + cidr_block = "0.0.0.0/0" + }, + { + rule_number = 120 + rule_action = "allow" + from_port = 22 + to_port = 22 + protocol = "tcp" + cidr_block = "0.0.0.0/0" + }, + { + rule_number = 130 + rule_action = "allow" + from_port = 3389 + to_port = 3389 + protocol = "tcp" + cidr_block = "0.0.0.0/0" + }, + ] + + public_outbound = [ + { + rule_number = 100 + rule_action = "allow" + from_port = 80 + to_port = 80 + protocol = "tcp" + cidr_block = "0.0.0.0/0" + }, + { + rule_number = 110 + rule_action = "allow" + from_port = 443 + to_port = 443 + protocol = "tcp" + cidr_block = "0.0.0.0/0" + }, + { + rule_number = 120 + rule_action = "allow" + from_port = 1433 + to_port = 1433 + protocol = "tcp" + cidr_block = "10.0.100.0/22" + }, + { + rule_number = 130 + rule_action = "allow" + from_port = 22 + to_port = 22 + protocol = "tcp" + cidr_block = "10.0.100.0/22" + }, + ] + } +} diff --git a/examples/network-acls/outputs.tf b/examples/network-acls/outputs.tf new file mode 100644 index 000000000..535d5f43e --- /dev/null +++ b/examples/network-acls/outputs.tf @@ -0,0 +1,54 @@ +# VPC +output "vpc_id" { + description = "The ID of the VPC" + value = "${module.vpc.vpc_id}" +} + +# CIDR blocks +output "vpc_cidr_block" { + description = "The CIDR block of the VPC" + value = ["${module.vpc.vpc_cidr_block}"] +} + +//output "vpc_ipv6_cidr_block" { +// description = "The IPv6 CIDR block" +// value = ["${module.vpc.vpc_ipv6_cidr_block}"] +//} + +# Subnets +output "private_subnets" { + description = "List of IDs of private subnets" + value = ["${module.vpc.private_subnets}"] +} + +output "public_subnets" { + description = "List of IDs of public subnets" + value = ["${module.vpc.public_subnets}"] +} + +# NAT gateways +output "nat_public_ips" { + description = "List of public Elastic IPs created for AWS NAT Gateway" + value = ["${module.vpc.nat_public_ips}"] +} + +# Network ACLs +output "public_network_acl_id" { + description = "ID of the public network ACL" + value = "${module.vpc.public_network_acl_id}" +} + +output "private_network_acl_id" { + description = "ID of the private network ACL" + value = "${module.vpc.private_network_acl_id}" +} + +output "elasticache_network_acl_id" { + description = "ID of the elasticache network ACL" + value = "${module.vpc.elasticache_network_acl_id}" +} + +output "default_network_acl_id" { + description = "The ID of the default network ACL" + value = "${module.vpc.default_network_acl_id}" +} diff --git a/main.tf b/main.tf index 1013fd594..28fd09de4 100644 --- a/main.tf +++ b/main.tf @@ -284,6 +284,264 @@ resource "aws_subnet" "intra" { tags = "${merge(map("Name", format("%s-${var.intra_subnet_suffix}-%s", var.name, element(var.azs, count.index))), var.tags, var.intra_subnet_tags)}" } +####################### +# Default Network ACLs +####################### +resource "aws_default_network_acl" "this" { + count = "${var.create_vpc && var.manage_default_network_acl ? 1 : 0}" + + default_network_acl_id = "${element(concat(aws_vpc.this.*.default_network_acl_id, list("")), 0)}" + + ingress = "${var.default_network_acl_ingress}" + egress = "${var.default_network_acl_egress}" + + tags = "${merge(map("Name", format("%s", var.default_network_acl_name)), var.tags, var.default_network_acl_tags)}" + + lifecycle { + ignore_changes = ["subnet_ids"] + } +} + +######################## +# Public Network ACLs +######################## +resource "aws_network_acl" "public" { + count = "${var.create_vpc && var.public_dedicated_network_acl && length(var.public_subnets) > 0 ? 1 : 0}" + + vpc_id = "${element(concat(aws_vpc.this.*.id, list("")), 0)}" + subnet_ids = ["${aws_subnet.public.*.id}"] + + tags = "${merge(map("Name", format("%s-${var.public_subnet_suffix}", var.name)), var.tags, var.public_acl_tags)}" +} + +resource "aws_network_acl_rule" "public_inbound" { + count = "${var.create_vpc && var.public_dedicated_network_acl && length(var.public_subnets) > 0 ? length(var.public_inbound_acl_rules) : 0}" + + network_acl_id = "${aws_network_acl.public.id}" + + egress = false + rule_number = "${lookup(var.public_inbound_acl_rules[count.index], "rule_number")}" + rule_action = "${lookup(var.public_inbound_acl_rules[count.index], "rule_action")}" + from_port = "${lookup(var.public_inbound_acl_rules[count.index], "from_port")}" + to_port = "${lookup(var.public_inbound_acl_rules[count.index], "to_port")}" + protocol = "${lookup(var.public_inbound_acl_rules[count.index], "protocol")}" + cidr_block = "${lookup(var.public_inbound_acl_rules[count.index], "cidr_block")}" +} + +resource "aws_network_acl_rule" "public_outbound" { + count = "${var.create_vpc && var.public_dedicated_network_acl && length(var.public_subnets) > 0 ? length(var.public_outbound_acl_rules) : 0}" + + network_acl_id = "${aws_network_acl.public.id}" + + egress = true + rule_number = "${lookup(var.public_outbound_acl_rules[count.index], "rule_number")}" + rule_action = "${lookup(var.public_outbound_acl_rules[count.index], "rule_action")}" + from_port = "${lookup(var.public_outbound_acl_rules[count.index], "from_port")}" + to_port = "${lookup(var.public_outbound_acl_rules[count.index], "to_port")}" + protocol = "${lookup(var.public_outbound_acl_rules[count.index], "protocol")}" + cidr_block = "${lookup(var.public_outbound_acl_rules[count.index], "cidr_block")}" +} + +####################### +# Private Network ACLs +####################### +resource "aws_network_acl" "private" { + count = "${var.create_vpc && var.private_dedicated_network_acl && length(var.private_subnets) > 0 ? 1 : 0}" + + vpc_id = "${element(concat(aws_vpc.this.*.id, list("")), 0)}" + subnet_ids = ["${aws_subnet.private.*.id}"] + + tags = "${merge(map("Name", format("%s-${var.private_subnet_suffix}", var.name)), var.tags, var.private_acl_tags)}" +} + +resource "aws_network_acl_rule" "private_inbound" { + count = "${var.create_vpc && var.private_dedicated_network_acl && length(var.private_subnets) > 0 ? length(var.private_inbound_acl_rules) : 0}" + + network_acl_id = "${aws_network_acl.private.id}" + + egress = false + rule_number = "${lookup(var.private_inbound_acl_rules[count.index], "rule_number")}" + rule_action = "${lookup(var.private_inbound_acl_rules[count.index], "rule_action")}" + from_port = "${lookup(var.private_inbound_acl_rules[count.index], "from_port")}" + to_port = "${lookup(var.private_inbound_acl_rules[count.index], "to_port")}" + protocol = "${lookup(var.private_inbound_acl_rules[count.index], "protocol")}" + cidr_block = "${lookup(var.private_inbound_acl_rules[count.index], "cidr_block")}" +} + +resource "aws_network_acl_rule" "private_outbound" { + count = "${var.create_vpc && var.private_dedicated_network_acl && length(var.private_subnets) > 0 ? length(var.private_outbound_acl_rules) : 0}" + + network_acl_id = "${aws_network_acl.private.id}" + + egress = true + rule_number = "${lookup(var.private_outbound_acl_rules[count.index], "rule_number")}" + rule_action = "${lookup(var.private_outbound_acl_rules[count.index], "rule_action")}" + from_port = "${lookup(var.private_outbound_acl_rules[count.index], "from_port")}" + to_port = "${lookup(var.private_outbound_acl_rules[count.index], "to_port")}" + protocol = "${lookup(var.private_outbound_acl_rules[count.index], "protocol")}" + cidr_block = "${lookup(var.private_outbound_acl_rules[count.index], "cidr_block")}" +} + +######################## +# Intra Network ACLs +######################## +resource "aws_network_acl" "intra" { + count = "${var.create_vpc && var.intra_dedicated_network_acl && length(var.intra_subnets) > 0 ? 1 : 0}" + + vpc_id = "${element(concat(aws_vpc.this.*.id, list("")), 0)}" + subnet_ids = ["${aws_subnet.intra.*.id}"] + + tags = "${merge(map("Name", format("%s-${var.intra_subnet_suffix}", var.name)), var.tags, var.intra_acl_tags)}" +} + +resource "aws_network_acl_rule" "intra_inbound" { + count = "${var.create_vpc && var.intra_dedicated_network_acl && length(var.intra_subnets) > 0 ? length(var.intra_inbound_acl_rules) : 0}" + + network_acl_id = "${aws_network_acl.intra.id}" + + egress = false + rule_number = "${lookup(var.intra_inbound_acl_rules[count.index], "rule_number")}" + rule_action = "${lookup(var.intra_inbound_acl_rules[count.index], "rule_action")}" + from_port = "${lookup(var.intra_inbound_acl_rules[count.index], "from_port")}" + to_port = "${lookup(var.intra_inbound_acl_rules[count.index], "to_port")}" + protocol = "${lookup(var.intra_inbound_acl_rules[count.index], "protocol")}" + cidr_block = "${lookup(var.intra_inbound_acl_rules[count.index], "cidr_block")}" +} + +resource "aws_network_acl_rule" "intra_outbound" { + count = "${var.create_vpc && var.intra_dedicated_network_acl && length(var.intra_subnets) > 0 ? length(var.intra_outbound_acl_rules) : 0}" + + network_acl_id = "${aws_network_acl.intra.id}" + + egress = true + rule_number = "${lookup(var.intra_outbound_acl_rules[count.index], "rule_number")}" + rule_action = "${lookup(var.intra_outbound_acl_rules[count.index], "rule_action")}" + from_port = "${lookup(var.intra_outbound_acl_rules[count.index], "from_port")}" + to_port = "${lookup(var.intra_outbound_acl_rules[count.index], "to_port")}" + protocol = "${lookup(var.intra_outbound_acl_rules[count.index], "protocol")}" + cidr_block = "${lookup(var.intra_outbound_acl_rules[count.index], "cidr_block")}" +} + +######################## +# Database Network ACLs +######################## +resource "aws_network_acl" "database" { + count = "${var.create_vpc && var.database_dedicated_network_acl && length(var.database_subnets) > 0 ? 1 : 0}" + + vpc_id = "${element(concat(aws_vpc.this.*.id, list("")), 0)}" + subnet_ids = ["${aws_subnet.database.*.id}"] + + tags = "${merge(map("Name", format("%s-${var.database_subnet_suffix}", var.name)), var.tags, var.database_acl_tags)}" +} + +resource "aws_network_acl_rule" "database_inbound" { + count = "${var.create_vpc && var.database_dedicated_network_acl && length(var.database_subnets) > 0 ? length(var.database_inbound_acl_rules) : 0}" + + network_acl_id = "${aws_network_acl.database.id}" + + egress = false + rule_number = "${lookup(var.database_inbound_acl_rules[count.index], "rule_number")}" + rule_action = "${lookup(var.database_inbound_acl_rules[count.index], "rule_action")}" + from_port = "${lookup(var.database_inbound_acl_rules[count.index], "from_port")}" + to_port = "${lookup(var.database_inbound_acl_rules[count.index], "to_port")}" + protocol = "${lookup(var.database_inbound_acl_rules[count.index], "protocol")}" + cidr_block = "${lookup(var.database_inbound_acl_rules[count.index], "cidr_block")}" +} + +resource "aws_network_acl_rule" "database_outbound" { + count = "${var.create_vpc && var.database_dedicated_network_acl && length(var.database_subnets) > 0 ? length(var.database_outbound_acl_rules) : 0}" + + network_acl_id = "${aws_network_acl.database.id}" + + egress = true + rule_number = "${lookup(var.database_outbound_acl_rules[count.index], "rule_number")}" + rule_action = "${lookup(var.database_outbound_acl_rules[count.index], "rule_action")}" + from_port = "${lookup(var.database_outbound_acl_rules[count.index], "from_port")}" + to_port = "${lookup(var.database_outbound_acl_rules[count.index], "to_port")}" + protocol = "${lookup(var.database_outbound_acl_rules[count.index], "protocol")}" + cidr_block = "${lookup(var.database_outbound_acl_rules[count.index], "cidr_block")}" +} + +######################## +# Redshift Network ACLs +######################## +resource "aws_network_acl" "redshift" { + count = "${var.create_vpc && var.redshift_dedicated_network_acl && length(var.redshift_subnets) > 0 ? 1 : 0}" + + vpc_id = "${element(concat(aws_vpc.this.*.id, list("")), 0)}" + subnet_ids = ["${aws_subnet.redshift.*.id}"] + + tags = "${merge(map("Name", format("%s-${var.redshift_subnet_suffix}", var.name)), var.tags, var.redshift_acl_tags)}" +} + +resource "aws_network_acl_rule" "redshift_inbound" { + count = "${var.create_vpc && var.redshift_dedicated_network_acl && length(var.redshift_subnets) > 0 ? length(var.redshift_inbound_acl_rules) : 0}" + + network_acl_id = "${aws_network_acl.redshift.id}" + + egress = false + rule_number = "${lookup(var.redshift_inbound_acl_rules[count.index], "rule_number")}" + rule_action = "${lookup(var.redshift_inbound_acl_rules[count.index], "rule_action")}" + from_port = "${lookup(var.redshift_inbound_acl_rules[count.index], "from_port")}" + to_port = "${lookup(var.redshift_inbound_acl_rules[count.index], "to_port")}" + protocol = "${lookup(var.redshift_inbound_acl_rules[count.index], "protocol")}" + cidr_block = "${lookup(var.redshift_inbound_acl_rules[count.index], "cidr_block")}" +} + +resource "aws_network_acl_rule" "redshift_outbound" { + count = "${var.create_vpc && var.redshift_dedicated_network_acl && length(var.redshift_subnets) > 0 ? length(var.redshift_outbound_acl_rules) : 0}" + + network_acl_id = "${aws_network_acl.redshift.id}" + + egress = true + rule_number = "${lookup(var.redshift_outbound_acl_rules[count.index], "rule_number")}" + rule_action = "${lookup(var.redshift_outbound_acl_rules[count.index], "rule_action")}" + from_port = "${lookup(var.redshift_outbound_acl_rules[count.index], "from_port")}" + to_port = "${lookup(var.redshift_outbound_acl_rules[count.index], "to_port")}" + protocol = "${lookup(var.redshift_outbound_acl_rules[count.index], "protocol")}" + cidr_block = "${lookup(var.redshift_outbound_acl_rules[count.index], "cidr_block")}" +} + +########################### +# Elasticache Network ACLs +########################### +resource "aws_network_acl" "elasticache" { + count = "${var.create_vpc && var.elasticache_dedicated_network_acl && length(var.elasticache_subnets) > 0 ? 1 : 0}" + + vpc_id = "${element(concat(aws_vpc.this.*.id, list("")), 0)}" + subnet_ids = ["${aws_subnet.elasticache.*.id}"] + + tags = "${merge(map("Name", format("%s-${var.elasticache_subnet_suffix}", var.name)), var.tags, var.elasticache_acl_tags)}" +} + +resource "aws_network_acl_rule" "elasticache_inbound" { + count = "${var.create_vpc && var.elasticache_dedicated_network_acl && length(var.elasticache_subnets) > 0 ? length(var.elasticache_inbound_acl_rules) : 0}" + + network_acl_id = "${aws_network_acl.elasticache.id}" + + egress = false + rule_number = "${lookup(var.elasticache_inbound_acl_rules[count.index], "rule_number")}" + rule_action = "${lookup(var.elasticache_inbound_acl_rules[count.index], "rule_action")}" + from_port = "${lookup(var.elasticache_inbound_acl_rules[count.index], "from_port")}" + to_port = "${lookup(var.elasticache_inbound_acl_rules[count.index], "to_port")}" + protocol = "${lookup(var.elasticache_inbound_acl_rules[count.index], "protocol")}" + cidr_block = "${lookup(var.elasticache_inbound_acl_rules[count.index], "cidr_block")}" +} + +resource "aws_network_acl_rule" "elasticache_outbound" { + count = "${var.create_vpc && var.elasticache_dedicated_network_acl && length(var.elasticache_subnets) > 0 ? length(var.elasticache_outbound_acl_rules) : 0}" + + network_acl_id = "${aws_network_acl.elasticache.id}" + + egress = true + rule_number = "${lookup(var.elasticache_outbound_acl_rules[count.index], "rule_number")}" + rule_action = "${lookup(var.elasticache_outbound_acl_rules[count.index], "rule_action")}" + from_port = "${lookup(var.elasticache_outbound_acl_rules[count.index], "from_port")}" + to_port = "${lookup(var.elasticache_outbound_acl_rules[count.index], "to_port")}" + protocol = "${lookup(var.elasticache_outbound_acl_rules[count.index], "protocol")}" + cidr_block = "${lookup(var.elasticache_outbound_acl_rules[count.index], "cidr_block")}" +} + ############## # NAT Gateway ############## diff --git a/outputs.tf b/outputs.tf index 277706622..d885ce35e 100644 --- a/outputs.tf +++ b/outputs.tf @@ -258,6 +258,36 @@ output "default_vpc_main_route_table_id" { // value = "${element(concat(aws_default_vpc.this.*.ipv6_cidr_block, list("")), 0)}" //} +output "public_network_acl_id" { + description = "ID of the public network ACL" + value = "${element(concat(aws_network_acl.public.*.id, list("")), 0)}" +} + +output "private_network_acl_id" { + description = "ID of the private network ACL" + value = "${element(concat(aws_network_acl.private.*.id, list("")), 0)}" +} + +output "intra_network_acl_id" { + description = "ID of the intra network ACL" + value = "${element(concat(aws_network_acl.intra.*.id, list("")), 0)}" +} + +output "database_network_acl_id" { + description = "ID of the database network ACL" + value = "${element(concat(aws_network_acl.database.*.id, list("")), 0)}" +} + +output "redshift_network_acl_id" { + description = "ID of the redshift network ACL" + value = "${element(concat(aws_network_acl.redshift.*.id, list("")), 0)}" +} + +output "elasticache_network_acl_id" { + description = "ID of the elasticache network ACL" + value = "${element(concat(aws_network_acl.elasticache.*.id, list("")), 0)}" +} + # VPC Endpoints output "vpc_endpoint_s3_id" { description = "The ID of VPC endpoint for S3" diff --git a/variables.tf b/variables.tf index 2cde7136d..9d6911bb6 100644 --- a/variables.tf +++ b/variables.tf @@ -443,6 +443,36 @@ variable "intra_subnet_tags" { default = {} } +variable "public_acl_tags" { + description = "Additional tags for the public subnets network ACL" + default = {} +} + +variable "private_acl_tags" { + description = "Additional tags for the private subnets network ACL" + default = {} +} + +variable "intra_acl_tags" { + description = "Additional tags for the intra subnets network ACL" + default = {} +} + +variable "database_acl_tags" { + description = "Additional tags for the database subnets network ACL" + default = {} +} + +variable "redshift_acl_tags" { + description = "Additional tags for the redshift subnets network ACL" + default = {} +} + +variable "elasticache_acl_tags" { + description = "Additional tags for the elasticache subnets network ACL" + default = {} +} + variable "dhcp_options_tags" { description = "Additional tags for the DHCP option set" default = {} @@ -525,3 +555,272 @@ variable "default_vpc_tags" { description = "Additional tags for the Default VPC" default = {} } + +variable "manage_default_network_acl" { + description = "Should be true to adopt and manage Default Network ACL" + default = false +} + +variable "default_network_acl_name" { + description = "Name to be used on the Default Network ACL" + default = "" +} + +variable "default_network_acl_tags" { + description = "Additional tags for the Default Network ACL" + default = {} +} + +variable "public_dedicated_network_acl" { + description = "Whether to use dedicated network ACL (not default) and custom rules for public subnets" + default = false +} + +variable "private_dedicated_network_acl" { + description = "Whether to use dedicated network ACL (not default) and custom rules for private subnets" + default = false +} + +variable "intra_dedicated_network_acl" { + description = "Whether to use dedicated network ACL (not default) and custom rules for intra subnets" + default = false +} + +variable "database_dedicated_network_acl" { + description = "Whether to use dedicated network ACL (not default) and custom rules for database subnets" + default = false +} + +variable "redshift_dedicated_network_acl" { + description = "Whether to use dedicated network ACL (not default) and custom rules for redshift subnets" + default = false +} + +variable "elasticache_dedicated_network_acl" { + description = "Whether to use dedicated network ACL (not default) and custom rules for elasticache subnets" + default = false +} + +variable "default_network_acl_ingress" { + description = "List of maps of ingress rules to set on the Default Network ACL" + + default = [{ + rule_no = 100 + action = "allow" + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_block = "0.0.0.0/0" + }, + { + rule_no = 101 + action = "allow" + from_port = 0 + to_port = 0 + protocol = "-1" + ipv6_cidr_block = "::/0" + }, + ] +} + +variable "default_network_acl_egress" { + description = "List of maps of egress rules to set on the Default Network ACL" + + default = [{ + rule_no = 100 + action = "allow" + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_block = "0.0.0.0/0" + }, + { + rule_no = 101 + action = "allow" + from_port = 0 + to_port = 0 + protocol = "-1" + ipv6_cidr_block = "::/0" + }, + ] +} + +variable "public_inbound_acl_rules" { + description = "Public subnets inbound network ACLs" + + default = [ + { + rule_number = 100 + rule_action = "allow" + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_block = "0.0.0.0/0" + }, + ] +} + +variable "public_outbound_acl_rules" { + description = "Public subnets outbound network ACLs" + + default = [ + { + rule_number = 100 + rule_action = "allow" + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_block = "0.0.0.0/0" + }, + ] +} + +variable "private_inbound_acl_rules" { + description = "Private subnets inbound network ACLs" + + default = [ + { + rule_number = 100 + rule_action = "allow" + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_block = "0.0.0.0/0" + }, + ] +} + +variable "private_outbound_acl_rules" { + description = "Private subnets outbound network ACLs" + + default = [ + { + rule_number = 100 + rule_action = "allow" + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_block = "0.0.0.0/0" + }, + ] +} + +variable "intra_inbound_acl_rules" { + description = "Intra subnets inbound network ACLs" + + default = [ + { + rule_number = 100 + rule_action = "allow" + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_block = "0.0.0.0/0" + }, + ] +} + +variable "intra_outbound_acl_rules" { + description = "Intra subnets outbound network ACLs" + + default = [ + { + rule_number = 100 + rule_action = "allow" + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_block = "0.0.0.0/0" + }, + ] +} + +variable "database_inbound_acl_rules" { + description = "Database subnets inbound network ACL rules" + + default = [ + { + rule_number = 100 + rule_action = "allow" + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_block = "0.0.0.0/0" + }, + ] +} + +variable "database_outbound_acl_rules" { + description = "Database subnets outbound network ACL rules" + + default = [ + { + rule_number = 100 + rule_action = "allow" + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_block = "0.0.0.0/0" + }, + ] +} + +variable "redshift_inbound_acl_rules" { + description = "Redshift subnets inbound network ACL rules" + + default = [ + { + rule_number = 100 + rule_action = "allow" + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_block = "0.0.0.0/0" + }, + ] +} + +variable "redshift_outbound_acl_rules" { + description = "Redshift subnets outbound network ACL rules" + + default = [ + { + rule_number = 100 + rule_action = "allow" + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_block = "0.0.0.0/0" + }, + ] +} + +variable "elasticache_inbound_acl_rules" { + description = "Elasticache subnets inbound network ACL rules" + + default = [ + { + rule_number = 100 + rule_action = "allow" + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_block = "0.0.0.0/0" + }, + ] +} + +variable "elasticache_outbound_acl_rules" { + description = "Elasticache subnets outbound network ACL rules" + + default = [ + { + rule_number = 100 + rule_action = "allow" + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_block = "0.0.0.0/0" + }, + ] +}