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
This commit is contained in:
Julio Castillo
2026-01-12 12:10:43 +01:00
committed by GitHub
parent c1248d328a
commit 6febcfe136
8 changed files with 622 additions and 26 deletions

View File

@@ -14,6 +14,8 @@ The module also makes fewer assumptions about implicit defaults, only using one
- [Hierarchical Policy](#hierarchical-policy) - [Hierarchical Policy](#hierarchical-policy)
- [Global Network policy](#global-network-policy) - [Global Network policy](#global-network-policy)
- [Regional Network policy](#regional-network-policy) - [Regional Network policy](#regional-network-policy)
- [Packet Mirroring Rules](#packet-mirroring-rules)
- [Packet Mirroring Rules](#packet-mirroring-rules)
- [Factory](#factory) - [Factory](#factory)
- [Firewall Rule Factory Schema](#firewall-rule-factory-schema) - [Firewall Rule Factory Schema](#firewall-rule-factory-schema)
- [Dynamic Rule Matching](#dynamic-rule-matching) - [Dynamic Rule Matching](#dynamic-rule-matching)
@@ -168,6 +170,60 @@ module "firewall-policy" {
# tftest modules=2 resources=8 inventory=regional-net.yaml # 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 ### Factory
Similarly to other modules, a rules factory is also included here to allow route management via descriptive configuration files. 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 - `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 - `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_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. 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. 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 # 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. 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 ```hcl
@@ -272,33 +359,54 @@ module "vpc" {
resource "google_network_security_security_profile" "security_profile" { resource "google_network_security_security_profile" "security_profile" {
name = "security-profile" name = "security-profile"
type = "THREAT_PREVENTION" type = "THREAT_PREVENTION"
parent = "organizations/0123456789" parent = var.organization_id
location = "global" location = "global"
} }
resource "google_network_security_security_profile_group" "security_profile_group" { resource "google_network_security_security_profile_group" "security_profile_group" {
name = "security-profile-group" name = "security-profile-group"
parent = "organizations/0123456789" parent = var.organization_id
location = "global" location = "global"
description = "Sample security profile group." description = "Sample security profile group."
threat_prevention_profile = google_network_security_security_profile.security_profile.id 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" { module "firewall-policy" {
source = "./fabric/modules/net-firewall-policy" source = "./fabric/modules/net-firewall-policy"
name = "fw-policy" name = "fw-policy"
parent_id = "my-project" parent_id = "my-project"
security_profile_group_ids = { 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 = { attachments = {
my-vpc = module.vpc.self_link my-vpc = module.vpc.self_link
} }
factories_config = { 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 ```yaml
@@ -316,6 +424,21 @@ http:
- 80 - 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 #### 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. 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 | description | type | required | default |
|---|---|:---:|:---:|:---:| |---|---|:---:|:---:|:---:|
| [name](variables.tf#L133) | Policy name. | <code>string</code> | ✓ | | | [name](variables.tf#L197) | Policy name. | <code>string</code> | ✓ | |
| [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. | <code>string</code> | ✓ | | | [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. | <code>string</code> | ✓ | |
| [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. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> | | [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. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> |
| [context](variables.tf#L24) | Context-specific interpolations. | <code title="object&#40;&#123;&#10; cidr_ranges &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; cidr_ranges_sets &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; folder_ids &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; iam_principals &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; locations &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; networks &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; project_ids &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; tag_values &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | | [context](variables.tf#L24) | Context-specific interpolations. | <code title="object&#40;&#123;&#10; cidr_ranges &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; cidr_ranges_sets &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; folder_ids &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; iam_principals &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; locations &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; networks &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; project_ids &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; tag_values &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
| [description](variables.tf#L40) | Policy description. | <code>string</code> | | <code>null</code> | | [description](variables.tf#L40) | Policy description. | <code>string</code> | | <code>null</code> |
| [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. | <code title="map&#40;object&#40;&#123;&#10; priority &#61; number&#10; action &#61; optional&#40;string, &#34;deny&#34;&#41;&#10; description &#61; optional&#40;string&#41;&#10; disabled &#61; optional&#40;bool, false&#41;&#10; enable_logging &#61; optional&#40;bool&#41;&#10; security_profile_group &#61; optional&#40;string&#41;&#10; target_resources &#61; optional&#40;list&#40;string&#41;&#41;&#10; target_service_accounts &#61; optional&#40;list&#40;string&#41;&#41;&#10; target_tags &#61; optional&#40;list&#40;string&#41;&#41;&#10; tls_inspect &#61; optional&#40;bool, null&#41;&#10; match &#61; object&#40;&#123;&#10; address_groups &#61; optional&#40;list&#40;string&#41;&#41;&#10; fqdns &#61; optional&#40;list&#40;string&#41;&#41;&#10; region_codes &#61; optional&#40;list&#40;string&#41;&#41;&#10; threat_intelligences &#61; optional&#40;list&#40;string&#41;&#41;&#10; destination_ranges &#61; optional&#40;list&#40;string&#41;&#41;&#10; source_ranges &#61; optional&#40;list&#40;string&#41;&#41;&#10; source_tags &#61; optional&#40;list&#40;string&#41;&#41;&#10; layer4_configs &#61; optional&#40;list&#40;object&#40;&#123;&#10; protocol &#61; optional&#40;string, &#34;all&#34;&#41;&#10; ports &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;, &#91;&#123;&#125;&#93;&#41;&#10; &#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [egress_mirroring_rules](variables.tf#L46) | List of egress packet mirroring rule definitions, action can be 'mirror', 'do_not_mirror', or 'goto_next'. | <code title="map&#40;object&#40;&#123;&#10; priority &#61; number&#10; action &#61; optional&#40;string, &#34;mirror&#34;&#41;&#10; description &#61; optional&#40;string&#41;&#10; disabled &#61; optional&#40;bool, false&#41;&#10; security_profile_group &#61; optional&#40;string&#41;&#10; target_tags &#61; optional&#40;list&#40;string&#41;&#41;&#10; tls_inspect &#61; optional&#40;bool, null&#41;&#10; match &#61; object&#40;&#123;&#10; destination_ranges &#61; optional&#40;list&#40;string&#41;&#41;&#10; source_ranges &#61; optional&#40;list&#40;string&#41;&#41;&#10; source_tags &#61; optional&#40;list&#40;string&#41;&#41;&#10; layer4_configs &#61; optional&#40;list&#40;object&#40;&#123;&#10; protocol &#61; optional&#40;string, &#34;all&#34;&#41;&#10; ports &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;, &#91;&#123;&#125;&#93;&#41;&#10; &#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [factories_config](variables.tf#L84) | Paths to folders for the optional factories. | <code title="object&#40;&#123;&#10; cidr_file_path &#61; optional&#40;string&#41;&#10; egress_rules_file_path &#61; optional&#40;string&#41;&#10; ingress_rules_file_path &#61; optional&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | | [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. | <code title="map&#40;object&#40;&#123;&#10; priority &#61; number&#10; action &#61; optional&#40;string, &#34;deny&#34;&#41;&#10; description &#61; optional&#40;string&#41;&#10; disabled &#61; optional&#40;bool, false&#41;&#10; enable_logging &#61; optional&#40;bool&#41;&#10; security_profile_group &#61; optional&#40;string&#41;&#10; target_resources &#61; optional&#40;list&#40;string&#41;&#41;&#10; target_service_accounts &#61; optional&#40;list&#40;string&#41;&#41;&#10; target_tags &#61; optional&#40;list&#40;string&#41;&#41;&#10; tls_inspect &#61; optional&#40;bool, null&#41;&#10; match &#61; object&#40;&#123;&#10; address_groups &#61; optional&#40;list&#40;string&#41;&#41;&#10; fqdns &#61; optional&#40;list&#40;string&#41;&#41;&#10; region_codes &#61; optional&#40;list&#40;string&#41;&#41;&#10; threat_intelligences &#61; optional&#40;list&#40;string&#41;&#41;&#10; destination_ranges &#61; optional&#40;list&#40;string&#41;&#41;&#10; source_ranges &#61; optional&#40;list&#40;string&#41;&#41;&#10; source_tags &#61; optional&#40;list&#40;string&#41;&#41;&#10; layer4_configs &#61; optional&#40;list&#40;object&#40;&#123;&#10; protocol &#61; optional&#40;string, &#34;all&#34;&#41;&#10; ports &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;, &#91;&#123;&#125;&#93;&#41;&#10; &#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [ingress_rules](variables.tf#L95) | List of ingress rule definitions, action can be 'allow', 'deny', 'goto_next' or 'apply_security_profile_group'. | <code title="map&#40;object&#40;&#123;&#10; priority &#61; number&#10; action &#61; optional&#40;string, &#34;allow&#34;&#41;&#10; description &#61; optional&#40;string&#41;&#10; disabled &#61; optional&#40;bool, false&#41;&#10; enable_logging &#61; optional&#40;bool&#41;&#10; security_profile_group &#61; optional&#40;string&#41;&#10; target_resources &#61; optional&#40;list&#40;string&#41;&#41;&#10; target_service_accounts &#61; optional&#40;list&#40;string&#41;&#41;&#10; target_tags &#61; optional&#40;list&#40;string&#41;&#41;&#10; tls_inspect &#61; optional&#40;bool, null&#41;&#10; match &#61; object&#40;&#123;&#10; address_groups &#61; optional&#40;list&#40;string&#41;&#41;&#10; fqdns &#61; optional&#40;list&#40;string&#41;&#41;&#10; region_codes &#61; optional&#40;list&#40;string&#41;&#41;&#10; threat_intelligences &#61; optional&#40;list&#40;string&#41;&#41;&#10; destination_ranges &#61; optional&#40;list&#40;string&#41;&#41;&#10; source_ranges &#61; optional&#40;list&#40;string&#41;&#41;&#10; source_tags &#61; optional&#40;list&#40;string&#41;&#41;&#10; layer4_configs &#61; optional&#40;list&#40;object&#40;&#123;&#10; protocol &#61; optional&#40;string, &#34;all&#34;&#41;&#10; ports &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;, &#91;&#123;&#125;&#93;&#41;&#10; &#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [factories_config](variables.tf#L115) | Paths to folders for the optional factories. | <code title="object&#40;&#123;&#10; cidr_file_path &#61; optional&#40;string&#41;&#10; egress_rules_file_path &#61; optional&#40;string&#41;&#10; ingress_rules_file_path &#61; optional&#40;string&#41;&#10; ingress_mirroring_rules_file_path &#61; optional&#40;string&#41;&#10; egress_mirroring_rules_file_path &#61; optional&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
| [region](variables.tf#L145) | Policy region. Leave null for hierarchical policy, set to 'global' for a global network policy. | <code>string</code> | | <code>null</code> | | [ingress_mirroring_rules](variables.tf#L128) | List of ingress packet mirroring rule definitions, action can be 'mirror', 'do_not_mirror', or 'goto_next'. | <code title="map&#40;object&#40;&#123;&#10; priority &#61; number&#10; action &#61; optional&#40;string, &#34;mirror&#34;&#41;&#10; description &#61; optional&#40;string&#41;&#10; disabled &#61; optional&#40;bool, false&#41;&#10; security_profile_group &#61; optional&#40;string&#41;&#10; target_tags &#61; optional&#40;list&#40;string&#41;&#41;&#10; tls_inspect &#61; optional&#40;bool, null&#41;&#10; match &#61; object&#40;&#123;&#10; destination_ranges &#61; optional&#40;list&#40;string&#41;&#41;&#10; source_ranges &#61; optional&#40;list&#40;string&#41;&#41;&#10; source_tags &#61; optional&#40;list&#40;string&#41;&#41;&#10; layer4_configs &#61; optional&#40;list&#40;object&#40;&#123;&#10; protocol &#61; optional&#40;string, &#34;all&#34;&#41;&#10; ports &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;, &#91;&#123;&#125;&#93;&#41;&#10; &#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [security_profile_group_ids](variables.tf#L151) | The optional security groups ids to be referenced in factories. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> | | [ingress_rules](variables.tf#L159) | List of ingress rule definitions, action can be 'allow', 'deny', 'goto_next' or 'apply_security_profile_group'. | <code title="map&#40;object&#40;&#123;&#10; priority &#61; number&#10; action &#61; optional&#40;string, &#34;allow&#34;&#41;&#10; description &#61; optional&#40;string&#41;&#10; disabled &#61; optional&#40;bool, false&#41;&#10; enable_logging &#61; optional&#40;bool&#41;&#10; security_profile_group &#61; optional&#40;string&#41;&#10; target_resources &#61; optional&#40;list&#40;string&#41;&#41;&#10; target_service_accounts &#61; optional&#40;list&#40;string&#41;&#41;&#10; target_tags &#61; optional&#40;list&#40;string&#41;&#41;&#10; tls_inspect &#61; optional&#40;bool, null&#41;&#10; match &#61; object&#40;&#123;&#10; address_groups &#61; optional&#40;list&#40;string&#41;&#41;&#10; fqdns &#61; optional&#40;list&#40;string&#41;&#41;&#10; region_codes &#61; optional&#40;list&#40;string&#41;&#41;&#10; threat_intelligences &#61; optional&#40;list&#40;string&#41;&#41;&#10; destination_ranges &#61; optional&#40;list&#40;string&#41;&#41;&#10; source_ranges &#61; optional&#40;list&#40;string&#41;&#41;&#10; source_tags &#61; optional&#40;list&#40;string&#41;&#41;&#10; layer4_configs &#61; optional&#40;list&#40;object&#40;&#123;&#10; protocol &#61; optional&#40;string, &#34;all&#34;&#41;&#10; ports &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;, &#91;&#123;&#125;&#93;&#41;&#10; &#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [region](variables.tf#L209) | Policy region. Leave null for hierarchical policy, set to 'global' for a global network policy. | <code>string</code> | | <code>null</code> |
| [security_profile_group_ids](variables.tf#L215) | The optional security groups ids to be referenced in factories. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> |
## Outputs ## Outputs

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright 2024 Google LLC * Copyright 2026 Google LLC
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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))), yamldecode(file(pathexpand(var.factories_config.ingress_rules_file_path))),
{}), tomap({}) {}), 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( factory_cidrs = coalesce(
try( try(
yamldecode(file(pathexpand(var.factories_config.cidr_file_path))), 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)
]
)
}
}
}
} }

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright 2023 Google LLC * Copyright 2026 Google LLC
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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) : for name, rule in merge(var.ingress_rules) :
"ingress/${name}" => merge(rule, { name = name, direction = "INGRESS" }) "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 = { ctx = {
for k, v in var.context : k => { for k, v in var.context : k => {
for kk, vv in v : "${local.ctx_p}${k}:${kk}" => vv 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.factory_egress_rules, local.factory_ingress_rules,
local._rules_egress, local._rules_ingress 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 # do not depend on the parent id as that might be dynamic and prevent count
use_hierarchical = var.region == null use_hierarchical = var.region == null
use_regional = !local.use_hierarchical && var.region != "global" use_regional = !local.use_hierarchical && var.region != "global"

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright 2024 Google LLC * Copyright 2026 Google LLC
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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
)
}
}
}

View File

@@ -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"
}
}
}
}
}
}
}
}
}
}

View File

@@ -1,5 +1,5 @@
/** /**
* Copyright 2024 Google LLC * Copyright 2026 Google LLC
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -43,6 +43,37 @@ variable "description" {
default = null 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" { 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." 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({ type = map(object({
@@ -84,14 +115,47 @@ variable "egress_rules" {
variable "factories_config" { variable "factories_config" {
description = "Paths to folders for the optional factories." description = "Paths to folders for the optional factories."
type = object({ type = object({
cidr_file_path = optional(string) cidr_file_path = optional(string)
egress_rules_file_path = optional(string) egress_rules_file_path = optional(string)
ingress_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 nullable = false
default = {} 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" { variable "ingress_rules" {
description = "List of ingress rule definitions, action can be 'allow', 'deny', 'goto_next' or 'apply_security_profile_group'." description = "List of ingress rule definitions, action can be 'allow', 'deny', 'goto_next' or 'apply_security_profile_group'."
type = map(object({ type = map(object({

View File

@@ -1,4 +1,4 @@
# Copyright 2024 Google LLC # Copyright 2026 Google LLC
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@@ -13,26 +13,74 @@
# limitations under the License. # limitations under the License.
values: 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 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 labels: null
location: global location: global
name: security-profile name: security-profile
parent: organizations/0123456789 parent: organizations/1122334455
terraform_labels:
goog-terraform-provisioned: 'true'
threat_prevention_profile: [] threat_prevention_profile: []
timeouts: null
type: THREAT_PREVENTION 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: google_network_security_security_profile_group.security_profile_group:
custom_intercept_profile: null
custom_mirroring_profile: null
description: Sample security profile group. description: Sample security profile group.
effective_labels:
goog-terraform-provisioned: 'true'
labels: null labels: null
location: global location: global
name: security-profile-group 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]: module.firewall-policy.google_compute_firewall_policy.hierarchical[0]:
description: null description: null
parent: my-project parent: my-project
short_name: fw-policy short_name: fw-policy
timeouts: null
module.firewall-policy.google_compute_firewall_policy_association.hierarchical["my-vpc"]: module.firewall-policy.google_compute_firewall_policy_association.hierarchical["my-vpc"]:
name: fw-policy-my-vpc name: fw-policy-my-vpc
timeouts: null
module.firewall-policy.google_compute_firewall_policy_rule.hierarchical["ingress/http"]: module.firewall-policy.google_compute_firewall_policy_rule.hierarchical["ingress/http"]:
action: apply_security_profile_group action: apply_security_profile_group
description: null description: null
@@ -54,20 +102,27 @@ values:
src_ip_ranges: src_ip_ranges:
- 10.0.0.0/8 - 10.0.0.0/8
src_region_codes: null src_region_codes: null
src_secure_tags: []
src_threat_intelligences: null src_threat_intelligences: null
priority: 1000 priority: 1000
target_resources: null target_resources: null
target_secure_tags: []
target_service_accounts: null target_service_accounts: null
timeouts: null
tls_inspect: null tls_inspect: null
module.vpc.google_compute_network.network[0]: module.vpc.google_compute_network.network[0]:
auto_create_subnetworks: false auto_create_subnetworks: false
delete_bgp_always_compare_med: false
delete_default_routes_on_create: false delete_default_routes_on_create: false
description: Terraform-managed. description: Terraform-managed.
enable_ula_internal_ipv6: null enable_ula_internal_ipv6: null
name: my-network name: my-network
network_firewall_policy_enforcement_order: AFTER_CLASSIC_FIREWALL network_firewall_policy_enforcement_order: AFTER_CLASSIC_FIREWALL
network_profile: null
params: []
project: my-project project: my-project
routing_mode: GLOBAL routing_mode: GLOBAL
timeouts: null
module.vpc.google_compute_route.gateway["private-googleapis"]: module.vpc.google_compute_route.gateway["private-googleapis"]:
description: Terraform-managed. description: Terraform-managed.
dest_range: 199.36.153.8/30 dest_range: 199.36.153.8/30
@@ -77,9 +132,11 @@ values:
next_hop_ilb: null next_hop_ilb: null
next_hop_instance: null next_hop_instance: null
next_hop_vpn_tunnel: null next_hop_vpn_tunnel: null
params: []
priority: 1000 priority: 1000
project: my-project project: my-project
tags: null tags: null
timeouts: null
module.vpc.google_compute_route.gateway["restricted-googleapis"]: module.vpc.google_compute_route.gateway["restricted-googleapis"]:
description: Terraform-managed. description: Terraform-managed.
dest_range: 199.36.153.4/30 dest_range: 199.36.153.4/30
@@ -89,9 +146,11 @@ values:
next_hop_ilb: null next_hop_ilb: null
next_hop_instance: null next_hop_instance: null
next_hop_vpn_tunnel: null next_hop_vpn_tunnel: null
params: []
priority: 1000 priority: 1000
project: my-project project: my-project
tags: null tags: null
timeouts: null
counts: counts:
google_compute_firewall_policy: 1 google_compute_firewall_policy: 1
@@ -99,7 +158,7 @@ counts:
google_compute_firewall_policy_rule: 1 google_compute_firewall_policy_rule: 1
google_compute_network: 1 google_compute_network: 1
google_compute_route: 3 google_compute_route: 3
google_network_security_security_profile: 1 google_network_security_security_profile: 2
google_network_security_security_profile_group: 1 google_network_security_security_profile_group: 2
modules: 2 modules: 2
resources: 9 resources: 11

View File

@@ -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