Skip to main content
VPC Modules 0.26.17Last updated in version 0.26.8

VPC-App Network ACLs Terraform Module

View SourceRelease Notes

This Terraform Module adds a default set of Network ACLs to a VPC created using the vpc-app module. The ACLs enforce the following security settings (based on A Reference VPC Architecture):

  • Public subnet: Allow all requests.
  • Private app subnet: Allow all requests to/from the public subnets, private persistence subnets, and the Mgmt VPC. Allow all outbound TCP requests plus return traffic from any IP for those TCP requests on ephemeral ports.
  • Private persistence subnet: Allow all requests to/from the private app subnets and the Mgmt VPC.

What's a VPC?

A VPC or Virtual Private Cloud is a logically isolated section of your AWS cloud. Each VPC defines a virtual network within which you run your AWS resources, as well as rules for what can go in and out of that network. This includes subnets, route tables that tell those subnets how to route inbound and outbound traffic, security groups, access controls lists for the network (NACLs), and any other network components such as VPN connections.

What's a Network ACL?

Network ACLs provide an extra layer of network security, similar to a security group. Whereas a security group controls what inbound and outbound traffic is allowed for a specific resource (e.g. a single EC2 instance), a network ACL controls what inbound and outbound traffic is allowed for an entire subnet.

How do I configure the Network ACLs for public ELB access?

The recommended configuration for public Elastic Load Balancers is to deploy the public facing ELB (Application Load Balancer/ALB or Network Load Balancer/NLB) in the public subnet tier, and the applications in the private app subnet tier. For the most part, the Network ACLs configured in this module should be sufficient for exposing access to the private services through the public ELB.

However, for NLBs, the default network ACLs in the module would restrict access to the private services routed from the NLB if the application is listening on a privileged port (port numbers less than 1024, e.g. HTTP port 80). This is because unlike ALBs, NLBs do not do address translation, and thus the VPC firewalls end up seeing the client IP address instead of the NLB IP address. This triggers the firewall rules in the network ACLs that will block access to the private service in the private app subnet, as the traffic will not appear to come from the public subnet tier.

To ensure the NLB traffic can make it to the private service, you must expose access to the privileged port from the client IP address in the network ACL rules. To do this, you can use the private_app_allow_inbound_ports_from_cidr input variable. For example, to allow access to a service listening on port 443 (HTTPS):

module "network_acls" {
# other arguments omitted for brevity

private_app_allow_inbound_ports_from_cidr = {
AllowAnyPublicHTTP = {
client_cidr_block = "0.0.0.0/0"
protocol = "tcp"
from_port = 443
to_port = 443
icmp_type = null
icmp_code = null

# We pick rule number 99 to ensure it has the highest priority.
rule_number = 99
}
}
}

Sample Usage

main.tf

# ------------------------------------------------------------------------------------------------------
# DEPLOY GRUNTWORK'S VPC-APP-NETWORK-ACLS MODULE
# ------------------------------------------------------------------------------------------------------

module "vpc_app_network_acls" {

source = "git::git@github.com:gruntwork-io/terraform-aws-vpc.git//modules/vpc-app-network-acls?ref=v0.26.17"

# ----------------------------------------------------------------------------------------------------
# REQUIRED VARIABLES
# ----------------------------------------------------------------------------------------------------

# The number of each type of subnet (public, private, private persistence)
# created in this VPC. Typically, this is equal to the number of availability
# zones in the current region. We should be able to compute this
# automatically, but due to a Terraform bug, we can't:
# https://github.com/hashicorp/terraform/issues/3888
num_subnets = <number>

# A list of CIDR blocks used by the private app subnets in the VPC
private_app_subnet_cidr_blocks = <list(string)>

# A list of IDs of the private app subnets in the VPC
private_app_subnet_ids = <list(string)>

# A list of CIDR blocks used by the private persistence subnets in the VPC
private_persistence_subnet_cidr_blocks = <list(string)>

# A list of IDs of the private persistence subnets in the VPC
private_persistence_subnet_ids = <list(string)>

# A list of CIDR blocks used by the public subnets in the VPC
public_subnet_cidr_blocks = <list(string)>

# A list of IDs of the public subnets in the VPC
public_subnet_ids = <list(string)>

# The id of the VPC
vpc_id = <string>

# The name of the VPC (e.g. stage, prod)
vpc_name = <string>

# ----------------------------------------------------------------------------------------------------
# OPTIONAL VARIABLES
# ----------------------------------------------------------------------------------------------------

# If set to true, the network ACLs will allow incoming requests from the Mgmt
# VPC CIDR block defined in var.mgmt_vpc_cidr_block.
allow_access_from_mgmt_vpc = false

# If set to false, this module will NOT create the NACLs for the private app
# subnet tier.
create_private_app_subnet_nacls = true

# If set to false, this module will NOT create the NACLs for the private
# persistence subnet tier.
create_private_persistence_subnet_nacls = true

# If set to false, this module will NOT create the NACLs for the public subnet
# tier. This is useful for VPCs that only need private subnets.
create_public_subnet_nacls = true

# If you set this variable to false, this module will not create any
# resources. This is used as a workaround because Terraform does not allow you
# to use the 'count' parameter on modules. By using this parameter, you can
# optionally create or not create the resources within this module.
create_resources = true

# A map of tags to apply to the Network ACLs created by this module. The key
# is the tag name and the value is the tag value. Note that the tag 'Name' is
# automatically added by this module but may be optionally overwritten by this
# variable.
custom_tags = {}

# The list of ports to exclude from the inbound allow all rules. This is
# useful for adhering to certain compliance standards like CIS that explicitly
# deny any allow rule for administrative ports.
exclude_ports_from_inbound_all = []

# The number to use for the first rule that is created by this module. All
# rules in this module will be inserted after this number. This is useful to
# provide additional head room for your NACL rules that should take precedence
# over the initial rule.
initial_nacl_rule_number = 100

# The CIDR block of the Mgmt VPC. All subnets will allow connections from this
# CIDR block. Only used if var.allow_access_from_mgmt_vpc is set to true.
mgmt_vpc_cidr_block = null

# A map of unique names to client IP CIDR block and inbound ports that should
# be exposed in the private app subnet tier nACLs. This is useful when
# exposing your service on a privileged port with an NLB, where the address
# isn't translated.
private_app_allow_inbound_ports_from_cidr = {}

# A map of unique names to destination IP CIDR block and outbound ports that
# should be allowed in the private app subnet tier nACLs. This is useful when
# allowing your VPC specific outbound communication to defined CIDR
# blocks(known networks)
private_app_allow_outbound_ports_to_destination_cidr = {}

# Use this variable to ensure the Network ACL does not get created until the
# VPC is ready. This can help to work around a Terraform or AWS issue where
# trying to create certain resources, such as Network ACLs, before the VPC's
# Gateway and NATs are ready, leads to a huge variety of eventual consistency
# bugs. You should typically point this variable at the vpc_ready output from
# the Gruntwork VPCs.
vpc_ready = null

}


