From 6febcfe136dc622f616e3bd83471df48217b9533 Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Mon, 12 Jan 2026 12:10:43 +0100 Subject: [PATCH] Add support for mirroring rules to `modules/net-firewall-policy` (#3636) * Add support for mirroring rules to net-firewall-policy * Split mirroring rules * Add schema * Sort variables --- modules/net-firewall-policy/README.md | 149 ++++++++++++++++-- modules/net-firewall-policy/factory.tf | 92 ++++++++++- modules/net-firewall-policy/main.tf | 14 +- modules/net-firewall-policy/net-global.tf | 70 +++++++- ...irewall-policy-mirroring-rules.schema.json | 90 +++++++++++ modules/net-firewall-policy/variables.tf | 72 ++++++++- .../examples/factory-spg.yaml | 73 ++++++++- .../examples/mirroring.yaml | 88 +++++++++++ 8 files changed, 622 insertions(+), 26 deletions(-) create mode 100644 modules/net-firewall-policy/schemas/firewall-policy-mirroring-rules.schema.json create mode 100644 tests/modules/net_firewall_policy/examples/mirroring.yaml diff --git a/modules/net-firewall-policy/README.md b/modules/net-firewall-policy/README.md index 1f1ed7806..4c42937d4 100644 --- a/modules/net-firewall-policy/README.md +++ b/modules/net-firewall-policy/README.md @@ -14,6 +14,8 @@ The module also makes fewer assumptions about implicit defaults, only using one - [Hierarchical Policy](#hierarchical-policy) - [Global Network policy](#global-network-policy) - [Regional Network policy](#regional-network-policy) + - [Packet Mirroring Rules](#packet-mirroring-rules) + - [Packet Mirroring Rules](#packet-mirroring-rules) - [Factory](#factory) - [Firewall Rule Factory Schema](#firewall-rule-factory-schema) - [Dynamic Rule Matching](#dynamic-rule-matching) @@ -168,6 +170,60 @@ module "firewall-policy" { # tftest modules=2 resources=8 inventory=regional-net.yaml ``` +### Packet Mirroring Rules + +### Packet Mirroring Rules +Packet mirroring rules can be defined using the `ingress_mirroring_rules` and `egress_mirroring_rules` variables. This is supported only for Global Network Policies. + +```hcl +resource "google_network_security_security_profile" "default" { + provider = google-beta + name = "sec-profile" + parent = var.organization_id + type = "CUSTOM_MIRRORING" + + custom_mirroring_profile { + mirroring_endpoint_group = "xxx" + } +} + +resource "google_network_security_security_profile_group" "default" { + provider = google-beta + name = "sec-profile-group" + parent = var.organization_id + custom_mirroring_profile = google_network_security_security_profile.default.id +} + + +module "firewall-policy" { + source = "./fabric/modules/net-firewall-policy" + name = "test-mirroring" + parent_id = "my-project" + region = "global" + attachments = { + my-vpc = var.vpc.self_link + } + security_profile_group_ids = { + my-spg = "//networksecurity.googleapis.com/${google_network_security_security_profile_group.default.id}" + } + ingress_mirroring_rules = { + rule-1 = { + priority = 1000 + action = "mirror" + description = "Mirror all traffic" + match = { + source_ranges = ["0.0.0.0/0"] + layer4_configs = [ + { protocol = "tcp", ports = ["80", "443"] } + ] + } + security_profile_group = "//networksecurity.googleapis.com/${google_network_security_security_profile_group.default.id}" + } + } +} +# tftest inventory=mirroring.yaml +``` + ### Factory Similarly to other modules, a rules factory is also included here to allow route management via descriptive configuration files. @@ -177,6 +233,8 @@ Factory configuration is via three optional attributes in the `rules_factory_con - `cidr_file_path` specifying the path to a mapping of logical names to CIDR ranges, used for source and destination ranges in rules when available - `egress_rules_file_path` specifying the path to the egress rules file - `ingress_rules_file_path` specifying the path to the ingress rules file +- `ingress_mirroring_rules_file_path` specifying the path to the mirroring rules file +- `egress_mirroring_rules_file_path` specifying the path to the mirroring rules file Factory rules are merged with rules declared in code, with the latter taking precedence where both use the same key. Also, the factory applies implicit defaults: `action` defaults to `deny` for egress and `allow` for ingress, while omitting `layer4_configs` makes the rule match all protocols. @@ -260,6 +318,35 @@ issue-1995: # tftest-file id=ingress path=configs/ingress.yaml schema=firewall-policy-rules.schema.json ``` +```yaml +icmp: + priority: 1000 + match: + source_ranges: + - 10.0.0.0/8 + layer4_configs: + - protocol: icmp +issue-1995: + priority: 10020 + description: Allow intra-cluster communication required by k8s networking model + enable_logging: true + target_service_accounts: + - sa-gke-cluster@burner-project.iam.gserviceaccount.com + match: + source_ranges: + - gke-nodes-range + layer4_configs: + - protocol: tcp + ports: + - 1-65535 + - protocol: udp + ports: + - 1-65535 + - protocol: icmp +# tftest-file id=ingress path=configs/ingress.yaml schema=firewall-policy-rules.schema.json +``` + + You might need to reference external security profile groups in your firewall rules, using their Terraform ids. For example, `//networksecurity.googleapis.com/${google_network_security_security_profile_group.security_profile_group.id}`. To do so, list your security profile groups in the `security_profile_group_ids` map variable. Then reference them by key from your factories. ```hcl @@ -272,33 +359,54 @@ module "vpc" { resource "google_network_security_security_profile" "security_profile" { name = "security-profile" type = "THREAT_PREVENTION" - parent = "organizations/0123456789" + parent = var.organization_id location = "global" } resource "google_network_security_security_profile_group" "security_profile_group" { name = "security-profile-group" - parent = "organizations/0123456789" + parent = var.organization_id location = "global" description = "Sample security profile group." threat_prevention_profile = google_network_security_security_profile.security_profile.id } +resource "google_network_security_security_profile" "mirror_profile" { + provider = google-beta + name = "sec-profile" + parent = var.organization_id + type = "CUSTOM_MIRRORING" + + custom_mirroring_profile { + mirroring_endpoint_group = "xxx" + } +} + +resource "google_network_security_security_profile_group" "mirror_profile_group" { + provider = google-beta + name = "sec-profile-group" + parent = var.organization_id + custom_mirroring_profile = google_network_security_security_profile.mirror_profile.id +} + + module "firewall-policy" { source = "./fabric/modules/net-firewall-policy" name = "fw-policy" parent_id = "my-project" security_profile_group_ids = { - http-sg = "//networksecurity.googleapis.com/${google_network_security_security_profile_group.security_profile_group.id}" + http-sg = "//networksecurity.googleapis.com/${google_network_security_security_profile_group.security_profile_group.id}" + mirror-sg = "//networksecurity.googleapis.com/${google_network_security_security_profile_group.mirror_profile_group.id}" } attachments = { my-vpc = module.vpc.self_link } factories_config = { - ingress_rules_file_path = "configs/ingress-spg.yaml" + ingress_rules_file_path = "configs/ingress-spg.yaml" + ingress_mirroring_rules_file_path = "configs/mirror-spg.yaml" } } -# tftest modules=2 resources=9 files=ingress-spg inventory=factory-spg.yaml +# tftest modules=2 resources=11 files=ingress-spg,mirror-spg inventory=factory-spg.yaml ``` ```yaml @@ -316,6 +424,21 @@ http: - 80 ``` +```yaml +# tftest-file id=mirror-spg path=configs/mirror-spg.yaml schema=firewall-policy-mirroring-rules.schema.json +mirror-ssh: + priority: 1000 + action: mirror + security_profile_group: mirror-sg + match: + source_ranges: + - 10.0.0.0/8 + layer4_configs: + - protocol: tcp + ports: + - 22 +``` + #### Firewall Rule Factory Schema The following schema outlines all available fields for defining a rule within a factory YAML file. Use this as a reference, and note the inline comments for fields that apply only to specific policy types. @@ -386,16 +509,18 @@ The following variable is defined at the top level of the rule (not within the ` | name | description | type | required | default | |---|---|:---:|:---:|:---:| -| [name](variables.tf#L133) | Policy name. | string | ✓ | | -| [parent_id](variables.tf#L139) | Parent node where the policy will be created, `folders/nnn` or `organizations/nnn` for hierarchical policy, project id for a network policy. | string | ✓ | | +| [name](variables.tf#L197) | Policy name. | string | ✓ | | +| [parent_id](variables.tf#L203) | Parent node where the policy will be created, `folders/nnn` or `organizations/nnn` for hierarchical policy, project id for a network policy. | string | ✓ | | | [attachments](variables.tf#L17) | Ids of the resources to which this policy will be attached, in descriptive name => self link format. Specify folders or organization for hierarchical policy, VPCs for network policy. | map(string) | | {} | | [context](variables.tf#L24) | Context-specific interpolations. | object({…}) | | {} | | [description](variables.tf#L40) | Policy description. | string | | null | -| [egress_rules](variables.tf#L46) | List of egress rule definitions, action can be 'allow', 'deny', 'goto_next' or 'apply_security_profile_group'. The match.layer4configs map is in protocol => optional [ports] format. | map(object({…})) | | {} | -| [factories_config](variables.tf#L84) | Paths to folders for the optional factories. | object({…}) | | {} | -| [ingress_rules](variables.tf#L95) | List of ingress rule definitions, action can be 'allow', 'deny', 'goto_next' or 'apply_security_profile_group'. | map(object({…})) | | {} | -| [region](variables.tf#L145) | Policy region. Leave null for hierarchical policy, set to 'global' for a global network policy. | string | | null | -| [security_profile_group_ids](variables.tf#L151) | The optional security groups ids to be referenced in factories. | map(string) | | {} | +| [egress_mirroring_rules](variables.tf#L46) | List of egress packet mirroring rule definitions, action can be 'mirror', 'do_not_mirror', or 'goto_next'. | map(object({…})) | | {} | +| [egress_rules](variables.tf#L77) | List of egress rule definitions, action can be 'allow', 'deny', 'goto_next' or 'apply_security_profile_group'. The match.layer4configs map is in protocol => optional [ports] format. | map(object({…})) | | {} | +| [factories_config](variables.tf#L115) | Paths to folders for the optional factories. | object({…}) | | {} | +| [ingress_mirroring_rules](variables.tf#L128) | List of ingress packet mirroring rule definitions, action can be 'mirror', 'do_not_mirror', or 'goto_next'. | map(object({…})) | | {} | +| [ingress_rules](variables.tf#L159) | List of ingress rule definitions, action can be 'allow', 'deny', 'goto_next' or 'apply_security_profile_group'. | map(object({…})) | | {} | +| [region](variables.tf#L209) | Policy region. Leave null for hierarchical policy, set to 'global' for a global network policy. | string | | null | +| [security_profile_group_ids](variables.tf#L215) | The optional security groups ids to be referenced in factories. | map(string) | | {} | ## Outputs diff --git a/modules/net-firewall-policy/factory.tf b/modules/net-firewall-policy/factory.tf index 6cc15a5d1..62e4d3890 100644 --- a/modules/net-firewall-policy/factory.tf +++ b/modules/net-firewall-policy/factory.tf @@ -1,5 +1,5 @@ /** - * Copyright 2024 Google LLC + * Copyright 2026 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,6 +25,16 @@ locals { yamldecode(file(pathexpand(var.factories_config.ingress_rules_file_path))), {}), tomap({}) ) + _factory_mirroring_rules_egress = coalesce( + try( + yamldecode(file(pathexpand(var.factories_config.egress_mirroring_rules_file_path))), + {}), tomap({}) + ) + _factory_mirroring_rules_ingress = coalesce( + try( + yamldecode(file(pathexpand(var.factories_config.ingress_mirroring_rules_file_path))), + {}), tomap({}) + ) factory_cidrs = coalesce( try( yamldecode(file(pathexpand(var.factories_config.cidr_file_path))), @@ -124,4 +134,84 @@ locals { } } } + factory_mirroring_ingress_rules = { + for k, v in local._factory_mirroring_rules_ingress : "mirror/ingress/${k}" => { + direction = "INGRESS" + name = k + priority = v.priority + action = lookup(v, "action", "mirror") + description = lookup(v, "description", null) + disabled = lookup(v, "disabled", false) + security_profile_group = lookup(v, "security_profile_group", null) + target_tags = lookup(v, "target_tags", null) + tls_inspect = lookup(v, "tls_inspect", null) + match = { + destination_ranges = ( + lookup(v.match, "destination_ranges", null) == null + ? null + : flatten([ + for r in v.match.destination_ranges : + try(local.factory_cidrs[r], r) + ]) + ) + source_ranges = ( + lookup(v.match, "source_ranges", null) == null + ? null + : flatten([ + for r in v.match.source_ranges : + try(local.factory_cidrs[r], r) + ]) + ) + source_tags = lookup(v.match, "source_tags", null) + layer4_configs = ( + lookup(v.match, "layer4_configs", null) == null + ? [{ protocol = "all", ports = null }] + : [ + for c in v.match.layer4_configs : + merge({ protocol = "all", ports = [] }, c) + ] + ) + } + } + } + factory_mirroring_egress_rules = { + for k, v in local._factory_mirroring_rules_egress : "mirror/egress/${k}" => { + direction = "EGRESS" + name = k + priority = v.priority + action = lookup(v, "action", "mirror") + description = lookup(v, "description", null) + disabled = lookup(v, "disabled", false) + security_profile_group = lookup(v, "security_profile_group", null) + target_tags = lookup(v, "target_tags", null) + tls_inspect = lookup(v, "tls_inspect", null) + match = { + destination_ranges = ( + lookup(v.match, "destination_ranges", null) == null + ? null + : flatten([ + for r in v.match.destination_ranges : + try(local.factory_cidrs[r], r) + ]) + ) + source_ranges = ( + lookup(v.match, "source_ranges", null) == null + ? null + : flatten([ + for r in v.match.source_ranges : + try(local.factory_cidrs[r], r) + ]) + ) + source_tags = lookup(v.match, "source_tags", null) + layer4_configs = ( + lookup(v.match, "layer4_configs", null) == null + ? [{ protocol = "all", ports = null }] + : [ + for c in v.match.layer4_configs : + merge({ protocol = "all", ports = [] }, c) + ] + ) + } + } + } } diff --git a/modules/net-firewall-policy/main.tf b/modules/net-firewall-policy/main.tf index b59a92126..d53892da3 100644 --- a/modules/net-firewall-policy/main.tf +++ b/modules/net-firewall-policy/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2023 Google LLC + * Copyright 2026 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,6 +23,14 @@ locals { for name, rule in merge(var.ingress_rules) : "ingress/${name}" => merge(rule, { name = name, direction = "INGRESS" }) } + _mirroring_rules_egress = { + for name, rule in merge(var.egress_mirroring_rules) : + "mirror/egress/${name}" => merge(rule, { name = name, direction = "EGRESS" }) + } + _mirroring_rules_ingress = { + for name, rule in merge(var.ingress_mirroring_rules) : + "mirror/ingress/${name}" => merge(rule, { name = name, direction = "INGRESS" }) + } ctx = { for k, v in var.context : k => { for kk, vv in v : "${local.ctx_p}${k}:${kk}" => vv @@ -33,6 +41,10 @@ locals { local.factory_egress_rules, local.factory_ingress_rules, local._rules_egress, local._rules_ingress ) + mirroring_rules = merge( + local.factory_mirroring_egress_rules, local.factory_mirroring_ingress_rules, + local._mirroring_rules_egress, local._mirroring_rules_ingress + ) # do not depend on the parent id as that might be dynamic and prevent count use_hierarchical = var.region == null use_regional = !local.use_hierarchical && var.region != "global" diff --git a/modules/net-firewall-policy/net-global.tf b/modules/net-firewall-policy/net-global.tf index a36865a1b..bda47e2d2 100644 --- a/modules/net-firewall-policy/net-global.tf +++ b/modules/net-firewall-policy/net-global.tf @@ -1,5 +1,5 @@ /** - * Copyright 2024 Google LLC + * Copyright 2026 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -146,3 +146,71 @@ resource "google_compute_network_firewall_policy_rule" "net-global" { } } } + +resource "google_compute_network_firewall_policy_packet_mirroring_rule" "net-global" { + provider = google-beta + for_each = toset( + !local.use_hierarchical && !local.use_regional + ? keys(local.mirroring_rules) + : [] + ) + project = lookup(local.ctx.project_ids, var.parent_id, var.parent_id) + firewall_policy = google_compute_network_firewall_policy.net-global[0].name + rule_name = local.mirroring_rules[each.key].name + action = local.mirroring_rules[each.key].action + description = local.mirroring_rules[each.key].description + direction = local.mirroring_rules[each.key].direction + disabled = local.mirroring_rules[each.key].disabled + priority = local.mirroring_rules[each.key].priority + tls_inspect = local.mirroring_rules[each.key].tls_inspect + + security_profile_group = try( + var.security_profile_group_ids[local.mirroring_rules[each.key].security_profile_group], + local.mirroring_rules[each.key].security_profile_group + ) + + match { + dest_ip_ranges = ( + local.mirroring_rules[each.key].match.destination_ranges == null + ? null + : distinct(flatten([ + for r in local.mirroring_rules[each.key].match.destination_ranges : try( + local.ctx.cidr_ranges_sets[r], + local.ctx.cidr_ranges[r], + r + ) + ])) + ) + src_ip_ranges = ( + local.mirroring_rules[each.key].match.source_ranges == null + ? null + : distinct(flatten([ + for r in local.mirroring_rules[each.key].match.source_ranges : try( + local.ctx.cidr_ranges_sets[r], + local.ctx.cidr_ranges[r], + r + ) + ])) + ) + dynamic "layer4_configs" { + for_each = local.mirroring_rules[each.key].match.layer4_configs + content { + ip_protocol = layer4_configs.value.protocol + ports = layer4_configs.value.ports + } + } + + } + dynamic "target_secure_tags" { + for_each = toset( + local.mirroring_rules[each.key].target_tags == null + ? [] + : local.mirroring_rules[each.key].target_tags + ) + content { + name = lookup( + local.ctx.tag_values, target_secure_tags.value, target_secure_tags.value + ) + } + } +} diff --git a/modules/net-firewall-policy/schemas/firewall-policy-mirroring-rules.schema.json b/modules/net-firewall-policy/schemas/firewall-policy-mirroring-rules.schema.json new file mode 100644 index 000000000..20d2870c4 --- /dev/null +++ b/modules/net-firewall-policy/schemas/firewall-policy-mirroring-rules.schema.json @@ -0,0 +1,90 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Firewall Policy Mirroring Rules", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^[a-z0-9_-]+$": { + "$ref": "#/$defs/rule" + } + }, + "$defs": { + "rule": { + "type": "object", + "additionalProperties": false, + "required": [ + "priority" + ], + "properties": { + "priority": { + "type": "number" + }, + "action": { + "type": "string", + "enum": [ + "mirror", + "do_not_mirror", + "goto_next" + ] + }, + "description": { + "type": "string" + }, + "disabled": { + "type": "boolean" + }, + "security_profile_group": { + "type": "string" + }, + "target_tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "tls_inspect": { + "type": "boolean" + }, + "match": { + "type": "object", + "additionalProperties": false, + "properties": { + "destination_ranges": { + "type": "array", + "items": { + "type": "string" + } + }, + "source_ranges": { + "type": "array", + "items": { + "type": "string" + } + }, + "source_tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "layer4_configs": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "protocol": { + "type": "string" + }, + "ports": { + "type": "array" + } + } + } + } + } + } + } + } + } +} diff --git a/modules/net-firewall-policy/variables.tf b/modules/net-firewall-policy/variables.tf index 64a35a2d4..153615f1b 100644 --- a/modules/net-firewall-policy/variables.tf +++ b/modules/net-firewall-policy/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2024 Google LLC + * Copyright 2026 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -43,6 +43,37 @@ variable "description" { default = null } +variable "egress_mirroring_rules" { + description = "List of egress packet mirroring rule definitions, action can be 'mirror', 'do_not_mirror', or 'goto_next'." + type = map(object({ + priority = number + action = optional(string, "mirror") + description = optional(string) + disabled = optional(bool, false) + security_profile_group = optional(string) + target_tags = optional(list(string)) + tls_inspect = optional(bool, null) + match = object({ + destination_ranges = optional(list(string)) + source_ranges = optional(list(string)) + source_tags = optional(list(string)) + layer4_configs = optional(list(object({ + protocol = optional(string, "all") + ports = optional(list(string)) + })), [{}]) + }) + })) + default = {} + nullable = false + validation { + condition = alltrue([ + for k, v in var.egress_mirroring_rules : + contains(["mirror", "do_not_mirror", "goto_next"], v.action) + ]) + error_message = "Action can only be one of 'mirror', 'do_not_mirror' or 'goto_next'." + } +} + variable "egress_rules" { description = "List of egress rule definitions, action can be 'allow', 'deny', 'goto_next' or 'apply_security_profile_group'. The match.layer4configs map is in protocol => optional [ports] format." type = map(object({ @@ -84,14 +115,47 @@ variable "egress_rules" { variable "factories_config" { description = "Paths to folders for the optional factories." type = object({ - cidr_file_path = optional(string) - egress_rules_file_path = optional(string) - ingress_rules_file_path = optional(string) + cidr_file_path = optional(string) + egress_rules_file_path = optional(string) + ingress_rules_file_path = optional(string) + ingress_mirroring_rules_file_path = optional(string) + egress_mirroring_rules_file_path = optional(string) }) nullable = false default = {} } +variable "ingress_mirroring_rules" { + description = "List of ingress packet mirroring rule definitions, action can be 'mirror', 'do_not_mirror', or 'goto_next'." + type = map(object({ + priority = number + action = optional(string, "mirror") + description = optional(string) + disabled = optional(bool, false) + security_profile_group = optional(string) + target_tags = optional(list(string)) + tls_inspect = optional(bool, null) + match = object({ + destination_ranges = optional(list(string)) + source_ranges = optional(list(string)) + source_tags = optional(list(string)) + layer4_configs = optional(list(object({ + protocol = optional(string, "all") + ports = optional(list(string)) + })), [{}]) + }) + })) + default = {} + nullable = false + validation { + condition = alltrue([ + for k, v in var.ingress_mirroring_rules : + contains(["mirror", "do_not_mirror", "goto_next"], v.action) + ]) + error_message = "Action can only be one of 'mirror', 'do_not_mirror' or 'goto_next'." + } +} + variable "ingress_rules" { description = "List of ingress rule definitions, action can be 'allow', 'deny', 'goto_next' or 'apply_security_profile_group'." type = map(object({ diff --git a/tests/modules/net_firewall_policy/examples/factory-spg.yaml b/tests/modules/net_firewall_policy/examples/factory-spg.yaml index 42b55a4fd..22f86eb88 100644 --- a/tests/modules/net_firewall_policy/examples/factory-spg.yaml +++ b/tests/modules/net_firewall_policy/examples/factory-spg.yaml @@ -1,4 +1,4 @@ -# Copyright 2024 Google LLC +# Copyright 2026 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -13,26 +13,74 @@ # limitations under the License. values: - google_network_security_security_profile.security_profile: + google_network_security_security_profile.mirror_profile: + custom_intercept_profile: [] + custom_mirroring_profile: + - mirroring_deployment_groups: null + mirroring_endpoint_group: xxx description: null + effective_labels: + goog-terraform-provisioned: 'true' + labels: null + location: global + name: sec-profile + parent: organizations/1122334455 + terraform_labels: + goog-terraform-provisioned: 'true' + threat_prevention_profile: [] + timeouts: null + type: CUSTOM_MIRRORING + url_filtering_profile: [] + google_network_security_security_profile.security_profile: + custom_intercept_profile: [] + custom_mirroring_profile: [] + description: null + effective_labels: + goog-terraform-provisioned: 'true' labels: null location: global name: security-profile - parent: organizations/0123456789 + parent: organizations/1122334455 + terraform_labels: + goog-terraform-provisioned: 'true' threat_prevention_profile: [] + timeouts: null type: THREAT_PREVENTION + google_network_security_security_profile_group.mirror_profile_group: + custom_intercept_profile: null + description: null + effective_labels: + goog-terraform-provisioned: 'true' + labels: null + location: global + name: sec-profile-group + parent: organizations/1122334455 + terraform_labels: + goog-terraform-provisioned: 'true' + threat_prevention_profile: null + timeouts: null + url_filtering_profile: null google_network_security_security_profile_group.security_profile_group: + custom_intercept_profile: null + custom_mirroring_profile: null description: Sample security profile group. + effective_labels: + goog-terraform-provisioned: 'true' labels: null location: global name: security-profile-group - parent: organizations/0123456789 + parent: organizations/1122334455 + terraform_labels: + goog-terraform-provisioned: 'true' + timeouts: null module.firewall-policy.google_compute_firewall_policy.hierarchical[0]: description: null parent: my-project short_name: fw-policy + timeouts: null module.firewall-policy.google_compute_firewall_policy_association.hierarchical["my-vpc"]: name: fw-policy-my-vpc + timeouts: null module.firewall-policy.google_compute_firewall_policy_rule.hierarchical["ingress/http"]: action: apply_security_profile_group description: null @@ -54,20 +102,27 @@ values: src_ip_ranges: - 10.0.0.0/8 src_region_codes: null + src_secure_tags: [] src_threat_intelligences: null priority: 1000 target_resources: null + target_secure_tags: [] target_service_accounts: null + timeouts: null tls_inspect: null module.vpc.google_compute_network.network[0]: auto_create_subnetworks: false + delete_bgp_always_compare_med: false delete_default_routes_on_create: false description: Terraform-managed. enable_ula_internal_ipv6: null name: my-network network_firewall_policy_enforcement_order: AFTER_CLASSIC_FIREWALL + network_profile: null + params: [] project: my-project routing_mode: GLOBAL + timeouts: null module.vpc.google_compute_route.gateway["private-googleapis"]: description: Terraform-managed. dest_range: 199.36.153.8/30 @@ -77,9 +132,11 @@ values: next_hop_ilb: null next_hop_instance: null next_hop_vpn_tunnel: null + params: [] priority: 1000 project: my-project tags: null + timeouts: null module.vpc.google_compute_route.gateway["restricted-googleapis"]: description: Terraform-managed. dest_range: 199.36.153.4/30 @@ -89,9 +146,11 @@ values: next_hop_ilb: null next_hop_instance: null next_hop_vpn_tunnel: null + params: [] priority: 1000 project: my-project tags: null + timeouts: null counts: google_compute_firewall_policy: 1 @@ -99,7 +158,7 @@ counts: google_compute_firewall_policy_rule: 1 google_compute_network: 1 google_compute_route: 3 - google_network_security_security_profile: 1 - google_network_security_security_profile_group: 1 + google_network_security_security_profile: 2 + google_network_security_security_profile_group: 2 modules: 2 - resources: 9 + resources: 11 diff --git a/tests/modules/net_firewall_policy/examples/mirroring.yaml b/tests/modules/net_firewall_policy/examples/mirroring.yaml new file mode 100644 index 000000000..96b9d4fcc --- /dev/null +++ b/tests/modules/net_firewall_policy/examples/mirroring.yaml @@ -0,0 +1,88 @@ +# Copyright 2026 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: + google_network_security_security_profile.default: + custom_intercept_profile: [] + custom_mirroring_profile: + - mirroring_deployment_groups: null + mirroring_endpoint_group: xxx + description: null + effective_labels: + goog-terraform-provisioned: 'true' + labels: null + location: global + name: sec-profile + parent: organizations/1122334455 + terraform_labels: + goog-terraform-provisioned: 'true' + threat_prevention_profile: [] + timeouts: null + type: CUSTOM_MIRRORING + url_filtering_profile: [] + google_network_security_security_profile_group.default: + custom_intercept_profile: null + description: null + effective_labels: + goog-terraform-provisioned: 'true' + labels: null + location: global + name: sec-profile-group + parent: organizations/1122334455 + terraform_labels: + goog-terraform-provisioned: 'true' + threat_prevention_profile: null + timeouts: null + url_filtering_profile: null + module.firewall-policy.google_compute_network_firewall_policy.net-global[0]: + description: null + name: test-mirroring + project: my-project + timeouts: null + module.firewall-policy.google_compute_network_firewall_policy_association.net-global["my-vpc"]: + attachment_target: https://www.googleapis.com/compute/v1/projects/xxx/global/networks/aaa + firewall_policy: test-mirroring + name: test-mirroring-my-vpc + project: my-project + timeouts: null + module.firewall-policy.google_compute_network_firewall_policy_packet_mirroring_rule.net-global["mirror/ingress/rule-1"]: + action: mirror + description: Mirror all traffic + direction: INGRESS + disabled: false + firewall_policy: test-mirroring + match: + - dest_ip_ranges: null + layer4_configs: + - ip_protocol: tcp + ports: + - '80' + - '443' + src_ip_ranges: + - 0.0.0.0/0 + priority: 1000 + project: my-project + rule_name: rule-1 + target_secure_tags: [] + timeouts: null + tls_inspect: null + +counts: + google_compute_network_firewall_policy: 1 + google_compute_network_firewall_policy_association: 1 + google_compute_network_firewall_policy_packet_mirroring_rule: 1 + google_network_security_security_profile: 1 + google_network_security_security_profile_group: 1 + modules: 1 + resources: 5