From f2469c1a966220710968bcc1d2be51eb82b33abb Mon Sep 17 00:00:00 2001 From: Samir Ribeiro <42391123+Samir-Cit@users.noreply.github.com> Date: Wed, 27 Dec 2023 04:24:08 -0300 Subject: [PATCH] feat: Change old firewall to new network-firewall (#1041) Co-authored-by: Daniel Andrade --- 0-bootstrap/modules/jenkins-agent/main.tf | 67 ++-- 3-networks-dual-svpc/modules/base_env/main.tf | 4 - .../modules/base_shared_vpc/README.md | 3 +- .../modules/base_shared_vpc/firewall.tf | 185 +++++------ .../modules/base_shared_vpc/variables.tf | 12 +- .../modules/restricted_shared_vpc/README.md | 3 +- .../modules/restricted_shared_vpc/firewall.tf | 184 +++++------ .../restricted_shared_vpc/service_control.tf | 5 +- .../restricted_shared_vpc/variables.tf | 12 +- .../envs/shared/net-hubs-transitivity.tf | 2 + .../modules/base_env/main.tf | 4 - .../modules/base_shared_vpc/README.md | 5 +- .../modules/base_shared_vpc/firewall.tf | 185 +++++------ .../modules/base_shared_vpc/outputs.tf | 5 + .../modules/base_shared_vpc/variables.tf | 14 +- .../modules/restricted_shared_vpc/README.md | 5 +- .../modules/restricted_shared_vpc/firewall.tf | 184 +++++------ .../modules/restricted_shared_vpc/outputs.tf | 5 + .../restricted_shared_vpc/service_control.tf | 5 +- .../restricted_shared_vpc/variables.tf | 14 +- .../modules/transitivity/README.md | 1 + .../modules/transitivity/main.tf | 71 ++--- .../modules/transitivity/variables.tf | 5 + .../base_env/example_peering_project.tf | 291 ++++++++---------- test/integration/networks/networks_test.go | 45 +-- 25 files changed, 604 insertions(+), 712 deletions(-) diff --git a/0-bootstrap/modules/jenkins-agent/main.tf b/0-bootstrap/modules/jenkins-agent/main.tf index f8afe9375..e9635b54d 100644 --- a/0-bootstrap/modules/jenkins-agent/main.tf +++ b/0-bootstrap/modules/jenkins-agent/main.tf @@ -18,7 +18,6 @@ locals { cicd_project_name = format("%s-%s", var.project_prefix, "b-cicd") impersonation_enabled_count = var.sa_enable_impersonation ? 1 : 0 activate_apis = distinct(concat(var.activate_apis, ["billingbudgets.googleapis.com"])) - jenkins_gce_fw_tags = ["ssh-jenkins-agent"] } resource "random_id" "suffix" { @@ -70,7 +69,11 @@ resource "google_compute_instance" "jenkins_agent_gce_instance" { machine_type = var.jenkins_agent_gce_machine_type zone = "${var.default_region}-a" - tags = local.jenkins_gce_fw_tags + params { + resource_manager_tags = { + "tagKeys/${google_tags_tag_key.jenkins_agents.name}" = "tagValues/${google_tags_tag_value.jenkins_agents.name}" + } + } boot_disk { initialize_params { @@ -105,26 +108,52 @@ resource "google_compute_instance" "jenkins_agent_gce_instance" { } /****************************************** - Jenkins Agent GCE Network and Firewall rules + Jenkins Agent GCE Network, Resource Manager Tags and Firewall rules *******************************************/ - -resource "google_compute_firewall" "fw_allow_ssh_into_jenkins_agent" { - project = module.cicd_project.project_id - name = "fw-${google_compute_network.jenkins_agents.name}-1000-i-a-all-all-tcp-22" - description = "Allow the Jenkins Controller (Client) to connect to the Jenkins Agents (Servers) using SSH." - network = google_compute_network.jenkins_agents.name - source_ranges = var.jenkins_controller_subnetwork_cidr_range - target_tags = local.jenkins_gce_fw_tags - priority = 1000 - - log_config { - metadata = "INCLUDE_ALL_METADATA" +resource "google_tags_tag_key" "jenkins_agents" { + description = "Tag Key to control the connection between Jenkins Controller (Client) and the Jenkins Agents (Servers) using SSH." + parent = "organizations/${var.org_id}" + purpose = "GCE_FIREWALL" + short_name = "ssh-jenkins-agent" + purpose_data = { + network = "${module.cicd_project.project_id}/${google_compute_network.jenkins_agents.name}" } +} - allow { - protocol = "tcp" - ports = ["22"] - } +resource "google_tags_tag_value" "jenkins_agents" { + description = "Allow the connection." + parent = "tagKeys/${google_tags_tag_key.jenkins_agents.name}" + short_name = "allow" +} + +module "jenkins_firewall_rules" { + source = "terraform-google-modules/network/google//modules/network-firewall-policy" + version = "~> 8.0" + project_id = module.cicd_project.project_id + policy_name = "fp-${google_compute_network.jenkins_agents.name}-jenkins-firewall" + description = "Jenkins Agent GCE network firewall rules." + target_vpcs = [google_compute_network.jenkins_agents.name] + + rules = [ + { + priority = "1000" + direction = "INGRESS" + action = "allow" + rule_name = "fw-${google_compute_network.jenkins_agents.name}-1000-i-a-all-all-tcp-22" + description = "Allow the Jenkins Controller (Client) to connect to the Jenkins Agents (Servers) using SSH." + enable_logging = true + target_secure_tags = ["tagValues/${google_tags_tag_value.jenkins_agents.name}"] + match = { + dest_ip_ranges = var.jenkins_controller_subnetwork_cidr_range + layer4_configs = [ + { + ip_protocol = "tcp" + ports = ["22"] + }, + ] + } + } + ] } resource "google_compute_network" "jenkins_agents" { diff --git a/3-networks-dual-svpc/modules/base_env/main.tf b/3-networks-dual-svpc/modules/base_env/main.tf index 98621cea1..a1b8e2612 100644 --- a/3-networks-dual-svpc/modules/base_env/main.tf +++ b/3-networks-dual-svpc/modules/base_env/main.tf @@ -241,8 +241,6 @@ module "restricted_shared_vpc" { secondary_ranges = { "sb-${var.environment_code}-shared-restricted-${var.default_region1}" = var.restricted_subnet_secondary_ranges[var.default_region1] } - allow_all_ingress_ranges = null - allow_all_egress_ranges = null } /****************************************** @@ -312,6 +310,4 @@ module "base_shared_vpc" { secondary_ranges = { "sb-${var.environment_code}-shared-base-${var.default_region1}" = var.base_subnet_secondary_ranges[var.default_region1] } - allow_all_ingress_ranges = null - allow_all_egress_ranges = null } diff --git a/3-networks-dual-svpc/modules/base_shared_vpc/README.md b/3-networks-dual-svpc/modules/base_shared_vpc/README.md index 4a8099725..10b8c0e1c 100644 --- a/3-networks-dual-svpc/modules/base_shared_vpc/README.md +++ b/3-networks-dual-svpc/modules/base_shared_vpc/README.md @@ -3,8 +3,6 @@ | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| allow\_all\_egress\_ranges | List of network ranges to which all egress traffic will be allowed | `any` | `null` | no | -| allow\_all\_ingress\_ranges | List of network ranges from which all ingress traffic will be allowed | `any` | `null` | no | | bgp\_asn\_subnet | BGP ASN for Subnets cloud routers. | `number` | n/a | yes | | default\_region1 | Default region 1 for subnets and Cloud Routers | `string` | n/a | yes | | default\_region2 | Default region 2 for subnets and Cloud Routers | `string` | n/a | yes | @@ -12,6 +10,7 @@ | dns\_enable\_logging | Toggle DNS logging for VPC DNS. | `bool` | `true` | no | | dns\_hub\_project\_id | The DNS hub project ID | `string` | n/a | yes | | domain | The DNS name of peering managed zone, for instance 'example.com.' | `string` | n/a | yes | +| enable\_all\_vpc\_internal\_traffic | Enable firewall policy rule to allow internal traffic (ingress and egress). | `bool` | `false` | no | | environment\_code | A short form of the folder level resources (environment) within the Google Cloud organization. | `string` | n/a | yes | | firewall\_enable\_logging | Toggle firewall logging for VPC Firewalls. | `bool` | `true` | no | | nat\_bgp\_asn | BGP ASN for first NAT cloud routes. | `number` | `64514` | no | diff --git a/3-networks-dual-svpc/modules/base_shared_vpc/firewall.tf b/3-networks-dual-svpc/modules/base_shared_vpc/firewall.tf index 8163ca047..671751162 100644 --- a/3-networks-dual-svpc/modules/base_shared_vpc/firewall.tf +++ b/3-networks-dual-svpc/modules/base_shared_vpc/firewall.tf @@ -15,108 +15,87 @@ */ /****************************************** - Mandatory firewall rules + Mandatory and optional firewall rules *****************************************/ - -resource "google_compute_firewall" "deny_all_egress" { - name = "fw-${var.environment_code}-shared-base-65530-e-d-all-all-all" - network = module.main.network_name - project = var.project_id - direction = "EGRESS" - priority = 65530 - - dynamic "log_config" { - for_each = var.firewall_enable_logging == true ? [{ - metadata = "INCLUDE_ALL_METADATA" - }] : [] - - content { - metadata = log_config.value.metadata - } - } - - deny { - protocol = "all" - } - - destination_ranges = ["0.0.0.0/0"] -} - - -resource "google_compute_firewall" "allow_private_api_egress" { - name = "fw-${var.environment_code}-shared-base-65430-e-a-allow-google-apis-all-tcp-443" - network = module.main.network_name - project = var.project_id - direction = "EGRESS" - priority = 65430 - - dynamic "log_config" { - for_each = var.firewall_enable_logging == true ? [{ - metadata = "INCLUDE_ALL_METADATA" - }] : [] - - content { - metadata = log_config.value.metadata - } - } - - allow { - protocol = "tcp" - ports = ["443"] - } - - destination_ranges = [local.private_googleapis_cidr] - - target_tags = ["allow-google-apis"] -} - - -resource "google_compute_firewall" "allow_all_egress" { - count = var.allow_all_egress_ranges != null ? 1 : 0 - name = "fw-${var.environment_code}-shared-base-1000-e-a-all-all-all" - network = module.main.network_name - project = var.project_id - direction = "EGRESS" - priority = 1000 - - dynamic "log_config" { - for_each = var.firewall_enable_logging == true ? [{ - metadata = "INCLUDE_ALL_METADATA" - }] : [] - - content { - metadata = log_config.value.metadata - } - } - - allow { - protocol = "all" - } - - destination_ranges = var.allow_all_egress_ranges -} - -resource "google_compute_firewall" "allow_all_ingress" { - count = var.allow_all_ingress_ranges != null ? 1 : 0 - name = "fw-${var.environment_code}-shared-base-1000-i-a-all" - network = module.main.network_name - project = var.project_id - direction = "INGRESS" - priority = 1000 - - dynamic "log_config" { - for_each = var.firewall_enable_logging == true ? [{ - metadata = "INCLUDE_ALL_METADATA" - }] : [] - - content { - metadata = log_config.value.metadata - } - } - - allow { - protocol = "all" - } - - source_ranges = var.allow_all_ingress_ranges +module "firewall_rules" { + source = "terraform-google-modules/network/google//modules/network-firewall-policy" + version = "~> 8.0" + project_id = var.project_id + policy_name = "fp-${var.environment_code}-dual-svpc-base-firewalls" + description = "Firewall rules for base dual shared vpc: ${module.main.network_name}." + target_vpcs = ["projects/${var.project_id}/global/networks/${module.main.network_name}"] + + rules = concat( + [ + { + priority = "65530" + direction = "EGRESS" + action = "deny" + rule_name = "fw-${var.environment_code}-shared-base-65530-e-d-all-all-all" + description = "Lower priority rule to deny all egress traffic." + enable_logging = var.firewall_enable_logging + match = { + dest_ip_ranges = ["0.0.0.0/0"] + layer4_configs = [ + { + ip_protocol = "all" + }, + ] + } + }, + { + priority = "1000" + direction = "EGRESS" + action = "allow" + rule_name = "fw-${var.environment_code}-shared-base-1000-e-a-allow-google-apis-all-tcp-443" + description = "Lower priority rule to allow private google apis on TCP port 443." + enable_logging = var.firewall_enable_logging + match = { + dest_ip_ranges = [local.private_googleapis_cidr] + layer4_configs = [ + { + ip_protocol = "tcp" + ports = ["443"] + }, + ] + } + } + ], + !var.enable_all_vpc_internal_traffic ? [] : [ + { + priority = "10000" + direction = "EGRESS" + action = "allow" + rule_name = "fw-${var.environment_code}-shared-base-10000-e-a-all-all-all" + description = "Allow all egress to the provided IP range." + enable_logging = var.firewall_enable_logging + match = { + dest_ip_ranges = module.main.subnets_ips + layer4_configs = [ + { + ip_protocol = "all" + }, + ] + } + } + ], + !var.enable_all_vpc_internal_traffic ? [] : [ + { + priority = "10001" + direction = "INGRESS" + action = "allow" + rule_name = "fw-${var.environment_code}-shared-base-10001-i-a-all" + description = "Allow all ingress to the provided IP range." + enable_logging = var.firewall_enable_logging + match = { + src_ip_ranges = module.main.subnets_ips + layer4_configs = [ + { + ip_protocol = "all" + }, + ] + } + } + ] + ) } diff --git a/3-networks-dual-svpc/modules/base_shared_vpc/variables.tf b/3-networks-dual-svpc/modules/base_shared_vpc/variables.tf index 5c4b1346d..4b2fca26b 100644 --- a/3-networks-dual-svpc/modules/base_shared_vpc/variables.tf +++ b/3-networks-dual-svpc/modules/base_shared_vpc/variables.tf @@ -137,12 +137,8 @@ variable "windows_activation_enabled" { default = false } -variable "allow_all_egress_ranges" { - description = "List of network ranges to which all egress traffic will be allowed" - default = null -} - -variable "allow_all_ingress_ranges" { - description = "List of network ranges from which all ingress traffic will be allowed" - default = null +variable "enable_all_vpc_internal_traffic" { + type = bool + description = "Enable firewall policy rule to allow internal traffic (ingress and egress)." + default = false } diff --git a/3-networks-dual-svpc/modules/restricted_shared_vpc/README.md b/3-networks-dual-svpc/modules/restricted_shared_vpc/README.md index 35d56c4ed..a0c577a24 100644 --- a/3-networks-dual-svpc/modules/restricted_shared_vpc/README.md +++ b/3-networks-dual-svpc/modules/restricted_shared_vpc/README.md @@ -4,8 +4,6 @@ | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| | access\_context\_manager\_policy\_id | The id of the default Access Context Manager policy. Can be obtained by running `gcloud access-context-manager policies list --organization YOUR_ORGANIZATION_ID --format="value(name)"`. | `number` | n/a | yes | -| allow\_all\_egress\_ranges | List of network ranges to which all egress traffic will be allowed | `any` | `null` | no | -| allow\_all\_ingress\_ranges | List of network ranges from which all ingress traffic will be allowed | `any` | `null` | no | | bgp\_asn\_subnet | BGP ASN for Subnets cloud routers. | `number` | n/a | yes | | default\_region1 | First subnet region. The shared vpc modules only configures two regions. | `string` | n/a | yes | | default\_region2 | Second subnet region. The shared vpc modules only configures two regions. | `string` | n/a | yes | @@ -14,6 +12,7 @@ | dns\_hub\_project\_id | The DNS hub project ID | `string` | n/a | yes | | domain | The DNS name of peering managed zone, for instance 'example.com.' | `string` | n/a | yes | | egress\_policies | A list of all [egress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#egress-rules-reference), each list object has a `from` and `to` value that describes egress\_from and egress\_to.

Example: `[{ from={ identities=[], identity_type="ID_TYPE" }, to={ resources=[], operations={ "SRV_NAME"={ OP_TYPE=[] }}}}]`

Valid Values:
`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`
`SRV_NAME` = "`*`" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)
`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) |
list(object({
from = any
to = any
}))
| `[]` | no | +| enable\_all\_vpc\_internal\_traffic | Enable firewall policy rule to allow internal traffic (ingress and egress). | `bool` | `false` | no | | environment\_code | A short form of the folder level resources (environment) within the Google Cloud organization. | `string` | n/a | yes | | firewall\_enable\_logging | Toggle firewall logging for VPC Firewalls. | `bool` | `true` | no | | ingress\_policies | A list of all [ingress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#ingress-rules-reference), each list object has a `from` and `to` value that describes ingress\_from and ingress\_to.

Example: `[{ from={ sources={ resources=[], access_levels=[] }, identities=[], identity_type="ID_TYPE" }, to={ resources=[], operations={ "SRV_NAME"={ OP_TYPE=[] }}}}]`

Valid Values:
`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`
`SRV_NAME` = "`*`" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)
`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) |
list(object({
from = any
to = any
}))
| `[]` | no | diff --git a/3-networks-dual-svpc/modules/restricted_shared_vpc/firewall.tf b/3-networks-dual-svpc/modules/restricted_shared_vpc/firewall.tf index 27db155d6..a74132d0e 100644 --- a/3-networks-dual-svpc/modules/restricted_shared_vpc/firewall.tf +++ b/3-networks-dual-svpc/modules/restricted_shared_vpc/firewall.tf @@ -16,107 +16,87 @@ /****************************************** - Mandatory firewall rules + Mandatory and optional firewall rules *****************************************/ -resource "google_compute_firewall" "deny_all_egress" { - name = "fw-${var.environment_code}-shared-restricted-65530-e-d-all-all-all" - network = module.main.network_name - project = var.project_id - direction = "EGRESS" - priority = 65530 - - dynamic "log_config" { - for_each = var.firewall_enable_logging == true ? [{ - metadata = "INCLUDE_ALL_METADATA" - }] : [] - - content { - metadata = log_config.value.metadata - } - } - - deny { - protocol = "all" - } - - destination_ranges = ["0.0.0.0/0"] -} - -resource "google_compute_firewall" "allow_restricted_api_egress" { - name = "fw-${var.environment_code}-shared-restricted-65430-e-a-allow-google-apis-all-tcp-443" - network = module.main.network_name - project = var.project_id - direction = "EGRESS" - priority = 65430 - - dynamic "log_config" { - for_each = var.firewall_enable_logging == true ? [{ - metadata = "INCLUDE_ALL_METADATA" - }] : [] - - content { - metadata = log_config.value.metadata - } - } - - allow { - protocol = "tcp" - ports = ["443"] - } - - destination_ranges = [local.restricted_googleapis_cidr] - - target_tags = ["allow-google-apis"] -} - -resource "google_compute_firewall" "allow_all_egress" { - count = var.allow_all_egress_ranges != null ? 1 : 0 - - name = "fw-${var.environment_code}-shared-base-1000-e-a-all-all-all" - network = module.main.network_name - project = var.project_id - direction = "EGRESS" - priority = 1000 - - dynamic "log_config" { - for_each = var.firewall_enable_logging == true ? [{ - metadata = "INCLUDE_ALL_METADATA" - }] : [] - - content { - metadata = log_config.value.metadata - } - } - - allow { - protocol = "all" - } - - destination_ranges = var.allow_all_egress_ranges -} - -resource "google_compute_firewall" "allow_all_ingress" { - count = var.allow_all_ingress_ranges != null ? 1 : 0 - - name = "fw-${var.environment_code}-shared-base-1000-i-a-all" - network = module.main.network_name - project = var.project_id - direction = "INGRESS" - priority = 1000 - - dynamic "log_config" { - for_each = var.firewall_enable_logging == true ? [{ - metadata = "INCLUDE_ALL_METADATA" - }] : [] - - content { - metadata = log_config.value.metadata - } - } - - allow { - protocol = "all" - } - - source_ranges = var.allow_all_ingress_ranges +module "firewall_rules" { + source = "terraform-google-modules/network/google//modules/network-firewall-policy" + version = "~> 8.0" + project_id = var.project_id + policy_name = "fp-${var.environment_code}-dual-svpc-restricted-firewalls" + description = "Firewall rules for restricted dual shared vpc: ${module.main.network_name}." + target_vpcs = ["projects/${var.project_id}/global/networks/${module.main.network_name}"] + + rules = concat( + [ + { + priority = "65530" + direction = "EGRESS" + action = "deny" + rule_name = "fw-${var.environment_code}-shared-restricted-65530-e-d-all-all-all" + description = "Lower priority rule to deny all egress traffic." + enable_logging = var.firewall_enable_logging + match = { + dest_ip_ranges = ["0.0.0.0/0"] + layer4_configs = [ + { + ip_protocol = "all" + }, + ] + } + }, + { + priority = "1000" + direction = "EGRESS" + action = "allow" + rule_name = "fw-${var.environment_code}-shared-restricted-1000-e-a-allow-google-apis-all-tcp-443" + description = "Lower priority rule to allow restricted google apis on TCP port 443." + enable_logging = var.firewall_enable_logging + match = { + dest_ip_ranges = [local.restricted_googleapis_cidr] + layer4_configs = [ + { + ip_protocol = "tcp" + ports = ["443"] + }, + ] + } + } + ], + !var.enable_all_vpc_internal_traffic ? [] : [ + { + priority = "10000" + direction = "EGRESS" + action = "allow" + rule_name = "fw-${var.environment_code}-shared-base-10000-e-a-all-all-all" + description = "Allow all egress to the provided IP range." + enable_logging = var.firewall_enable_logging + match = { + dest_ip_ranges = module.main.subnets_ips + layer4_configs = [ + { + ip_protocol = "all" + }, + ] + } + } + ], + !var.enable_all_vpc_internal_traffic ? [] : [ + { + priority = "10001" + direction = "INGRESS" + action = "allow" + rule_name = "fw-${var.environment_code}-shared-base-10001-i-a-all" + description = "Allow all ingress to the provided IP range." + enable_logging = var.firewall_enable_logging + match = { + src_ip_ranges = module.main.subnets_ips + layer4_configs = [ + { + ip_protocol = "all" + }, + ] + } + } + ] + ) } diff --git a/3-networks-dual-svpc/modules/restricted_shared_vpc/service_control.tf b/3-networks-dual-svpc/modules/restricted_shared_vpc/service_control.tf index 194c21f1e..b7cee7eef 100644 --- a/3-networks-dual-svpc/modules/restricted_shared_vpc/service_control.tf +++ b/3-networks-dual-svpc/modules/restricted_shared_vpc/service_control.tf @@ -49,10 +49,7 @@ resource "time_sleep" "wait_vpc_sc_propagation" { module.private_service_connect, google_dns_policy.default_policy, module.peering_zone, - google_compute_firewall.deny_all_egress, - google_compute_firewall.allow_restricted_api_egress, - google_compute_firewall.allow_all_egress, - google_compute_firewall.allow_all_ingress, + module.firewall_rules, google_compute_router.nat_router_region1, google_compute_address.nat_external_addresses1, google_compute_router_nat.nat_external_addresses_region1, diff --git a/3-networks-dual-svpc/modules/restricted_shared_vpc/variables.tf b/3-networks-dual-svpc/modules/restricted_shared_vpc/variables.tf index fceb6f26b..8805fae86 100644 --- a/3-networks-dual-svpc/modules/restricted_shared_vpc/variables.tf +++ b/3-networks-dual-svpc/modules/restricted_shared_vpc/variables.tf @@ -157,14 +157,10 @@ variable "restricted_services" { description = "List of services to restrict." } -variable "allow_all_egress_ranges" { - description = "List of network ranges to which all egress traffic will be allowed" - default = null -} - -variable "allow_all_ingress_ranges" { - description = "List of network ranges from which all ingress traffic will be allowed" - default = null +variable "enable_all_vpc_internal_traffic" { + type = bool + description = "Enable firewall policy rule to allow internal traffic (ingress and egress)." + default = false } variable "egress_policies" { diff --git a/3-networks-hub-and-spoke/envs/shared/net-hubs-transitivity.tf b/3-networks-hub-and-spoke/envs/shared/net-hubs-transitivity.tf index 048c1f476..049128b09 100644 --- a/3-networks-hub-and-spoke/envs/shared/net-hubs-transitivity.tf +++ b/3-networks-hub-and-spoke/envs/shared/net-hubs-transitivity.tf @@ -51,6 +51,7 @@ module "base_transitivity" { vpc_name = module.base_shared_vpc.network_name gw_subnets = { for region in keys(local.base_subnet_primary_ranges) : region => "sb-c-shared-base-hub-${region}" } regional_aggregates = local.base_regional_aggregates + firewall_policy = module.base_shared_vpc.firewall_policy commands = [ # Accept all ICMP (troubleshooting) "iptables -A INPUT -p icmp -j ACCEPT", @@ -85,6 +86,7 @@ module "restricted_transitivity" { vpc_name = module.restricted_shared_vpc.network_name gw_subnets = { for region in keys(local.restricted_subnet_primary_ranges) : region => "sb-c-shared-restricted-hub-${region}" } regional_aggregates = local.restricted_regional_aggregates + firewall_policy = module.restricted_shared_vpc.firewall_policy commands = [ # Accept all ICMP (troubleshooting) "iptables -A INPUT -p icmp -j ACCEPT", diff --git a/3-networks-hub-and-spoke/modules/base_env/main.tf b/3-networks-hub-and-spoke/modules/base_env/main.tf index 645f9d0be..640b36d03 100644 --- a/3-networks-hub-and-spoke/modules/base_env/main.tf +++ b/3-networks-hub-and-spoke/modules/base_env/main.tf @@ -235,8 +235,6 @@ module "restricted_shared_vpc" { secondary_ranges = { "sb-${var.environment_code}-shared-restricted-${var.default_region1}" = var.restricted_subnet_secondary_ranges[var.default_region1] } - allow_all_ingress_ranges = local.enable_transitivity ? local.restricted_hub_subnet_ranges : null - allow_all_egress_ranges = local.enable_transitivity ? local.restricted_subnet_aggregates : null } /****************************************** @@ -307,6 +305,4 @@ module "base_shared_vpc" { secondary_ranges = { "sb-${var.environment_code}-shared-base-${var.default_region1}" = var.base_subnet_secondary_ranges[var.default_region1] } - allow_all_ingress_ranges = local.enable_transitivity ? local.base_hub_subnet_ranges : null - allow_all_egress_ranges = local.enable_transitivity ? local.base_subnet_aggregates : null } diff --git a/3-networks-hub-and-spoke/modules/base_shared_vpc/README.md b/3-networks-hub-and-spoke/modules/base_shared_vpc/README.md index e9d33c423..bc1d6b4e1 100644 --- a/3-networks-hub-and-spoke/modules/base_shared_vpc/README.md +++ b/3-networks-hub-and-spoke/modules/base_shared_vpc/README.md @@ -3,8 +3,6 @@ | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| allow\_all\_egress\_ranges | List of network ranges to which all egress traffic will be allowed | `any` | `null` | no | -| allow\_all\_ingress\_ranges | List of network ranges from which all ingress traffic will be allowed | `any` | `null` | no | | base\_net\_hub\_project\_id | The base net hub project ID | `string` | `""` | no | | bgp\_asn\_subnet | BGP ASN for Subnets cloud routers. | `number` | n/a | yes | | default\_region1 | Default region 1 for subnets and Cloud Routers | `string` | n/a | yes | @@ -13,6 +11,8 @@ | dns\_enable\_logging | Toggle DNS logging for VPC DNS. | `bool` | `true` | no | | dns\_hub\_project\_id | The DNS hub project ID | `string` | n/a | yes | | domain | The DNS name of peering managed zone, for instance 'example.com.' | `string` | n/a | yes | +| enable\_all\_vpc\_internal\_traffic | Enable firewall policy rule to allow internal traffic (ingress and egress). | `bool` | `false` | no | +| enable\_transitivity\_traffic | Enable a firewall policy rule to allow traffic between Hub and Spokes (ingress only). | `bool` | `true` | no | | environment\_code | A short form of the folder level resources (environment) within the Google Cloud organization. | `string` | n/a | yes | | firewall\_enable\_logging | Toggle firewall logging for VPC Firewalls. | `bool` | `true` | no | | mode | Network deployment mode, should be set to `hub` or `spoke` when `enable_hub_and_spoke` architecture chosen, keep as `null` otherwise. | `string` | `null` | no | @@ -31,6 +31,7 @@ | Name | Description | |------|-------------| +| firewall\_policy | Policy created for firewall policy rules. | | network\_name | The name of the VPC being created | | network\_self\_link | The URI of the VPC being created | | region1\_router1 | Router 1 for Region 1 | diff --git a/3-networks-hub-and-spoke/modules/base_shared_vpc/firewall.tf b/3-networks-hub-and-spoke/modules/base_shared_vpc/firewall.tf index 6fdbc18c1..e288c832f 100644 --- a/3-networks-hub-and-spoke/modules/base_shared_vpc/firewall.tf +++ b/3-networks-hub-and-spoke/modules/base_shared_vpc/firewall.tf @@ -15,108 +15,87 @@ */ /****************************************** - Mandatory firewall rules + Mandatory and optional firewall rules *****************************************/ - -resource "google_compute_firewall" "deny_all_egress" { - name = "fw-${var.environment_code}-shared-base-65530-e-d-all-all-all" - network = module.main.network_name - project = var.project_id - direction = "EGRESS" - priority = 65530 - - dynamic "log_config" { - for_each = var.firewall_enable_logging == true ? [{ - metadata = "INCLUDE_ALL_METADATA" - }] : [] - - content { - metadata = log_config.value.metadata - } - } - - deny { - protocol = "all" - } - - destination_ranges = ["0.0.0.0/0"] -} - - -resource "google_compute_firewall" "allow_private_api_egress" { - name = "fw-${var.environment_code}-shared-base-65430-e-a-allow-google-apis-all-tcp-443" - network = module.main.network_name - project = var.project_id - direction = "EGRESS" - priority = 65430 - - dynamic "log_config" { - for_each = var.firewall_enable_logging == true ? [{ - metadata = "INCLUDE_ALL_METADATA" - }] : [] - - content { - metadata = log_config.value.metadata - } - } - - allow { - protocol = "tcp" - ports = ["443"] - } - - destination_ranges = [local.private_googleapis_cidr] - - target_tags = ["allow-google-apis"] -} - - -resource "google_compute_firewall" "allow_all_egress" { - count = var.allow_all_egress_ranges != null ? 1 : 0 - name = "fw-${var.environment_code}-shared-base-1000-e-a-all-all-all" - network = module.main.network_name - project = var.project_id - direction = "EGRESS" - priority = 1000 - - dynamic "log_config" { - for_each = var.firewall_enable_logging == true ? [{ - metadata = "INCLUDE_ALL_METADATA" - }] : [] - - content { - metadata = log_config.value.metadata - } - } - - allow { - protocol = "all" - } - - destination_ranges = var.allow_all_egress_ranges -} - -resource "google_compute_firewall" "allow_all_ingress" { - count = var.allow_all_ingress_ranges != null ? 1 : 0 - name = "fw-${var.environment_code}-shared-base-1000-i-a-all" - network = module.main.network_name - project = var.project_id - direction = "INGRESS" - priority = 1000 - - dynamic "log_config" { - for_each = var.firewall_enable_logging == true ? [{ - metadata = "INCLUDE_ALL_METADATA" - }] : [] - - content { - metadata = log_config.value.metadata - } - } - - allow { - protocol = "all" - } - - source_ranges = var.allow_all_ingress_ranges +module "firewall_rules" { + source = "terraform-google-modules/network/google//modules/network-firewall-policy" + version = "~> 8.0" + project_id = var.project_id + policy_name = "fp-${var.environment_code}-hub-and-spoke-base-firewalls" + description = "Firewall rules for base hub and spoke shared vpc: ${module.main.network_name}." + target_vpcs = ["projects/${var.project_id}/global/networks/${module.main.network_name}"] + + rules = concat( + [ + { + priority = "65530" + direction = "EGRESS" + action = "deny" + rule_name = "fw-${var.environment_code}-shared-base-65530-e-d-all-all-all" + description = "Lower priority rule to deny all egress traffic." + enable_logging = var.firewall_enable_logging + match = { + dest_ip_ranges = ["0.0.0.0/0"] + layer4_configs = [ + { + ip_protocol = "all" + }, + ] + } + }, + { + priority = "1000" + direction = "EGRESS" + action = "allow" + rule_name = "fw-${var.environment_code}-shared-base-1000-e-a-allow-google-apis-all-tcp-443" + description = "Lower priority rule to allow private google apis on TCP port 443." + enable_logging = var.firewall_enable_logging + match = { + dest_ip_ranges = [local.private_googleapis_cidr] + layer4_configs = [ + { + ip_protocol = "tcp" + ports = ["443"] + }, + ] + } + } + ], + !var.enable_all_vpc_internal_traffic ? [] : [ + { + priority = "10000" + direction = "EGRESS" + action = "allow" + rule_name = "fw-${var.environment_code}-shared-base-10000-e-a-all-all-all" + description = "Allow all egress to the provided IP range." + enable_logging = var.firewall_enable_logging + match = { + dest_ip_ranges = module.main.subnets_ips + layer4_configs = [ + { + ip_protocol = "all" + }, + ] + } + } + ], + !var.enable_all_vpc_internal_traffic ? [] : [ + { + priority = "10001" + direction = "INGRESS" + action = "allow" + rule_name = "fw-${var.environment_code}-shared-base-10001-i-a-all" + description = "Allow all ingress to the provided IP range." + enable_logging = var.firewall_enable_logging + match = { + src_ip_ranges = module.main.subnets_ips + layer4_configs = [ + { + ip_protocol = "all" + }, + ] + } + } + ] + ) } diff --git a/3-networks-hub-and-spoke/modules/base_shared_vpc/outputs.tf b/3-networks-hub-and-spoke/modules/base_shared_vpc/outputs.tf index 49173a31d..d7527cbc7 100644 --- a/3-networks-hub-and-spoke/modules/base_shared_vpc/outputs.tf +++ b/3-networks-hub-and-spoke/modules/base_shared_vpc/outputs.tf @@ -78,3 +78,8 @@ output "region2_router2" { value = try(module.region2_router2[0], null) description = "Router 2 for Region 2" } + +output "firewall_policy" { + value = module.firewall_rules.fw_policy[0].name + description = "Policy created for firewall policy rules." +} diff --git a/3-networks-hub-and-spoke/modules/base_shared_vpc/variables.tf b/3-networks-hub-and-spoke/modules/base_shared_vpc/variables.tf index f9d92a43d..0afd5bbaa 100644 --- a/3-networks-hub-and-spoke/modules/base_shared_vpc/variables.tf +++ b/3-networks-hub-and-spoke/modules/base_shared_vpc/variables.tf @@ -149,12 +149,14 @@ variable "windows_activation_enabled" { default = false } -variable "allow_all_egress_ranges" { - description = "List of network ranges to which all egress traffic will be allowed" - default = null +variable "enable_all_vpc_internal_traffic" { + type = bool + description = "Enable firewall policy rule to allow internal traffic (ingress and egress)." + default = false } -variable "allow_all_ingress_ranges" { - description = "List of network ranges from which all ingress traffic will be allowed" - default = null +variable "enable_transitivity_traffic" { + type = bool + description = "Enable a firewall policy rule to allow traffic between Hub and Spokes (ingress only)." + default = true } diff --git a/3-networks-hub-and-spoke/modules/restricted_shared_vpc/README.md b/3-networks-hub-and-spoke/modules/restricted_shared_vpc/README.md index 63291e27a..41edab23c 100644 --- a/3-networks-hub-and-spoke/modules/restricted_shared_vpc/README.md +++ b/3-networks-hub-and-spoke/modules/restricted_shared_vpc/README.md @@ -4,8 +4,6 @@ | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| | access\_context\_manager\_policy\_id | The id of the default Access Context Manager policy. Can be obtained by running `gcloud access-context-manager policies list --organization YOUR_ORGANIZATION_ID --format="value(name)"`. | `number` | n/a | yes | -| allow\_all\_egress\_ranges | List of network ranges to which all egress traffic will be allowed | `any` | `null` | no | -| allow\_all\_ingress\_ranges | List of network ranges from which all ingress traffic will be allowed | `any` | `null` | no | | bgp\_asn\_subnet | BGP ASN for Subnets cloud routers. | `number` | n/a | yes | | default\_region1 | First subnet region. The shared vpc modules only configures two regions. | `string` | n/a | yes | | default\_region2 | Second subnet region. The shared vpc modules only configures two regions. | `string` | n/a | yes | @@ -14,6 +12,8 @@ | dns\_hub\_project\_id | The DNS hub project ID | `string` | n/a | yes | | domain | The DNS name of peering managed zone, for instance 'example.com.' | `string` | n/a | yes | | egress\_policies | A list of all [egress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#egress-rules-reference), each list object has a `from` and `to` value that describes egress\_from and egress\_to.

Example: `[{ from={ identities=[], identity_type="ID_TYPE" }, to={ resources=[], operations={ "SRV_NAME"={ OP_TYPE=[] }}}}]`

Valid Values:
`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`
`SRV_NAME` = "`*`" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)
`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) |
list(object({
from = any
to = any
}))
| `[]` | no | +| enable\_all\_vpc\_internal\_traffic | Enable firewall policy rule to allow internal traffic (ingress and egress). | `bool` | `false` | no | +| enable\_transitivity\_traffic | Enable a firewall policy rule to allow traffic between Hub and Spokes (ingress only). | `bool` | `true` | no | | environment\_code | A short form of the folder level resources (environment) within the Google Cloud organization. | `string` | n/a | yes | | firewall\_enable\_logging | Toggle firewall logging for VPC Firewalls. | `bool` | `true` | no | | ingress\_policies | A list of all [ingress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#ingress-rules-reference), each list object has a `from` and `to` value that describes ingress\_from and ingress\_to.

Example: `[{ from={ sources={ resources=[], access_levels=[] }, identities=[], identity_type="ID_TYPE" }, to={ resources=[], operations={ "SRV_NAME"={ OP_TYPE=[] }}}}]`

Valid Values:
`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`
`SRV_NAME` = "`*`" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)
`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) |
list(object({
from = any
to = any
}))
| `[]` | no | @@ -39,6 +39,7 @@ | Name | Description | |------|-------------| | access\_level\_name | Access context manager access level name | +| firewall\_policy | Policy created for firewall policy rules. | | network\_name | The name of the VPC being created | | network\_self\_link | The URI of the VPC being created | | region1\_router1 | Router 1 for Region 1 | diff --git a/3-networks-hub-and-spoke/modules/restricted_shared_vpc/firewall.tf b/3-networks-hub-and-spoke/modules/restricted_shared_vpc/firewall.tf index fda32317d..4ccccaff9 100644 --- a/3-networks-hub-and-spoke/modules/restricted_shared_vpc/firewall.tf +++ b/3-networks-hub-and-spoke/modules/restricted_shared_vpc/firewall.tf @@ -16,107 +16,87 @@ /****************************************** - Mandatory firewall rules + Mandatory and optional firewall rules *****************************************/ -resource "google_compute_firewall" "deny_all_egress" { - name = "fw-${var.environment_code}-shared-restricted-65530-e-d-all-all-all" - network = module.main.network_name - project = var.project_id - direction = "EGRESS" - priority = 65530 - - dynamic "log_config" { - for_each = var.firewall_enable_logging == true ? [{ - metadata = "INCLUDE_ALL_METADATA" - }] : [] - - content { - metadata = log_config.value.metadata - } - } - - deny { - protocol = "all" - } - - destination_ranges = ["0.0.0.0/0"] -} - -resource "google_compute_firewall" "allow_restricted_api_egress" { - name = "fw-${var.environment_code}-shared-restricted-65430-e-a-allow-google-apis-all-tcp-443" - network = module.main.network_name - project = var.project_id - direction = "EGRESS" - priority = 65430 - - dynamic "log_config" { - for_each = var.firewall_enable_logging == true ? [{ - metadata = "INCLUDE_ALL_METADATA" - }] : [] - - content { - metadata = log_config.value.metadata - } - } - - allow { - protocol = "tcp" - ports = ["443"] - } - - destination_ranges = [local.restricted_googleapis_cidr] - - target_tags = ["allow-google-apis"] -} - -resource "google_compute_firewall" "allow_all_egress" { - count = var.allow_all_egress_ranges != null ? 1 : 0 - - name = "fw-${var.environment_code}-shared-base-1000-e-a-all-all-all" - network = module.main.network_name - project = var.project_id - direction = "EGRESS" - priority = 1000 - - dynamic "log_config" { - for_each = var.firewall_enable_logging == true ? [{ - metadata = "INCLUDE_ALL_METADATA" - }] : [] - - content { - metadata = log_config.value.metadata - } - } - - allow { - protocol = "all" - } - - destination_ranges = var.allow_all_egress_ranges -} - -resource "google_compute_firewall" "allow_all_ingress" { - count = var.allow_all_ingress_ranges != null ? 1 : 0 - - name = "fw-${var.environment_code}-shared-base-1000-i-a-all" - network = module.main.network_name - project = var.project_id - direction = "INGRESS" - priority = 1000 - - dynamic "log_config" { - for_each = var.firewall_enable_logging == true ? [{ - metadata = "INCLUDE_ALL_METADATA" - }] : [] - - content { - metadata = log_config.value.metadata - } - } - - allow { - protocol = "all" - } - - source_ranges = var.allow_all_ingress_ranges +module "firewall_rules" { + source = "terraform-google-modules/network/google//modules/network-firewall-policy" + version = "~> 8.0" + project_id = var.project_id + policy_name = "fp-${var.environment_code}-hub-and-spoke-restricted-firewalls" + description = "Firewall rules for restricted hub and spoke shared vpc: ${module.main.network_name}." + target_vpcs = ["projects/${var.project_id}/global/networks/${module.main.network_name}"] + + rules = concat( + [ + { + priority = "65530" + direction = "EGRESS" + action = "deny" + rule_name = "fw-${var.environment_code}-shared-restricted-65530-e-d-all-all-all" + description = "Lower priority rule to deny all egress traffic." + enable_logging = var.firewall_enable_logging + match = { + dest_ip_ranges = ["0.0.0.0/0"] + layer4_configs = [ + { + ip_protocol = "all" + }, + ] + } + }, + { + priority = "1000" + direction = "EGRESS" + action = "allow" + rule_name = "fw-${var.environment_code}-shared-restricted-1000-e-a-allow-google-apis-all-tcp-443" + description = "Lower priority rule to allow restricted google apis on TCP port 443." + enable_logging = var.firewall_enable_logging + match = { + dest_ip_ranges = [local.restricted_googleapis_cidr] + layer4_configs = [ + { + ip_protocol = "tcp" + ports = ["443"] + }, + ] + } + } + ], + !var.enable_all_vpc_internal_traffic ? [] : [ + { + priority = "10000" + direction = "EGRESS" + action = "allow" + rule_name = "fw-${var.environment_code}-shared-base-10000-e-a-all-all-all" + description = "Allow all egress to the provided IP range." + enable_logging = var.firewall_enable_logging + match = { + dest_ip_ranges = module.main.subnets_ips + layer4_configs = [ + { + ip_protocol = "all" + }, + ] + } + } + ], + !var.enable_all_vpc_internal_traffic ? [] : [ + { + priority = "10001" + direction = "INGRESS" + action = "allow" + rule_name = "fw-${var.environment_code}-shared-base-10001-i-a-all" + description = "Allow all ingress to the provided IP range." + enable_logging = var.firewall_enable_logging + match = { + src_ip_ranges = module.main.subnets_ips + layer4_configs = [ + { + ip_protocol = "all" + }, + ] + } + } + ] + ) } diff --git a/3-networks-hub-and-spoke/modules/restricted_shared_vpc/outputs.tf b/3-networks-hub-and-spoke/modules/restricted_shared_vpc/outputs.tf index 634e4c3bc..989c1b5b5 100644 --- a/3-networks-hub-and-spoke/modules/restricted_shared_vpc/outputs.tf +++ b/3-networks-hub-and-spoke/modules/restricted_shared_vpc/outputs.tf @@ -78,3 +78,8 @@ output "region2_router2" { value = try(module.region2_router2[0], null) description = "Router 2 for Region 2" } + +output "firewall_policy" { + value = module.firewall_rules.fw_policy[0].name + description = "Policy created for firewall policy rules." +} diff --git a/3-networks-hub-and-spoke/modules/restricted_shared_vpc/service_control.tf b/3-networks-hub-and-spoke/modules/restricted_shared_vpc/service_control.tf index 89ac51d58..e95ae8bb2 100644 --- a/3-networks-hub-and-spoke/modules/restricted_shared_vpc/service_control.tf +++ b/3-networks-hub-and-spoke/modules/restricted_shared_vpc/service_control.tf @@ -51,10 +51,7 @@ resource "time_sleep" "wait_vpc_sc_propagation" { module.private_service_connect, google_dns_policy.default_policy, module.peering_zone, - google_compute_firewall.deny_all_egress, - google_compute_firewall.allow_restricted_api_egress, - google_compute_firewall.allow_all_egress, - google_compute_firewall.allow_all_ingress, + module.firewall_rules, google_compute_router.nat_router_region1, google_compute_address.nat_external_addresses1, google_compute_router_nat.nat_external_addresses_region1, diff --git a/3-networks-hub-and-spoke/modules/restricted_shared_vpc/variables.tf b/3-networks-hub-and-spoke/modules/restricted_shared_vpc/variables.tf index 55f9fb0d6..2bd9ecb3e 100644 --- a/3-networks-hub-and-spoke/modules/restricted_shared_vpc/variables.tf +++ b/3-networks-hub-and-spoke/modules/restricted_shared_vpc/variables.tf @@ -175,14 +175,16 @@ variable "restricted_services" { description = "List of services to restrict." } -variable "allow_all_egress_ranges" { - description = "List of network ranges to which all egress traffic will be allowed" - default = null +variable "enable_all_vpc_internal_traffic" { + type = bool + description = "Enable firewall policy rule to allow internal traffic (ingress and egress)." + default = false } -variable "allow_all_ingress_ranges" { - description = "List of network ranges from which all ingress traffic will be allowed" - default = null +variable "enable_transitivity_traffic" { + type = bool + description = "Enable a firewall policy rule to allow traffic between Hub and Spokes (ingress only)." + default = true } variable "egress_policies" { diff --git a/3-networks-hub-and-spoke/modules/transitivity/README.md b/3-networks-hub-and-spoke/modules/transitivity/README.md index 6ba3a3ee3..43e656ee6 100755 --- a/3-networks-hub-and-spoke/modules/transitivity/README.md +++ b/3-networks-hub-and-spoke/modules/transitivity/README.md @@ -14,6 +14,7 @@ For example usage, please check the the [net-hubs-transitivity.tf](../../envs/sh |------|-------------|------|---------|:--------:| | commands | Commands for the transitivity gateway to run on every boot. | `list(string)` | `[]` | no | | firewall\_enable\_logging | Toggle firewall logging for VPC Firewalls. | `bool` | `true` | no | +| firewall\_policy | Network Firewall Policy Id to deploy transitivity firewall rules. | `string` | n/a | yes | | gw\_subnets | Subnets in {REGION => SUBNET} format. | `map(string)` | n/a | yes | | health\_check\_enable\_log | Toggle logging for health checks. | `bool` | `false` | no | | project\_id | VPC Project ID | `string` | n/a | yes | diff --git a/3-networks-hub-and-spoke/modules/transitivity/main.tf b/3-networks-hub-and-spoke/modules/transitivity/main.tf index 95f74693e..1014c97c4 100644 --- a/3-networks-hub-and-spoke/modules/transitivity/main.tf +++ b/3-networks-hub-and-spoke/modules/transitivity/main.tf @@ -20,6 +20,7 @@ locals { stripped_vpc_name = replace(var.vpc_name, "vpc-", "") + routes = flatten([for region, ranges in var.regional_aggregates : [for range in ranges : { region = region, range = range }]]) } module "service_account" { @@ -127,10 +128,6 @@ module "ilbs" { project = var.project_id } -locals { - routes = flatten([for region, ranges in var.regional_aggregates : [for range in ranges : { region = region, range = range }]]) -} - resource "google_compute_route" "routes" { for_each = { for route in local.routes : replace("ilb-${route.region}-${route.range}", "/[./]/", "-") => route @@ -143,52 +140,38 @@ resource "google_compute_route" "routes" { next_hop_ilb = module.ilbs[each.value.region].forwarding_rule } -resource "google_compute_firewall" "allow_transtivity_ingress" { - name = "fw-${local.stripped_vpc_name}-1000-i-a-all-all-all-transitivity" - network = var.vpc_name - project = var.project_id - direction = "INGRESS" - priority = 1000 - - dynamic "log_config" { - for_each = var.firewall_enable_logging == true ? [{ - metadata = "INCLUDE_ALL_METADATA" - }] : [] +resource "google_compute_network_firewall_policy_rule" "allow_transtivity_ingress" { + rule_name = "fw-${local.stripped_vpc_name}-20000-i-a-all-all-all-transitivity" + project = var.project_id + firewall_policy = var.firewall_policy + priority = 20000 + direction = "INGRESS" + action = "allow" + target_service_accounts = [module.service_account.email] + description = "Allow ingress from regional IP ranges." - content { - metadata = log_config.value.metadata + match { + src_ip_ranges = flatten(values(var.regional_aggregates)) + layer4_configs { + ip_protocol = "all" } } - - allow { - protocol = "all" - } - - source_ranges = flatten(values(var.regional_aggregates)) - target_service_accounts = [module.service_account.email] } -resource "google_compute_firewall" "allow_transitivity_egress" { - name = "fw-allow-transitivity-egress" - network = var.vpc_name - project = var.project_id - direction = "EGRESS" - priority = 1000 - - dynamic "log_config" { - for_each = var.firewall_enable_logging == true ? [{ - metadata = "INCLUDE_ALL_METADATA" - }] : [] +resource "google_compute_network_firewall_policy_rule" "allow_transitivity_egress" { + rule_name = "fw-${local.stripped_vpc_name}-20001-e-a-all-all-all-transitivity" + project = var.project_id + firewall_policy = var.firewall_policy + priority = 20001 + direction = "EGRESS" + action = "allow" + target_service_accounts = [module.service_account.email] + description = "Allow egress from regional IP ranges." - content { - metadata = log_config.value.metadata + match { + dest_ip_ranges = flatten(values(var.regional_aggregates)) + layer4_configs { + ip_protocol = "all" } } - - allow { - protocol = "all" - } - - destination_ranges = flatten(values(var.regional_aggregates)) - target_service_accounts = [module.service_account.email] } diff --git a/3-networks-hub-and-spoke/modules/transitivity/variables.tf b/3-networks-hub-and-spoke/modules/transitivity/variables.tf index ef865d5cc..96a039e57 100644 --- a/3-networks-hub-and-spoke/modules/transitivity/variables.tf +++ b/3-networks-hub-and-spoke/modules/transitivity/variables.tf @@ -57,3 +57,8 @@ variable "health_check_enable_log" { description = "Toggle logging for health checks." default = false } + +variable "firewall_policy" { + type = string + description = "Network Firewall Policy Id to deploy transitivity firewall rules." +} diff --git a/4-projects/modules/base_env/example_peering_project.tf b/4-projects/modules/base_env/example_peering_project.tf index c7a8d7ac9..0ca5b1354 100644 --- a/4-projects/modules/base_env/example_peering_project.tf +++ b/4-projects/modules/base_env/example_peering_project.tf @@ -119,173 +119,130 @@ module "peering" { } /****************************************** - Mandatory firewall rules + Mandatory and optional firewall rules *****************************************/ - -resource "google_compute_firewall" "deny_all_egress" { - name = "fw-${local.env_code}-peering-base-65530-e-d-all-all-tcp-udp" - network = module.peering_network.network_name - project = module.peering_project.project_id - direction = "EGRESS" - priority = 65530 - - dynamic "log_config" { - for_each = var.firewall_enable_logging == true ? [{ - metadata = "INCLUDE_ALL_METADATA" - }] : [] - - content { - metadata = log_config.value.metadata - } - } - - deny { - protocol = "tcp" - } - - deny { - protocol = "udp" - } - - destination_ranges = ["0.0.0.0/0"] -} - - -resource "google_compute_firewall" "allow_private_api_egress" { - name = "fw-${local.env_code}-peering-base-65430-e-a-allow-google-apis-all-tcp-443" - network = module.peering_network.network_name - project = module.peering_project.project_id - direction = "EGRESS" - priority = 65430 - - dynamic "log_config" { - for_each = var.firewall_enable_logging == true ? [{ - metadata = "INCLUDE_ALL_METADATA" - }] : [] - - content { - metadata = log_config.value.metadata - } - } - - allow { - protocol = "tcp" - ports = ["443"] - } - - destination_ranges = ["199.36.153.8/30"] - - target_tags = ["allow-google-apis"] -} - - -/****************************************** - Optional firewall rules - *****************************************/ - -// Allow access to kms.windows.googlecloud.com for Windows license activation -resource "google_compute_firewall" "allow_windows_activation" { - count = var.windows_activation_enabled ? 1 : 0 - name = "fw-${local.env_code}-peering-base-0-e-a-allow-win-activation-all-tcp-1688" - network = module.peering_network.network_name - project = module.peering_project.project_id - direction = "EGRESS" - priority = 0 - - dynamic "log_config" { - for_each = var.firewall_enable_logging == true ? [{ - metadata = "INCLUDE_ALL_METADATA" - }] : [] - - content { - metadata = log_config.value.metadata - } - } - - allow { - protocol = "tcp" - ports = ["1688"] - } - - destination_ranges = ["35.190.247.13/32"] - - target_tags = ["allow-win-activation"] -} - -// Allow traffic for Internal & Global load balancing health check and load balancing IP ranges. -resource "google_compute_firewall" "allow_lb" { - count = var.optional_fw_rules_enabled ? 1 : 0 - name = "fw-${local.env_code}-peering-base-1000-i-a-all-allow-lb-tcp-80-8080-443" - network = module.peering_network.network_name - project = module.peering_project.project_id - - dynamic "log_config" { - for_each = var.firewall_enable_logging == true ? [{ - metadata = "INCLUDE_ALL_METADATA" - }] : [] - - content { - metadata = log_config.value.metadata - } - } - - source_ranges = concat(data.google_netblock_ip_ranges.health_checkers.cidr_blocks_ipv4, data.google_netblock_ip_ranges.legacy_health_checkers.cidr_blocks_ipv4) - - // Allow common app ports by default. - allow { - protocol = "tcp" - ports = ["80", "8080", "443"] - } - - target_tags = ["allow-lb"] -} - -// Allow SSH and RDP via IAP when using the Firewall Secure Tags. -module "allow_iap_ssh_rdp" { - source = "terraform-google-modules/network/google//modules/network-firewall-policy" - version = "~> 8.0" - +module "firewall_rules" { + source = "terraform-google-modules/network/google//modules/network-firewall-policy" + version = "~> 8.0" project_id = module.peering_project.project_id - policy_name = "fp-${local.env_code}-allow-iap-policy" - - rules = [ - { - // Allow SSH via IAP when using the ssh-iap-access/allow resource manager tag for Linux workloads. - rule_name = "fw-${local.env_code}-peering-base-1000-i-a-all-allow-iap-ssh-tcp-22" - action = "allow" - direction = "INGRESS" - priority = "1000" - enable_logging = true - target_secure_tags = ["tagValues/${google_tags_tag_value.firewall_tag_value_ssh[0].name}"] - match = { - src_ip_ranges = data.google_netblock_ip_ranges.iap_forwarders.cidr_blocks_ipv4 - layer4_configs = [ - { - ip_protocol = "tcp" - ports = ["22"] - }, - ] + policy_name = "fp-${local.env_code}-peering-project-firewalls" + description = "Firewall rules for Peering Network: ${module.peering_network.network_name}." + + rules = concat( + [ + { + priority = "65530" + direction = "EGRESS" + action = "deny" + rule_name = "fw-${local.env_code}-peering-base-65530-e-d-all-all-tcp-udp" + description = "Lower priority rule to deny all egress traffic." + enable_logging = var.firewall_enable_logging + match = { + dest_ip_ranges = ["0.0.0.0/0"] + layer4_configs = [ + { + ip_protocol = "tcp" + }, + { + ip_protocol = "udp" + }, + ] + } + }, + { + priority = "10000" + direction = "EGRESS" + action = "allow" + rule_name = "fw-${local.env_code}-peering-base-10000-e-a-allow-google-apis-all-tcp-443" + description = "Lower priority rule to allow private google apis on TCP port 443." + enable_logging = var.firewall_enable_logging + match = { + dest_ip_ranges = ["199.36.153.8/30"] + layer4_configs = [ + { + ip_protocol = "tcp" + ports = ["443"] + }, + ] + } + }, + { + // Allow SSH via IAP when using the ssh-iap-access/allow resource manager tag for Linux workloads. + rule_name = "fw-${local.env_code}-peering-base-1000-i-a-all-allow-iap-ssh-tcp-22" + action = "allow" + direction = "INGRESS" + priority = "1000" + enable_logging = true + target_secure_tags = ["tagValues/${google_tags_tag_value.firewall_tag_value_ssh[0].name}"] + match = { + src_ip_ranges = data.google_netblock_ip_ranges.iap_forwarders.cidr_blocks_ipv4 + layer4_configs = [ + { + ip_protocol = "tcp" + ports = ["22"] + }, + ] + } + }, + { + // Allow RDP via IAP when using the rdp-iap-access/allow resource manager tag for Windows workloads. + rule_name = "fw-${local.env_code}-peering-base-1001-i-a-all-allow-iap-rdp-tcp-3389" + action = "allow" + direction = "INGRESS" + priority = "1001" + enable_logging = true + target_secure_tags = ["tagValues/${google_tags_tag_value.firewall_tag_value_rdp[0].name}"] + match = { + src_ip_ranges = data.google_netblock_ip_ranges.iap_forwarders.cidr_blocks_ipv4 + layer4_configs = [ + { + ip_protocol = "tcp" + ports = ["3389"] + }, + ] + } } - }, - { - // Allow RDP via IAP when using the rdp-iap-access/allow resource manager tag for Windows workloads. - rule_name = "fw-${local.env_code}-peering-base-1001-i-a-all-allow-iap-rdp-tcp-3389" - action = "allow" - direction = "INGRESS" - priority = "1001" - enable_logging = true - target_secure_tags = ["tagValues/${google_tags_tag_value.firewall_tag_value_rdp[0].name}"] - match = { - src_ip_ranges = data.google_netblock_ip_ranges.iap_forwarders.cidr_blocks_ipv4 - layer4_configs = [ - { - ip_protocol = "tcp" - ports = ["3389"] - }, - ] + ], + !var.windows_activation_enabled ? [] : [ + { + priority = "0" + direction = "EGRESS" + action = "allow" + rule_name = "fw-${local.env_code}-peering-base-0-e-a-allow-win-activation-all-tcp-1688" + description = "Allow access to kms.windows.googlecloud.com for Windows license activation." + enable_logging = var.firewall_enable_logging + match = { + dest_ip_ranges = ["35.190.247.13/32"] + layer4_configs = [ + { + ip_protocol = "tcp" + ports = ["1688"] + }, + ] + } } - } - ] + ], + !var.optional_fw_rules_enabled ? [] : [ + { + priority = "1000" + direction = "INGRESS" + action = "allow" + rule_name = "fw-${local.env_code}-peering-base-1000-i-a-all-allow-lb-tcp-80-8080-443" + description = "Allow traffic for Internal & Global load balancing health check and load balancing IP ranges." + enable_logging = var.firewall_enable_logging + match = { + src_ip_ranges = concat(data.google_netblock_ip_ranges.health_checkers.cidr_blocks_ipv4, data.google_netblock_ip_ranges.legacy_health_checkers.cidr_blocks_ipv4) + layer4_configs = [ + { + // Allow common app ports by default. + ip_protocol = "tcp" + ports = ["80", "8080", "443"] + }, + ] + } + }, + ] + ) depends_on = [ google_tags_tag_value.firewall_tag_value_ssh, @@ -293,14 +250,14 @@ module "allow_iap_ssh_rdp" { ] } -resource "google_compute_network_firewall_policy_association" "vpc_associations" { - name = "fpa-${local.env_code}-allow-iap-ssh-rdp" +resource "google_compute_network_firewall_policy_association" "vpc_association" { + name = "${module.firewall_rules.fw_policy[0].name}-${module.peering_network.network_name}" attachment_target = module.peering_network.network_id - firewall_policy = module.allow_iap_ssh_rdp.fw_policy[0].id + firewall_policy = module.firewall_rules.fw_policy[0].id project = module.peering_project.project_id depends_on = [ - module.allow_iap_ssh_rdp, + module.firewall_rules, module.peering_network ] } diff --git a/test/integration/networks/networks_test.go b/test/integration/networks/networks_test.go index b99f584d3..72b144e57 100644 --- a/test/integration/networks/networks_test.go +++ b/test/integration/networks/networks_test.go @@ -37,7 +37,15 @@ func getNetworkMode(t *testing.T) string { return "" } -func getNetworkResourceNames(envCode string, networkMode string) map[string]map[string]string { +func getFirewallMode(t *testing.T) string { + mode := utils.ValFromEnv(t, "TF_VAR_example_foundations_mode") + if mode == "HubAndSpoke" { + return "hub-and-spoke" + } + return "dual-svpc" +} + +func getNetworkResourceNames(envCode string, networkMode string, firewallMode string) map[string]map[string]string { return map[string]map[string]string{ "base": { "network_name": fmt.Sprintf("vpc-%s-shared-base%s", envCode, networkMode), @@ -53,8 +61,9 @@ func getNetworkResourceNames(envCode string, networkMode string) map[string]map[ "region1_router2": fmt.Sprintf("cr-%s-shared-base%s-us-west1-cr2", envCode, networkMode), "region2_router1": fmt.Sprintf("cr-%s-shared-base%s-us-central1-cr3", envCode, networkMode), "region2_router2": fmt.Sprintf("cr-%s-shared-base%s-us-central1-cr4", envCode, networkMode), + "firewall_policy": fmt.Sprintf("fp-%s-%s-base-firewalls", envCode, firewallMode), "fw_deny_all_egress": fmt.Sprintf("fw-%s-shared-base-65530-e-d-all-all-all", envCode), - "fw_allow_api_egress": fmt.Sprintf("fw-%s-shared-base-65430-e-a-allow-google-apis-all-tcp-443", envCode), + "fw_allow_api_egress": fmt.Sprintf("fw-%s-shared-base-1000-e-a-allow-google-apis-all-tcp-443", envCode), }, "restricted": { "network_name": fmt.Sprintf("vpc-%s-shared-restricted%s", envCode, networkMode), @@ -70,8 +79,9 @@ func getNetworkResourceNames(envCode string, networkMode string) map[string]map[ "region1_router2": fmt.Sprintf("cr-%s-shared-restricted%s-us-west1-cr6", envCode, networkMode), "region2_router1": fmt.Sprintf("cr-%s-shared-restricted%s-us-central1-cr7", envCode, networkMode), "region2_router2": fmt.Sprintf("cr-%s-shared-restricted%s-us-central1-cr8", envCode, networkMode), + "firewall_policy": fmt.Sprintf("fp-%s-%s-restricted-firewalls", envCode, firewallMode), "fw_deny_all_egress": fmt.Sprintf("fw-%s-shared-restricted-65530-e-d-all-all-all", envCode), - "fw_allow_api_egress": fmt.Sprintf("fw-%s-shared-restricted-65430-e-a-allow-google-apis-all-tcp-443", envCode), + "fw_allow_api_egress": fmt.Sprintf("fw-%s-shared-restricted-1000-e-a-allow-google-apis-all-tcp-443", envCode), }, } } @@ -84,6 +94,7 @@ func TestNetworks(t *testing.T) { orgID := terraform.OutputMap(t, bootstrap.GetTFOptions(), "common_config")["org_id"] networkMode := getNetworkMode(t) + firewallMode := getFirewallMode(t) policyID := testutils.GetOrgACMPolicyID(t, orgID) require.NotEmpty(t, policyID, "Access Context Manager Policy ID must be configured in the organization for the test to proceed.") @@ -335,7 +346,7 @@ func TestNetworks(t *testing.T) { servicePerimeterLink := fmt.Sprintf("accessPolicies/%s/servicePerimeters/%s", policyID, networks.GetStringOutput("restricted_service_perimeter_name")) accessLevel := fmt.Sprintf("accessPolicies/%s/accessLevels/%s", policyID, networks.GetStringOutput("restricted_access_level_name")) - networkNames := getNetworkResourceNames(envCode, networkMode) + networkNames := getNetworkResourceNames(envCode, networkMode, firewallMode) servicePerimeter := gcloud.Runf(t, "access-context-manager perimeters describe %s --policy %s", servicePerimeterLink, policyID) assert.Equal(servicePerimeterLink, servicePerimeter.Get("name").String(), fmt.Sprintf("service perimeter %s should exist", servicePerimeterLink)) @@ -399,26 +410,20 @@ func TestNetworks(t *testing.T) { assert.Equal(usCentral1Range, subnet2.Get("ipCidrRange").String(), fmt.Sprintf("IP CIDR range %s should be", usCentral1Range)) denyAllEgressName := networkNames[networkType]["fw_deny_all_egress"] - denyAllEgressRule := gcloud.Runf(t, "compute firewall-rules describe %s --project %s --impersonate-service-account %s", denyAllEgressName, projectID, terraformSA) - assert.Equal(denyAllEgressName, denyAllEgressRule.Get("name").String(), fmt.Sprintf("firewall rule %s should exist", denyAllEgressName)) + denyAllEgressRule := gcloud.Runf(t, "compute network-firewall-policies rules describe 65530 --firewall-policy %s --global-firewall-policy --project %s --impersonate-service-account %s", networkNames[networkType]["firewall_policy"], projectID, terraformSA).Array()[0] + assert.Equal(denyAllEgressName, denyAllEgressRule.Get("ruleName").String(), fmt.Sprintf("firewall rule %s should exist", denyAllEgressName)) assert.Equal("EGRESS", denyAllEgressRule.Get("direction").String(), fmt.Sprintf("firewall rule %s direction should be EGRESS", denyAllEgressName)) - assert.True(denyAllEgressRule.Get("logConfig.enable").Bool(), fmt.Sprintf("firewall rule %s should have log configuration enabled", denyAllEgressName)) - assert.Equal("0.0.0.0/0", denyAllEgressRule.Get("destinationRanges").Array()[0].String(), fmt.Sprintf("firewall rule %s destination ranges should be 0.0.0.0/0", denyAllEgressName)) - assert.Equal(1, len(denyAllEgressRule.Get("denied").Array()), fmt.Sprintf("firewall rule %s should have only one denied", denyAllEgressName)) - assert.Equal(1, len(denyAllEgressRule.Get("denied.0").Map()), fmt.Sprintf("firewall rule %s should have only one denied only with no ports", denyAllEgressName)) - assert.Equal("all", denyAllEgressRule.Get("denied.0.IPProtocol").String(), fmt.Sprintf("firewall rule %s should deny all protocols", denyAllEgressName)) + assert.Equal("deny", denyAllEgressRule.Get("action").String(), fmt.Sprintf("firewall rule %s action should be deny", denyAllEgressName)) + assert.True(denyAllEgressRule.Get("enableLogging").Bool(), fmt.Sprintf("firewall rule %s should have log configuration enabled", denyAllEgressName)) + assert.Equal("0.0.0.0/0", denyAllEgressRule.Get("match.destIpRanges").Array()[0].String(), fmt.Sprintf("firewall rule %s destination ranges should be 0.0.0.0/0", denyAllEgressName)) allowApiEgressName := networkNames[networkType]["fw_allow_api_egress"] - allowApiEgressRule := gcloud.Runf(t, "compute firewall-rules describe %s --project %s --impersonate-service-account %s", allowApiEgressName, projectID, terraformSA) - assert.Equal(allowApiEgressName, allowApiEgressRule.Get("name").String(), fmt.Sprintf("firewall rule %s should exist", allowApiEgressName)) + allowApiEgressRule := gcloud.Runf(t, "compute network-firewall-policies rules describe 1000 --firewall-policy %s --global-firewall-policy --project %s --impersonate-service-account %s", networkNames[networkType]["firewall_policy"], projectID, terraformSA).Array()[0] + assert.Equal(allowApiEgressName, allowApiEgressRule.Get("ruleName").String(), fmt.Sprintf("firewall rule %s should exist", allowApiEgressName)) assert.Equal("EGRESS", allowApiEgressRule.Get("direction").String(), fmt.Sprintf("firewall rule %s direction should be EGRESS", allowApiEgressName)) - assert.True(allowApiEgressRule.Get("logConfig.enable").Bool(), fmt.Sprintf("firewall rule %s should have log configuration enabled", allowApiEgressName)) - assert.Equal(googleapisCIDR[envName][networkType], allowApiEgressRule.Get("destinationRanges").Array()[0].String(), fmt.Sprintf("firewall rule %s destination ranges should be %s", allowApiEgressName, googleapisCIDR[envName][networkType])) - assert.Equal(1, len(allowApiEgressRule.Get("allowed").Array()), fmt.Sprintf("firewall rule %s should have only one allowed", allowApiEgressName)) - assert.Equal(2, len(allowApiEgressRule.Get("allowed.0").Map()), fmt.Sprintf("firewall rule %s should have only one allowed only with protocol end ports", allowApiEgressName)) - assert.Equal("tcp", allowApiEgressRule.Get("allowed.0.IPProtocol").String(), fmt.Sprintf("firewall rule %s should allow tcp protocol", allowApiEgressName)) - assert.Equal(1, len(allowApiEgressRule.Get("allowed.0.ports").Array()), fmt.Sprintf("firewall rule %s should allow only one port", allowApiEgressName)) - assert.Equal("443", allowApiEgressRule.Get("allowed.0.ports.0").String(), fmt.Sprintf("firewall rule %s should allow port 443", allowApiEgressName)) + assert.Equal("allow", allowApiEgressRule.Get("action").String(), fmt.Sprintf("firewall rule %s action should be allow", allowApiEgressName)) + assert.True(allowApiEgressRule.Get("enableLogging").Bool(), fmt.Sprintf("firewall rule %s should have log configuration enabled", allowApiEgressName)) + assert.Equal(googleapisCIDR[envName][networkType], allowApiEgressRule.Get("match.destIpRanges").Array()[0].String(), fmt.Sprintf("firewall rule %s destination ranges should be %s", allowApiEgressName, googleapisCIDR[envName][networkType])) if networkMode == "" { for _, router := range []struct {