From ca3c86cb5c77d18fc1ff372596ca8f283cfa76fb Mon Sep 17 00:00:00 2001 From: Simone Ruffilli Date: Wed, 13 Dec 2023 16:19:40 +0100 Subject: [PATCH] Add support for policy based routes to net-vpc (#1926) * Add support for PBR to net-vpc --- modules/net-vpc/README.md | 56 ++++++++++++++++---- modules/net-vpc/routes.tf | 29 +++++++++++ modules/net-vpc/variables.tf | 48 +++++++++++++++++ tests/modules/net_vpc/examples/pbr.yaml | 68 +++++++++++++++++++++++++ 4 files changed, 191 insertions(+), 10 deletions(-) create mode 100644 tests/modules/net_vpc/examples/pbr.yaml diff --git a/modules/net-vpc/README.md b/modules/net-vpc/README.md index a2fdde4b9..d5b7b79a6 100644 --- a/modules/net-vpc/README.md +++ b/modules/net-vpc/README.md @@ -17,6 +17,7 @@ This module allows creation and management of VPC networks including subnetworks - [DNS Policies](#dns-policies) - [Subnet Factory](#subnet-factory) - [Custom Routes](#custom-routes) + - [Policy Based Routes](#policy-based-routes) - [Private Google Access routes](#private-google-access-routes) - [Allow Firewall Policy to be evaluated before Firewall Rules](#allow-firewall-policy-to-be-evaluated-before-firewall-rules) - [IPv6](#ipv6) @@ -455,6 +456,40 @@ module "vpc" { # tftest modules=5 resources=15 inventory=routes.yaml ``` +### Policy Based Routes + +Policy based routes can be configured through the `policy_based_routes` variable. + +```hcl +module "vpc" { + source = "./fabric/modules/net-vpc" + project_id = var.project_id + name = "my-vpc" + policy_based_routes = { + skip-pbr-for-nva = { + use_default_routing = true + priority = 100 + target = { + tags = ["nva"] + } + } + send-all-to-nva = { + next_hop_ilb_ip = "10.0.0.253" + priority = 101 + filter = { + src_range = "10.0.0.0/8" + dest_range = "0.0.0.0/0" + } + target = { + interconnect_attachment = "europe-west8" + } + } + } + create_googleapis_routes = null +} +# tftest modules=1 resources=3 inventory=pbr.yaml +``` + ### Private Google Access routes By default the VPC module creates IPv4 routes for the [Private Google Access ranges](https://cloud.google.com/vpc/docs/configure-private-google-access#config-routing). This behavior can be controlled through the `create_googleapis_routes` variable: @@ -541,7 +576,7 @@ module "vpc" { | name | description | type | required | default | |---|---|:---:|:---:|:---:| | [name](variables.tf#L95) | The name of the network being created. | string | ✓ | | -| [project_id](variables.tf#L111) | The ID of the project where this VPC will be created. | string | ✓ | | +| [project_id](variables.tf#L159) | The ID of the project where this VPC will be created. | string | ✓ | | | [auto_create_subnetworks](variables.tf#L17) | Set to true to create an auto mode subnet, defaults to custom mode. | bool | | false | | [create_googleapis_routes](variables.tf#L23) | Toggle creation of googleapis private/restricted routes. Disabled when vpc creation is turned off, or when set to null. | object({…}) | | {} | | [delete_default_routes_on_create](variables.tf#L34) | Set to true to delete the default routes at creation time. | bool | | false | @@ -552,15 +587,16 @@ module "vpc" { | [ipv6_config](variables.tf#L79) | Optional IPv6 configuration for this network. | object({…}) | | {} | | [mtu](variables.tf#L89) | Maximum Transmission Unit in bytes. The minimum value for this field is 1460 (the default) and the maximum value is 1500 bytes. | number | | null | | [peering_config](variables.tf#L100) | VPC peering configuration. | object({…}) | | null | -| [psa_config](variables.tf#L116) | The Private Service Access configuration for Service Networking. | object({…}) | | null | -| [routes](variables.tf#L127) | Network routes, keyed by name. | map(object({…})) | | {} | -| [routing_mode](variables.tf#L148) | The network routing mode (default 'GLOBAL'). | string | | "GLOBAL" | -| [shared_vpc_host](variables.tf#L158) | Enable shared VPC for this project. | bool | | false | -| [shared_vpc_service_projects](variables.tf#L164) | Shared VPC service projects to register with this host. | list(string) | | [] | -| [subnets](variables.tf#L170) | Subnet configuration. | list(object({…})) | | [] | -| [subnets_proxy_only](variables.tf#L217) | List of proxy-only subnets for Regional HTTPS or Internal HTTPS load balancers. Note: Only one proxy-only subnet for each VPC network in each region can be active. | list(object({…})) | | [] | -| [subnets_psc](variables.tf#L251) | List of subnets for Private Service Connect service producers. | list(object({…})) | | [] | -| [vpc_create](variables.tf#L283) | Create VPC. When set to false, uses a data source to reference existing VPC. | bool | | true | +| [policy_based_routes](variables.tf#L111) | Policy based routes, keyed by name. | map(object({…})) | | {} | +| [psa_config](variables.tf#L164) | The Private Service Access configuration for Service Networking. | object({…}) | | null | +| [routes](variables.tf#L175) | Network routes, keyed by name. | map(object({…})) | | {} | +| [routing_mode](variables.tf#L196) | The network routing mode (default 'GLOBAL'). | string | | "GLOBAL" | +| [shared_vpc_host](variables.tf#L206) | Enable shared VPC for this project. | bool | | false | +| [shared_vpc_service_projects](variables.tf#L212) | Shared VPC service projects to register with this host. | list(string) | | [] | +| [subnets](variables.tf#L218) | Subnet configuration. | list(object({…})) | | [] | +| [subnets_proxy_only](variables.tf#L265) | List of proxy-only subnets for Regional HTTPS or Internal HTTPS load balancers. Note: Only one proxy-only subnet for each VPC network in each region can be active. | list(object({…})) | | [] | +| [subnets_psc](variables.tf#L299) | List of subnets for Private Service Connect service producers. | list(object({…})) | | [] | +| [vpc_create](variables.tf#L331) | Create VPC. When set to false, uses a data source to reference existing VPC. | bool | | true | ## Outputs diff --git a/modules/net-vpc/routes.tf b/modules/net-vpc/routes.tf index 065ea5fda..be16bb936 100644 --- a/modules/net-vpc/routes.tf +++ b/modules/net-vpc/routes.tf @@ -108,3 +108,32 @@ resource "google_compute_route" "vpn_tunnel" { tags = each.value.tags next_hop_vpn_tunnel = each.value.next_hop } + +resource "google_network_connectivity_policy_based_route" "default" { + for_each = var.policy_based_routes + project = var.project_id + network = local.network.id + name = "${var.name}-${each.key}" + description = each.value.description + priority = each.value.priority + next_hop_other_routes = each.value.use_default_routing ? "DEFAULT_ROUTING" : null + next_hop_ilb_ip = each.value.use_default_routing ? null : each.value.next_hop_ilb_ip + filter { + protocol_version = "IPV4" + ip_protocol = each.value.filter.ip_protocol + dest_range = each.value.filter.dest_range + src_range = each.value.filter.src_range + } + dynamic "virtual_machine" { + for_each = each.value.target.tags != null ? [""] : [] + content { + tags = each.value.target.tags + } + } + dynamic "interconnect_attachment" { + for_each = each.value.target.interconnect_attachment != null ? [""] : [] + content { + region = each.value.target.interconnect_attachment + } + } +} diff --git a/modules/net-vpc/variables.tf b/modules/net-vpc/variables.tf index f463470d1..06b95209c 100644 --- a/modules/net-vpc/variables.tf +++ b/modules/net-vpc/variables.tf @@ -108,6 +108,54 @@ variable "peering_config" { default = null } +variable "policy_based_routes" { + description = "Policy based routes, keyed by name." + type = map(object({ + description = optional(string, "Terraform-managed.") + labels = optional(map(string)) + priority = optional(number) + next_hop_ilb_ip = optional(string) + use_default_routing = optional(bool, false) + filter = optional(object({ + ip_protocol = optional(string) + dest_range = optional(string) + src_range = optional(string) + }), {}) + target = optional(object({ + interconnect_attachment = optional(string) + tags = optional(list(string)) + }), {}) + })) + default = {} + nullable = false + validation { + condition = alltrue([ + for r in var.policy_based_routes : + contains(["TCP", "UDP", "ALL", null], r.filter.ip_protocol) + if r.filter.ip_protocol != null + ]) + error_message = "Unsupported protocol for route." + } + validation { + condition = alltrue([ + for r in var.policy_based_routes : + ( + (r.use_default_routing == true ? 1 : 0) + + + (r.next_hop_ilb_ip != null ? 1 : 0) + ) == 1 + ]) + error_message = "Either set `use_default_routing = true` or specify an internal passthrough LB IP." + } + validation { + condition = alltrue([ + for r in var.policy_based_routes : + r.target.tags == null || r.target.interconnect_attachment == null + ]) + error_message = "Either use virtual machine tags or a vlan attachment region as a target." + } +} + variable "project_id" { description = "The ID of the project where this VPC will be created." type = string diff --git a/tests/modules/net_vpc/examples/pbr.yaml b/tests/modules/net_vpc/examples/pbr.yaml new file mode 100644 index 000000000..4255e4e66 --- /dev/null +++ b/tests/modules/net_vpc/examples/pbr.yaml @@ -0,0 +1,68 @@ +# 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.vpc.google_compute_network.network[0]: + auto_create_subnetworks: false + delete_default_routes_on_create: false + description: Terraform-managed. + enable_ula_internal_ipv6: null + name: my-vpc + network_firewall_policy_enforcement_order: AFTER_CLASSIC_FIREWALL + project: project-id + routing_mode: GLOBAL + timeouts: null + ? module.vpc.google_network_connectivity_policy_based_route.default["send-all-to-nva"] + : description: Terraform-managed. + filter: + - dest_range: 0.0.0.0/0 + ip_protocol: ALL + protocol_version: IPV4 + src_range: 10.0.0.0/8 + interconnect_attachment: + - region: europe-west8 + labels: null + name: my-vpc-send-all-to-nva + next_hop_ilb_ip: 10.0.0.253 + next_hop_other_routes: null + priority: 101 + project: project-id + timeouts: null + virtual_machine: [] + ? module.vpc.google_network_connectivity_policy_based_route.default["skip-pbr-for-nva"] + : description: Terraform-managed. + filter: + - dest_range: 0.0.0.0/0 + ip_protocol: ALL + protocol_version: IPV4 + src_range: 0.0.0.0/0 + interconnect_attachment: [] + labels: null + name: my-vpc-skip-pbr-for-nva + next_hop_ilb_ip: null + next_hop_other_routes: DEFAULT_ROUTING + priority: 100 + project: project-id + timeouts: null + virtual_machine: + - tags: + - nva + +counts: + google_compute_network: 1 + google_network_connectivity_policy_based_route: 2 + modules: 1 + resources: 3 + +outputs: {}