Reference

Required

num_subnetsnumberrequired

The number of each type of subnet (public, private, private persistence) created in this VPC. Typically, this is equal to the number of availability zones in the current region. We should be able to compute this automatically, but due to a Terraform bug, we can't: https://github.com/hashicorp/terraform/issues/3888

private_app_subnet_cidr_blockslist(string)required

A list of CIDR blocks used by the private app subnets in the VPC

private_app_subnet_idslist(string)required

A list of IDs of the private app subnets in the VPC

A list of CIDR blocks used by the private persistence subnets in the VPC

private_persistence_subnet_idslist(string)required

A list of IDs of the private persistence subnets in the VPC

public_subnet_cidr_blockslist(string)required

A list of CIDR blocks used by the public subnets in the VPC

public_subnet_idslist(string)required

A list of IDs of the public subnets in the VPC

vpc_idstringrequired

The id of the VPC

vpc_namestringrequired

The name of the VPC (e.g. stage, prod)

Optional

If set to true, the network ACLs will allow incoming requests from the Mgmt VPC CIDR block defined in mgmt_vpc_cidr_block.

false

If set to false, this module will NOT create the NACLs for the private app subnet tier.

true

If set to false, this module will NOT create the NACLs for the private persistence subnet tier.

true

If set to false, this module will NOT create the NACLs for the public subnet tier. This is useful for VPCs that only need private subnets.

true
create_resourcesbooloptional

If you set this variable to false, this module will not create any resources. This is used as a workaround because Terraform does not allow you to use the 'count' parameter on modules. By using this parameter, you can optionally create or not create the resources within this module.

true
custom_tagsmap(string)optional

A map of tags to apply to the Network ACLs created by this module. The key is the tag name and the value is the tag value. Note that the tag 'Name' is automatically added by this module but may be optionally overwritten by this variable.

{}
exclude_ports_from_inbound_alllist(number)optional

The list of ports to exclude from the inbound allow all rules. This is useful for adhering to certain compliance standards like CIS that explicitly deny any allow rule for administrative ports.

[]

The number to use for the first rule that is created by this module. All rules in this module will be inserted after this number. This is useful to provide additional head room for your NACL rules that should take precedence over the initial rule.

100
mgmt_vpc_cidr_blockstringoptional

The CIDR block of the Mgmt VPC. All subnets will allow connections from this CIDR block. Only used if allow_access_from_mgmt_vpc is set to true.

null

A map of unique names to client IP CIDR block and inbound ports that should be exposed in the private app subnet tier nACLs. This is useful when exposing your service on a privileged port with an NLB, where the address isn't translated.

{}
Details

A rule number indicating priority. A lower number has precedence. Note that the default rules created by this
module start with var.initial_nacl_rule_number.

Details

Network protocol (tcp, udp, icmp, or all) to expose.

Details

Range of ports to expose.

Details

ICMP types to expose
Required if specifying ICMP for the protocol

A map of unique names to destination IP CIDR block and outbound ports that should be allowed in the private app subnet tier nACLs. This is useful when allowing your VPC specific outbound communication to defined CIDR blocks(known networks)

{}
Details

A rule number indicating priority. A lower number has precedence. Note that the default rules created by this
module start with 100.

Details

Network protocol (tcp, udp, icmp, or all) to expose.

Details

Range of ports to expose.

Details

ICMP types to expose
Required if specifying ICMP for the protocol

vpc_readystringoptional

Use this variable to ensure the Network ACL does not get created until the VPC is ready. This can help to work around a Terraform or AWS issue where trying to create certain resources, such as Network ACLs, before the VPC's Gateway and NATs are ready, leads to a huge variety of eventual consistency bugs. You should typically point this variable at the vpc_ready output from the Gruntwork VPCs.

null