Skip to content

Commit

Permalink
feat: enable the creation of private NAT gateways without internet ga…
Browse files Browse the repository at this point in the history
…teways

fixes #1060
  • Loading branch information
pierre authored and pierre committed Apr 27, 2024
1 parent c182453 commit 8f62e62
Show file tree
Hide file tree
Showing 8 changed files with 136 additions and 1 deletion.
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,23 @@ If `one_nat_gateway_per_az = true` and `single_nat_gateway = false`, then the mo
- The variable `var.azs` **must** be specified.
- The number of public subnet CIDR blocks specified in `public_subnets` **must** be greater than or equal to the number of availability zones specified in `var.azs`. This is to ensure that each NAT Gateway has a dedicated public subnet to deploy to.

### Fully private NAT Gateway

```hcl
module vpc {
source = "terraform-aws-modules/vpc/aws"
# The rest of arguments are omitted for brevity
enable_nat_gateway = true
single_nat_gateway = false
create_igw = false
nat_gateway_connectivity_type = "private"
use_nat_gateway_private_ips = []
}
```

## "private" versus "intra" subnets

By default, if NAT Gateways are enabled, private subnets will be configured with routes for Internet traffic that point at the NAT Gateways configured by use of the above options.
Expand All @@ -118,6 +135,10 @@ Since AWS Lambda functions allocate Elastic Network Interfaces in proportion to

You can add additional tags with `intra_subnet_tags` as with other subnet types.

## private NAT solution to address IP exhaustion

