From 94a1d71dfebc464dfe2fdec4701d0dacbc5cb379 Mon Sep 17 00:00:00 2001 From: Ra'Jiska Date: Sat, 18 Nov 2023 21:25:33 +0800 Subject: [PATCH] IPv6 + NAT64 Support --- ec2.tf | 5 +++++ examples/full/main.tf | 18 ++++++++++++++++-- examples/full/network.tf | 40 +++++++++++++++++++++++++++++++++++++++- main.tf | 28 +++++++++++++++++++--------- templates/user_data.sh | 5 ++++- variables.tf | 22 ++++++++++++++++++++++ 6 files changed, 105 insertions(+), 13 deletions(-) diff --git a/ec2.tf b/ec2.tf index 23aa6e5..46de553 100644 --- a/ec2.tf +++ b/ec2.tf @@ -56,6 +56,7 @@ resource "aws_launch_template" "main" { subnet_id = var.subnet_id associate_public_ip_address = true security_groups = [aws_security_group.main.id] + ipv6_address_count = var.use_nat64 ? 1 : null } dynamic "instance_market_options" { @@ -83,6 +84,10 @@ resource "aws_launch_template" "main" { TERRAFORM_EIP_ID = length(var.eip_allocation_ids) != 0 ? var.eip_allocation_ids[0] : "" TERRAFORM_CWAGENT_ENABLED = var.use_cloudwatch_agent ? "true" : "" TERRAFORM_CWAGENT_CFG_PARAM_NAME = local.cwagent_param_name != null ? local.cwagent_param_name : "" + TERRAFORM_NAT64_ENABLED = var.use_nat64 ? "true" : "" + TERRAFORM_NAT64_IPV4_ADDR = var.use_nat64 ? var.nat64_configuration.tayga_ipv4_addr : "" + TERRAFORM_NAT64_IPV6_ADDR = var.use_nat64 ? var.nat64_configuration.tayga_ipv6_addr : "" + TERRAFORM_NAT64_DYNAMIC_POOL = var.use_nat64 ? var.nat64_configuration.tayga_dynamic_pool : "" })) tags = var.tags diff --git a/examples/full/main.tf b/examples/full/main.tf index 98be0b4..1be8db7 100644 --- a/examples/full/main.tf +++ b/examples/full/main.tf @@ -1,6 +1,7 @@ locals { - name = "fck-nat-example" - vpc_cidr = "10.255.255.0/24" + name = "fck-nat-example" + vpc_cidr = "10.255.255.0/24" + ipv6_support = true } data "aws_region" "current" {} @@ -14,4 +15,17 @@ module "fck-nat" { update_route_table = true route_table_id = aws_route_table.private.id ha_mode = false + use_nat64 = local.ipv6_support +} + +resource "aws_route" "ipv6_route" { + count = local.ipv6_support ? 1 : 0 + + # Is there only because for some reason I made the parameter `route_table_id` take only one ID instead of an array of + # IDs as multiple subnets might depend on a single NAT instance. This is not fixed yet as it would break + # compatibility. Will be fixed in v2. + + route_table_id = aws_route_table.private_ipv6[0].id + destination_ipv6_cidr_block = "64:ff9b::/96" + network_interface_id = module.fck-nat.eni_id } \ No newline at end of file diff --git a/examples/full/network.tf b/examples/full/network.tf index e5c52b3..5a65b26 100644 --- a/examples/full/network.tf +++ b/examples/full/network.tf @@ -1,5 +1,6 @@ resource "aws_vpc" "main" { - cidr_block = local.vpc_cidr + cidr_block = local.vpc_cidr + assign_generated_ipv6_cidr_block = local.ipv6_support tags = { Name = local.name @@ -12,6 +13,7 @@ resource "aws_subnet" "public" { vpc_id = aws_vpc.main.id cidr_block = cidrsubnet(local.vpc_cidr, 4, 0) availability_zone = "${data.aws_region.current.name}a" + ipv6_cidr_block = local.ipv6_support ? cidrsubnet(aws_vpc.main.ipv6_cidr_block, 8, 0) : null tags = { Name = "${local.name}-public" @@ -52,6 +54,7 @@ resource "aws_subnet" "private" { vpc_id = aws_vpc.main.id cidr_block = cidrsubnet(local.vpc_cidr, 4, 1) availability_zone = "${data.aws_region.current.name}a" + ipv6_cidr_block = local.ipv6_support ? cidrsubnet(aws_vpc.main.ipv6_cidr_block, 8, 1) : null tags = { Name = "${local.name}-private" @@ -70,3 +73,38 @@ resource "aws_route_table_association" "private" { subnet_id = aws_subnet.private.id route_table_id = aws_route_table.private.id } + +### Private IPv6 + +resource "aws_subnet" "private_ipv6" { + count = local.ipv6_support ? 1 : 0 + + vpc_id = aws_vpc.main.id + ipv6_cidr_block = cidrsubnet(aws_vpc.main.ipv6_cidr_block, 8, 2) + availability_zone = "${data.aws_region.current.name}a" + enable_dns64 = true + ipv6_native = true + assign_ipv6_address_on_creation = true + enable_resource_name_dns_aaaa_record_on_launch = true + + tags = { + Name = "${local.name}-private-ipv6" + } +} + +resource "aws_route_table" "private_ipv6" { + count = local.ipv6_support ? 1 : 0 + + vpc_id = aws_vpc.main.id + + tags = { + Name = "${local.name}-private-ipv6" + } +} + +resource "aws_route_table_association" "private_ipv6" { + count = local.ipv6_support ? 1 : 0 + + subnet_id = aws_subnet.private_ipv6[0].id + route_table_id = aws_route_table.private_ipv6[0].id +} \ No newline at end of file diff --git a/main.tf b/main.tf index e90fa30..6d4ad0a 100644 --- a/main.tf +++ b/main.tf @@ -18,11 +18,12 @@ resource "aws_security_group" "main" { vpc_id = data.aws_vpc.main.id ingress { - description = "Unrestricted ingress from within VPC" - from_port = 0 - to_port = 0 - protocol = "-1" - cidr_blocks = ["${data.aws_vpc.main.cidr_block}"] + description = "Unrestricted ingress from within VPC" + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["${data.aws_vpc.main.cidr_block}"] + ipv6_cidr_blocks = var.use_nat64 ? ["${data.aws_vpc.main.ipv6_cidr_block}"] : null } egress { @@ -40,10 +41,11 @@ resource "aws_security_group" "main" { } resource "aws_network_interface" "main" { - description = "${var.name} static private ENI" - subnet_id = var.subnet_id - security_groups = [aws_security_group.main.id] - source_dest_check = false + description = "${var.name} static private ENI" + subnet_id = var.subnet_id + security_groups = [aws_security_group.main.id] + source_dest_check = false + ipv6_address_count = var.use_nat64 ? 1 : null tags = merge(var.tags, { Name = var.name @@ -58,6 +60,14 @@ resource "aws_route" "main" { network_interface_id = aws_network_interface.main.id } +resource "aws_route" "main_ipv6" { + count = var.update_route_table && var.use_nat64 ? 1 : 0 + + route_table_id = var.route_table_id + destination_ipv6_cidr_block = "64:ff9b::/96" + network_interface_id = aws_network_interface.main.id +} + resource "aws_ssm_parameter" "cloudwatch_agent_config" { count = var.use_cloudwatch_agent && var.cloudwatch_agent_configuration_param_arn == null ? 1 : 0 diff --git a/templates/user_data.sh b/templates/user_data.sh index 90c77e2..e10a56a 100644 --- a/templates/user_data.sh +++ b/templates/user_data.sh @@ -4,6 +4,9 @@ echo "eni_id=${TERRAFORM_ENI_ID}" >> /etc/fck-nat.conf echo "eip_id=${TERRAFORM_EIP_ID}" >> /etc/fck-nat.conf echo "cwagent_enabled=${TERRAFORM_CWAGENT_ENABLED}" >> /etc/fck-nat.conf -echo "cwagent_cfg_param_name=${TERRAFORM_CWAGENT_CFG_PARAM_NAME}" >> /etc/fck-nat.conf +echo "nat64_enabled=${TERRAFORM_NAT64_ENABLED}" >> /etc/fck-nat.conf +echo "nat64_ipv4_addr=${TERRAFORM_NAT64_IPV4_ADDR}" >> /etc/fck-nat.conf +echo "nat64_ipv6_addr=${TERRAFORM_NAT64_IPV6_ADDR}" >> /etc/fck-nat.conf +echo "nat64_ipv4_dynamic_pool=${TERRAFORM_NAT64_DYNAMIC_POOL}" >> /etc/fck-nat.conf service fck-nat restart diff --git a/variables.tf b/variables.tf index 381194e..155829c 100644 --- a/variables.tf +++ b/variables.tf @@ -99,6 +99,28 @@ variable "cloudwatch_agent_configuration_param_arn" { default = null } +variable "use_nat64" { + description = "Whether or not to enable NAT64 on the NAT instance. Your VPC and at least the public subnet this NAT instance is deployed into must support IPv6" + type = bool + default = false +} + +variable "nat64_configuration" { + description = "NAT64 configuration for the NAT instance through TAYGA" + type = object({ + tayga_ipv4_addr = optional(string, "192.168.255.1"), + tayga_ipv6_addr = optional(string, "2001:db8:1::2"), + tayga_dynamic_pool = optional(string, "192.168.0.0/16"), + }) + default = { + default = { + tayga_ipv4_addr = "192.168.255.1", + tayga_ipv6_addr = "2001:db8:1::2", + tayga_dynamic_pool = "192.168.0.0/16" + } + } +} + variable "tags" { description = "Tags to apply to resources created within the module" type = map(string)