From d20a078134f5e36babfcae7b7241bd4126423199 Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Fri, 12 May 2023 16:20:38 +0200 Subject: [PATCH] Cloud NAT rules support --- modules/net-cloudnat/README.md | 68 +++++++++++++++---- modules/net-cloudnat/main.tf | 14 +++- modules/net-cloudnat/variables.tf | 39 +++++------ .../modules/net_cloudnat/examples/rules.yaml | 55 +++++++++++++++ 4 files changed, 143 insertions(+), 33 deletions(-) create mode 100644 tests/modules/net_cloudnat/examples/rules.yaml diff --git a/modules/net-cloudnat/README.md b/modules/net-cloudnat/README.md index f186b58b..637d5cb8 100644 --- a/modules/net-cloudnat/README.md +++ b/modules/net-cloudnat/README.md @@ -2,7 +2,7 @@ Simple Cloud NAT management, with optional router creation. -## Example +## Basic Example ```hcl module "nat" { @@ -14,25 +14,67 @@ module "nat" { } # tftest modules=1 resources=2 ``` + +# Reserved IPs and custom rules + +```hcl +module "addresses" { + source = "./fabric/modules/net-address" + project_id = "my-project" + external_addresses = { + a1 = "europe-west1" + a2 = "europe-west1" + a3 = "europe-west1" + } +} + +module "nat" { + source = "./fabric/modules/net-cloudnat" + project_id = "my-project" + region = "europe-west1" + name = "nat" + router_network = "my-vpc" + addresses = [ + module.addresses.external_addresses["a1"].self_link, + module.addresses.external_addresses["a3"].self_link + ] + + config_port_allocation = { + enable_endpoint_independent_mapping = false + } + + rules = [ + { + description = "rule1" + match = "destination.ip == '8.8.8.8'" + source_ips = [ + module.addresses.external_addresses["a2"].self_link + ] + } + ] +} +# tftest modules=2 resources=5 inventory=rules.yaml +``` ## Variables | name | description | type | required | default | |---|---|:---:|:---:|:---:| -| [config_port_allocation](variables.tf#L23) | Configuration for how to assign ports to virtual machines. min_ports_per_vm and max_ports_per_vm have no effect unless enable_dynamic_port_allocation is set to 'true'. | object({…} | ✓ | | -| [name](variables.tf#L73) | Name of the Cloud NAT resource. | string | ✓ | | -| [project_id](variables.tf#L78) | Project where resources will be created. | string | ✓ | | -| [region](variables.tf#L83) | Region where resources will be created. | string | ✓ | | +| [name](variables.tf#L63) | Name of the Cloud NAT resource. | string | ✓ | | +| [project_id](variables.tf#L68) | Project where resources will be created. | string | ✓ | | +| [region](variables.tf#L73) | Region where resources will be created. | string | ✓ | | | [addresses](variables.tf#L17) | Optional list of external address self links. | list(string) | | [] | -| [config_source_subnets](variables.tf#L45) | Subnetwork configuration (ALL_SUBNETWORKS_ALL_IP_RANGES, ALL_SUBNETWORKS_ALL_PRIMARY_IP_RANGES, LIST_OF_SUBNETWORKS). | string | | "ALL_SUBNETWORKS_ALL_IP_RANGES" | -| [config_timeouts](variables.tf#L51) | Timeout configurations. | object({…}) | | {…} | -| [logging_filter](variables.tf#L67) | Enables logging if not null, value is one of 'ERRORS_ONLY', 'TRANSLATIONS_ONLY', 'ALL'. | string | | null | -| [router_asn](variables.tf#L88) | Router ASN used for auto-created router. | number | | 64514 | -| [router_create](variables.tf#L94) | Create router. | bool | | true | -| [router_name](variables.tf#L100) | Router name, leave blank if router will be created to use auto generated name. | string | | null | -| [router_network](variables.tf#L106) | Name of the VPC used for auto-created router. | string | | null | -| [subnetworks](variables.tf#L112) | Subnetworks to NAT, only used when config_source_subnets equals LIST_OF_SUBNETWORKS. | list(object({…})) | | [] | +| [config_port_allocation](variables.tf#L23) | Configuration for how to assign ports to virtual machines. min_ports_per_vm and max_ports_per_vm have no effect unless enable_dynamic_port_allocation is set to 'true'. | object({…}) | | {} | +| [config_source_subnets](variables.tf#L39) | Subnetwork configuration (ALL_SUBNETWORKS_ALL_IP_RANGES, ALL_SUBNETWORKS_ALL_PRIMARY_IP_RANGES, LIST_OF_SUBNETWORKS). | string | | "ALL_SUBNETWORKS_ALL_IP_RANGES" | +| [config_timeouts](variables.tf#L45) | Timeout configurations. | object({…}) | | {} | +| [logging_filter](variables.tf#L57) | Enables logging if not null, value is one of 'ERRORS_ONLY', 'TRANSLATIONS_ONLY', 'ALL'. | string | | null | +| [router_asn](variables.tf#L78) | Router ASN used for auto-created router. | number | | 64514 | +| [router_create](variables.tf#L84) | Create router. | bool | | true | +| [router_name](variables.tf#L90) | Router name, leave blank if router will be created to use auto generated name. | string | | null | +| [router_network](variables.tf#L96) | Name of the VPC used for auto-created router. | string | | null | +| [rules](variables.tf#L102) | List of rules associated with this NAT. | list(object({…})) | | [] | +| [subnetworks](variables.tf#L113) | Subnetworks to NAT, only used when config_source_subnets equals LIST_OF_SUBNETWORKS. | list(object({…})) | | [] | ## Outputs diff --git a/modules/net-cloudnat/main.tf b/modules/net-cloudnat/main.tf index 63209d05..68613c4d 100644 --- a/modules/net-cloudnat/main.tf +++ b/modules/net-cloudnat/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2022 Google LLC + * Copyright 2023 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -63,5 +63,17 @@ resource "google_compute_router_nat" "nat" { secondary_ip_range_names = subnetwork.value.secondary_ranges } } + + dynamic "rules" { + for_each = { for i, r in var.rules : i => r } + content { + rule_number = rules.key + description = rules.value.description + match = rules.value.match + action { + source_nat_active_ips = rules.value.source_ips + } + } + } } diff --git a/modules/net-cloudnat/variables.tf b/modules/net-cloudnat/variables.tf index 97b03b27..abf06415 100644 --- a/modules/net-cloudnat/variables.tf +++ b/modules/net-cloudnat/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2022 Google LLC + * Copyright 2023 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,14 +28,8 @@ variable "config_port_allocation" { min_ports_per_vm = optional(number, 64) max_ports_per_vm = optional(number, 65536) }) - - default = { - enable_endpoint_independent_mapping = true - enable_dynamic_port_allocation = false - min_ports_per_vm = 64 - max_ports_per_vm = 65536 - } - + default = {} + nullable = false validation { condition = var.config_port_allocation.enable_dynamic_port_allocation ? var.config_port_allocation.enable_endpoint_independent_mapping == false : true error_message = "You must set enable_endpoint_independent_mapping to false to set enable_dynamic_port_allocation to true." @@ -51,17 +45,13 @@ variable "config_source_subnets" { variable "config_timeouts" { description = "Timeout configurations." type = object({ - icmp = number - tcp_established = number - tcp_transitory = number - udp = number + icmp = optional(number, 30) + tcp_established = optional(number, 1200) + tcp_transitory = optional(number, 30) + udp = optional(number, 30) }) - default = { - icmp = 30 - tcp_established = 1200 - tcp_transitory = 30 - udp = 30 - } + default = {} + nullable = false } variable "logging_filter" { @@ -109,6 +99,17 @@ variable "router_network" { default = null } +variable "rules" { + description = "List of rules associated with this NAT." + type = list(object({ + description = optional(string), + match = string + source_ips = list(string) + })) + default = [] + nullable = false +} + variable "subnetworks" { description = "Subnetworks to NAT, only used when config_source_subnets equals LIST_OF_SUBNETWORKS." type = list(object({ diff --git a/tests/modules/net_cloudnat/examples/rules.yaml b/tests/modules/net_cloudnat/examples/rules.yaml new file mode 100644 index 00000000..62efd1b3 --- /dev/null +++ b/tests/modules/net_cloudnat/examples/rules.yaml @@ -0,0 +1,55 @@ +# Copyright 2023 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +values: + module.addresses.google_compute_address.external["a1"]: + address_type: EXTERNAL + name: a1 + project: my-project + region: europe-west1 + module.addresses.google_compute_address.external["a2"]: + address_type: EXTERNAL + name: a2 + project: my-project + region: europe-west1 + module.addresses.google_compute_address.external["a3"]: + address_type: EXTERNAL + name: a3 + project: my-project + region: europe-west1 + module.nat.google_compute_router.router[0]: + name: nat-nat + network: my-vpc + project: my-project + region: europe-west1 + module.nat.google_compute_router_nat.nat: + enable_dynamic_port_allocation: false + enable_endpoint_independent_mapping: false + icmp_idle_timeout_sec: 30 + name: nat + nat_ip_allocate_option: MANUAL_ONLY + project: my-project + region: europe-west1 + router: nat-nat + rules: + - action: + - source_nat_drain_ips: [] + description: rule1 + match: destination.ip == '8.8.8.8' + rule_number: 0 + +counts: + google_compute_address: 3 + google_compute_router: 1 + google_compute_router_nat: 1