If you need to setup overlapping CIDR ranges in your VPCs to prevent IP exhaustion and need internet access inside your private subnets. You will need to define fully private NAT gateways. Please refer to the AWS page defining the solution [here](https://docs.aws.amazon.com/whitepapers/latest/building-scalable-secure-multi-vpc-network-infrastructure/private-nat-gateway.html) for more details. You can enable this by defining private subnets inside the non routable IP address range while setuping the NAT gateways in the routable IP address range and enabling the private mode by defining the connectivity type to `private`.

## VPC Flow Log

VPC Flow Log allows to capture IP traffic for a specific network interface (ENI), subnet, or entire VPC. This module supports enabling or disabling VPC Flow Logs for entire VPC. If you need to have VPC Flow Logs for subnet or ENI, you have to manage it outside of this module with [aws_flow_log resource](https://www.terraform.io/docs/providers/aws/r/flow_log.html).
Expand Down Expand Up @@ -492,7 +513,11 @@ No modules.
| <a name="input_map_public_ip_on_launch"></a> [map\_public\_ip\_on\_launch](#input\_map\_public\_ip\_on\_launch) | Specify true to indicate that instances launched into the subnet should be assigned a public IP address. Default is `false` | `bool` | `false` | no |
| <a name="input_name"></a> [name](#input\_name) | Name to be used on all the resources as identifier | `string` | `""` | no |
| <a name="input_nat_eip_tags"></a> [nat\_eip\_tags](#input\_nat\_eip\_tags) | Additional tags for the NAT EIP | `map(string)` | `{}` | no |
| <a name="input_nat_gateway_connectivity_type"></a> [nat\_gateway\_connectivity\_type](#input\_nat\_gateway\_connectivity\_type) | Connectivity type for the NAT Gateway | `string` | `"public"` | no |
| <a name="input_nat_gateway_destination_cidr_block"></a> [nat\_gateway\_destination\_cidr\_block](#input\_nat\_gateway\_destination\_cidr\_block) | Used to pass a custom destination route for private NAT Gateway. If not specified, the default 0.0.0.0/0 is used as a destination route | `string` | `"0.0.0.0/0"` | no |
| <a name="input_nat_gateway_private_ips"></a> [nat\_gateway\_private\_ips](#input\_nat\_gateway\_private\_ips) | The private IPv4 address to assign to the NAT Gateways | `list(string)` | `[]` | no |
| <a name="input_nat_gateway_secondary_private_ip_address_count"></a> [nat\_gateway\_secondary\_private\_ip\_address\_count](#input\_nat\_gateway\_secondary\_private\_ip\_address\_count) | The number of secondary private IPv4 addresses you want to assign to the NAT Gateways | `list(number)` | `[]` | no |
| <a name="input_nat_gateway_secondary_private_ip_addresses"></a> [nat\_gateway\_secondary\_private\_ip\_addresses](#input\_nat\_gateway\_secondary\_private\_ip\_addresses) | List of secondary IPv4 address lists to assign to the NAT Gateways | `list(list(string))` | `[]` | no |
| <a name="input_nat_gateway_tags"></a> [nat\_gateway\_tags](#input\_nat\_gateway\_tags) | Additional tags for the NAT gateways | `map(string)` | `{}` | no |
| <a name="input_one_nat_gateway_per_az"></a> [one\_nat\_gateway\_per\_az](#input\_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` | `bool` | `false` | no |
| <a name="input_outpost_acl_tags"></a> [outpost\_acl\_tags](#input\_outpost\_acl\_tags) | Additional tags for the outpost subnets network ACL | `map(string)` | `{}` | no |
Expand Down
34 changes: 34 additions & 0 deletions examples/fully-private-nat-gateways/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<!-- BEGIN_TF_DOCS -->
## Requirements

| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.0 |
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 5.30 |

## Providers

| Name | Version |
|------|---------|
| <a name="provider_aws"></a> [aws](#provider\_aws) | >= 5.30 |

## Modules

| Name | Source | Version |
|------|--------|---------|
| <a name="module_vpc"></a> [vpc](#module\_vpc) | ../.. | n/a |

## Resources

| Name | Type |
|------|------|
| [aws_ec2_transit_gateway_vpc_attachment.tgw_attach](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ec2_transit_gateway_vpc_attachment) | resource |

## Inputs

No inputs.

## Outputs

No outputs.
<!-- END_TF_DOCS -->
37 changes: 37 additions & 0 deletions examples/fully-private-nat-gateways/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
locals {
tgw_id = "tgw-12345678"
}

resource "aws_ec2_transit_gateway_vpc_attachment" "tgw_attach" {
subnet_ids = module.vpc.private_subnets
transit_gateway_id = local.tgw_id
vpc_id = module.vpc.vpc_id
}

module "vpc" {
source = "../.."

cidr = "10.247.84.0/22"
secondary_cidr_blocks = ["100.120.0.0/16"]

azs = ["us-west-2a", "us-west-2b", "us-west-2c"]

create_igw = false
nat_gateway_connectivity_type = "private"
nat_gateway_private_ips = ["10.247.84.122", "10.247.85.204", "10.247.86.117"]
nat_gateway_secondary_private_ip_addresses = [["10.247.84.133"], ["10.247.85.79", "10.247.85.66"], ["10.247.86.64", "10.247.86.47", "10.247.86.181"]]

# You can automatically set the number of secondary private IPs for the NAT Gateways, that is a conflicting argumebt with nat_gateway_secondary_private_ip_addresses
# nat_gateway_secondary_private_ip_address_count = [1, 2, 3]

# In this scenario the NAT gateways are created inside public subnets i.e without an igw in the routes.
public_subnets = ["10.247.84.0/24", "10.247.85.0/24", "10.247.86.0/24"]

# The private subnets are using a range of IPs that belongs to the non routable IP range
private_subnets = ["100.120.4.0/22", "100.120.8.0/22", "100.120.12.0/22"]

enable_nat_gateway = true
single_nat_gateway = false
one_nat_gateway_per_az = true

}
Empty file.
Empty file.
10 changes: 10 additions & 0 deletions examples/fully-private-nat-gateways/versions.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
terraform {
required_version = ">= 1.0"

required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 5.30"
}
}
}
7 changes: 6 additions & 1 deletion main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -1059,7 +1059,7 @@ resource "aws_eip" "nat" {
resource "aws_nat_gateway" "this" {
count = local.create_vpc && var.enable_nat_gateway ? local.nat_gateway_count : 0

allocation_id = element(
allocation_id = var.nat_gateway_connectivity_type == "private" ? null : element(
local.nat_gateway_ips,
var.single_nat_gateway ? 0 : count.index,
)
Expand All @@ -1068,6 +1068,11 @@ resource "aws_nat_gateway" "this" {
var.single_nat_gateway ? 0 : count.index,
)

connectivity_type = var.nat_gateway_connectivity_type
private_ip = length(var.nat_gateway_private_ips) > 0 ? var.nat_gateway_private_ips[count.index] : null
secondary_private_ip_addresses = length(var.nat_gateway_secondary_private_ip_addresses) > 0 ? var.nat_gateway_secondary_private_ip_addresses[count.index] : null
secondary_private_ip_address_count = length(var.nat_gateway_secondary_private_ip_address_count) > 0 ? var.nat_gateway_secondary_private_ip_address_count[count.index] : null

tags = merge(
{
"Name" = format(
Expand Down
24 changes: 24 additions & 0 deletions variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -1210,6 +1210,30 @@ variable "external_nat_ips" {
default = []
}

variable "nat_gateway_connectivity_type" {
description = "Connectivity type for the NAT Gateway"
type = string
default = "public"
}

variable "nat_gateway_private_ips" {
description = "The private IPv4 address to assign to the NAT Gateways"
type = list(string)
default = []
}

variable "nat_gateway_secondary_private_ip_addresses" {
description = "List of secondary IPv4 address lists to assign to the NAT Gateways"
type = list(list(string))
default = []
}

variable "nat_gateway_secondary_private_ip_address_count" {
description = "The number of secondary private IPv4 addresses you want to assign to the NAT Gateways"
type = list(number)
default = []
}

variable "nat_gateway_tags" {
description = "Additional tags for the NAT gateways"
type = map(string)
Expand Down

0 comments on commit 8f62e62

Please sign in to comment.