Remove firewall policy management from resource management modules (#1581)

* rename firewall policy module, fix outputs

* add TOC to firewall policy module

* don't depend policy on parent id

* remove firewall policy from resource management modules

* remove factory conditionals

* fast net a and b

* fast stages

* fast tfdoc

* fast tfdoc

* remove unused test

* fix shielded folder blueprint

* fix shielded folder blueprint
This commit is contained in:
Ludovico Magnocavallo
2023-08-09 13:23:07 +02:00
committed by GitHub
parent b7ff8f0933
commit 79373721df
58 changed files with 1040 additions and 1017 deletions

View File

@@ -0,0 +1,252 @@
# Firewall Policies
This module allows creation and management of two different firewall policy types:
- a [hierarchical policy](https://cloud.google.com/firewall/docs/firewall-policies) in a folder or organization, or
- a [global](https://cloud.google.com/vpc/docs/network-firewall-policies) or [regional](https://cloud.google.com/vpc/docs/regional-firewall-policies) network policy
The module also manages policy rules via code or a factory, and optional policy attachments. The interface deviates slightly from the [`net-vpc-firewall`](../net-vpc-firewall/) module since the underlying resources and API objects are different.
The module also makes fewer assumptions about implicit defaults, only using one to set `match.layer4_configs` to `[{ protocol = "all" }]` if no explicit set of protocols and ports has been specified.
<!-- BEGIN TOC -->
- [Examples](#examples)
- [Hierarchical Policy](#hierarchical-policy)
- [Global Network policy](#global-network-policy)
- [Regional Network policy](#regional-network-policy)
- [Factory](#factory)
- [Variables](#variables)
- [Outputs](#outputs)
<!-- END TOC -->
## Examples
### Hierarchical Policy
```hcl
module "firewall-policy" {
source = "./fabric/modules/net-firewall-policy"
name = "test-1"
parent_id = "folders/1234567890"
attachments = {
test = "folders/4567890123"
}
egress_rules = {
smtp = {
priority = 900
match = {
destination_ranges = ["0.0.0.0/0"]
layer4_configs = [{ protocol = "tcp", ports = ["25"] }]
}
}
}
ingress_rules = {
icmp = {
priority = 1000
match = {
source_ranges = ["0.0.0.0/0"]
layer4_configs = [{ protocol = "icmp" }]
}
}
mgmt = {
priority = 1001
match = {
source_ranges = ["10.1.1.0/24"]
}
}
ssh = {
priority = 1002
match = {
source_ranges = ["10.0.0.0/8"]
# source_tags = ["tagValues/123456"]
layer4_configs = [{ protocol = "tcp", ports = ["22"] }]
}
}
}
}
# tftest modules=1 resources=6 inventory=hierarchical.yaml
```
### Global Network policy
```hcl
module "vpc" {
source = "./fabric/modules/net-vpc"
project_id = "my-project"
name = "my-network"
}
module "firewall-policy" {
source = "./fabric/modules/net-firewall-policy"
name = "test-1"
parent_id = "my-project"
region = "global"
attachments = {
my-vpc = module.vpc.self_link
}
egress_rules = {
smtp = {
priority = 900
match = {
destination_ranges = ["0.0.0.0/0"]
layer4_configs = [{ protocol = "tcp", ports = ["25"] }]
}
}
}
ingress_rules = {
icmp = {
priority = 1000
match = {
source_ranges = ["0.0.0.0/0"]
layer4_configs = [{ protocol = "icmp" }]
}
}
mgmt = {
priority = 1001
match = {
source_ranges = ["10.1.1.0/24"]
}
}
ssh = {
priority = 1002
match = {
source_ranges = ["10.0.0.0/8"]
# source_tags = ["tagValues/123456"]
layer4_configs = [{ protocol = "tcp", ports = ["22"] }]
}
}
}
}
# tftest modules=2 resources=9 inventory=global-net.yaml
```
### Regional Network policy
```hcl
module "vpc" {
source = "./fabric/modules/net-vpc"
project_id = "my-project"
name = "my-network"
}
module "firewall-policy" {
source = "./fabric/modules/net-firewall-policy"
name = "test-1"
parent_id = "my-project"
region = "europe-west8"
attachments = {
my-vpc = module.vpc.self_link
}
egress_rules = {
smtp = {
priority = 900
match = {
destination_ranges = ["0.0.0.0/0"]
layer4_configs = [{ protocol = "tcp", ports = ["25"] }]
}
}
}
ingress_rules = {
icmp = {
priority = 1000
match = {
source_ranges = ["0.0.0.0/0"]
layer4_configs = [{ protocol = "icmp" }]
}
}
}
}
# tftest modules=2 resources=7 inventory=regional-net.yaml
```
### Factory
Similarly to other modules, a rules factory (see [Resource Factories](../../blueprints/factories/)) is also included here to allow route management via descriptive configuration files.
Factory configuration is via three optional attributes in the `rules_factory_config` variable:
- `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
Factory rules are merged with rules declared in code, with the latter taking precedence where both use the same key.
This is an example of a simple factory:
```hcl
module "firewall-policy" {
source = "./fabric/modules/net-firewall-policy"
name = "test-1"
parent_id = "folders/1234567890"
attachments = {
test = "folders/4567890123"
}
ingress_rules = {
ssh = {
priority = 1002
match = {
source_ranges = ["10.0.0.0/8"]
layer4_configs = [{ protocol = "tcp", ports = ["22"] }]
}
}
}
rules_factory_config = {
cidr_file_path = "configs/cidrs.yaml"
egress_rules_file_path = "configs/egress.yaml"
ingress_rules_file_path = "configs/ingress.yaml"
}
}
# tftest modules=1 resources=5 files=cidrs,egress,ingress inventory=factory.yaml
```
```yaml
# tftest-file id=cidrs path=configs/cidrs.yaml
rfc1918:
- 10.0.0.0/8
- 172.16.0.0/12
- 192.168.0.0/24
```
```yaml
# tftest-file id=egress path=configs/egress.yaml
smtp:
priority: 900
match:
destination_ranges:
- rfc1918
layer4_configs:
- protocol: tcp
ports:
- 25
```
```yaml
# tftest-file id=ingress path=configs/ingress.yaml
icmp:
priority: 1000
match:
source_ranges:
- 10.0.0.0/8
layer4_configs:
- protocol: icmp
```
<!-- BEGIN TFDOC -->
## Variables
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
| [name](variables.tf#L113) | Policy name. | <code>string</code> | ✓ | |
| [parent_id](variables.tf#L119) | 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> |
| [description](variables.tf#L24) | Policy description. | <code>string</code> | | <code>null</code> |
| [egress_rules](variables.tf#L30) | List of egress rule definitions, action can be 'allow', 'deny', 'goto_next'. 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; target_service_accounts &#61; optional&#40;list&#40;string&#41;&#41;&#10; target_tags &#61; optional&#40;list&#40;string&#41;&#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#L71) | List of ingress rule definitions, action can be 'allow', 'deny', 'goto_next'. | <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; target_service_accounts &#61; optional&#40;list&#40;string&#41;&#41;&#10; target_tags &#61; optional&#40;list&#40;string&#41;&#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#L125) | Policy region. Leave null for hierarchical policy, set to 'global' for a global network policy. | <code>string</code> | | <code>null</code> |
| [rules_factory_config](variables.tf#L131) | Configuration for the optional rules factory. | <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> |
## Outputs
| name | description | sensitive |
|---|---|:---:|
| [id](outputs.tf#L17) | Fully qualified firewall policy id. | |
<!-- END TFDOC -->

View File

@@ -0,0 +1,113 @@
/**
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
locals {
_factory_egress_rules = try(
yamldecode(file(var.rules_factory_config.egress_rules_file_path)), {}
)
_factory_ingress_rules = try(
yamldecode(file(var.rules_factory_config.ingress_rules_file_path)), {}
)
factory_cidrs = try(
yamldecode(file(var.rules_factory_config.cidr_file_path)), {}
)
factory_egress_rules = {
for k, v in local._factory_egress_rules : "ingress/${k}" => {
action = "deny"
direction = "EGRESS"
priority = v.priority
description = lookup(v, "description", null)
disabled = lookup(v, "disabled", false)
enable_logging = lookup(v, "enable_logging", null)
target_service_accounts = lookup(v, "target_service_accounts", null)
target_tags = lookup(v, "target_tags", null)
match = {
address_groups = lookup(v.match, "address_groups", null)
fqdns = lookup(v.match, "fqdns", null)
region_codes = lookup(v.match, "region_codes", null)
threat_intelligences = lookup(v.match, "threat_intelligences", null)
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 = null }, c)
]
)
}
}
}
factory_ingress_rules = {
for k, v in local._factory_ingress_rules : "egress/${k}" => {
action = "allow"
direction = "INGRESS"
priority = v.priority
description = lookup(v, "description", null)
disabled = lookup(v, "disabled", false)
enable_logging = lookup(v, "enable_logging", null)
target_service_accounts = lookup(v, "target_service_accounts", null)
target_tags = lookup(v, "target_tags", null)
match = {
address_groups = lookup(v.match, "address_groups", null)
fqdns = lookup(v.match, "fqdns", null)
region_codes = lookup(v.match, "region_codes", null)
threat_intelligences = lookup(v.match, "threat_intelligences", null)
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 = null }, c)
]
)
}
}
}
}

View File

@@ -0,0 +1,102 @@
/**
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
resource "google_compute_firewall_policy" "hierarchical" {
count = local.use_hierarchical ? 1 : 0
parent = var.parent_id
short_name = var.name
description = var.description
}
resource "google_compute_firewall_policy_association" "hierarchical" {
for_each = local.use_hierarchical ? var.attachments : {}
name = "${var.name}-${each.key}"
attachment_target = each.value
firewall_policy = google_compute_firewall_policy.hierarchical.0.name
}
output "foo" {
value = {
rules = local.rules
cidrs = local.factory_cidrs
}
}
resource "google_compute_firewall_policy_rule" "hierarchical" {
# Terraform's type system barfs in the condition if we use the locals map
for_each = toset(
local.use_hierarchical ? keys(local.rules) : []
)
firewall_policy = google_compute_firewall_policy.hierarchical.0.name
action = local.rules[each.key].action
description = local.rules[each.key].description
direction = local.rules[each.key].direction
disabled = local.rules[each.key].disabled
enable_logging = local.rules[each.key].enable_logging
priority = local.rules[each.key].priority
target_service_accounts = local.rules[each.key].target_service_accounts
match {
dest_ip_ranges = local.rules[each.key].match.destination_ranges
src_ip_ranges = local.rules[each.key].match.source_ranges
dest_address_groups = (
local.rules[each.key].direction == "EGRESS"
? local.rules[each.key].match.address_groups
: null
)
dest_fqdns = (
local.rules[each.key].direction == "EGRESS"
? local.rules[each.key].match.fqdns
: null
)
dest_region_codes = (
local.rules[each.key].direction == "EGRESS"
? local.rules[each.key].match.region_codes
: null
)
dest_threat_intelligences = (
local.rules[each.key].direction == "EGRESS"
? local.rules[each.key].match.threat_intelligences
: null
)
src_address_groups = (
local.rules[each.key].direction == "INGRESS"
? local.rules[each.key].match.address_groups
: null
)
src_fqdns = (
local.rules[each.key].direction == "INGRESS"
? local.rules[each.key].match.fqdns
: null
)
src_region_codes = (
local.rules[each.key].direction == "INGRESS"
? local.rules[each.key].match.region_codes
: null
)
src_threat_intelligences = (
local.rules[each.key].direction == "INGRESS"
? local.rules[each.key].match.threat_intelligences
: null
)
dynamic "layer4_configs" {
for_each = local.rules[each.key].match.layer4_configs
content {
ip_protocol = layer4_configs.value.protocol
ports = layer4_configs.value.ports
}
}
}
}

View File

@@ -0,0 +1,33 @@
/**
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
locals {
_rules_egress = {
for name, rule in merge(var.egress_rules) :
"egress/${name}" => merge(rule, { name = name, direction = "EGRESS" })
}
_rules_ingress = {
for name, rule in merge(var.ingress_rules) :
"ingress/${name}" => merge(rule, { name = name, direction = "INGRESS" })
}
rules = merge(
local.factory_egress_rules, local.factory_ingress_rules,
local._rules_egress, local._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"
}

View File

@@ -0,0 +1,118 @@
/**
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
resource "google_compute_network_firewall_policy" "net-global" {
count = !local.use_hierarchical && !local.use_regional ? 1 : 0
project = var.parent_id
name = var.name
description = var.description
}
resource "google_compute_network_firewall_policy_association" "net-global" {
for_each = (
!local.use_hierarchical && !local.use_regional ? var.attachments : {}
)
project = var.parent_id
name = "${var.name}-${each.key}"
attachment_target = each.value
firewall_policy = google_compute_network_firewall_policy.net-global.0.name
}
resource "google_compute_network_firewall_policy_rule" "net-global" {
# Terraform's type system barfs in the condition if we use the locals map
for_each = toset(
!local.use_hierarchical && !local.use_regional
? keys(local.rules)
: []
)
project = var.parent_id
firewall_policy = google_compute_network_firewall_policy.net-global.0.name
rule_name = local.rules[each.key].name
action = local.rules[each.key].action
description = local.rules[each.key].description
direction = local.rules[each.key].direction
disabled = local.rules[each.key].disabled
enable_logging = local.rules[each.key].enable_logging
priority = local.rules[each.key].priority
target_service_accounts = local.rules[each.key].target_service_accounts
match {
dest_ip_ranges = local.rules[each.key].match.destination_ranges
src_ip_ranges = local.rules[each.key].match.source_ranges
dest_address_groups = (
local.rules[each.key].direction == "EGRESS"
? local.rules[each.key].match.address_groups
: null
)
dest_fqdns = (
local.rules[each.key].direction == "EGRESS"
? local.rules[each.key].match.fqdns
: null
)
dest_region_codes = (
local.rules[each.key].direction == "EGRESS"
? local.rules[each.key].match.region_codes
: null
)
dest_threat_intelligences = (
local.rules[each.key].direction == "EGRESS"
? local.rules[each.key].match.threat_intelligences
: null
)
src_address_groups = (
local.rules[each.key].direction == "INGRESS"
? local.rules[each.key].match.address_groups
: null
)
src_fqdns = (
local.rules[each.key].direction == "INGRESS"
? local.rules[each.key].match.fqdns
: null
)
src_region_codes = (
local.rules[each.key].direction == "INGRESS"
? local.rules[each.key].match.region_codes
: null
)
src_threat_intelligences = (
local.rules[each.key].direction == "INGRESS"
? local.rules[each.key].match.threat_intelligences
: null
)
dynamic "layer4_configs" {
for_each = local.rules[each.key].match.layer4_configs
content {
ip_protocol = layer4_configs.value.protocol
ports = layer4_configs.value.ports
}
}
dynamic "src_secure_tags" {
for_each = toset(coalesce(local.rules[each.key].match.source_tags, []))
content {
name = src_secure_tags.key
}
}
}
dynamic "target_secure_tags" {
for_each = toset(
local.rules[each.key].target_tags == null
? []
: local.rules[each.key].target_tags
)
content {
name = target_secure_tags.value
}
}
}

View File

@@ -0,0 +1,121 @@
/**
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
resource "google_compute_region_network_firewall_policy" "net-regional" {
count = !local.use_hierarchical && local.use_regional ? 1 : 0
project = var.parent_id
name = var.name
description = var.description
region = var.region
}
resource "google_compute_region_network_firewall_policy_association" "net-regional" {
for_each = (
!local.use_hierarchical && local.use_regional ? var.attachments : {}
)
project = var.parent_id
region = var.region
name = "${var.name}-${each.key}"
attachment_target = each.value
firewall_policy = google_compute_region_network_firewall_policy.net-regional.0.name
}
resource "google_compute_region_network_firewall_policy_rule" "net-regional" {
# Terraform's type system barfs in the condition if we use the locals map
for_each = toset(
!local.use_hierarchical && local.use_regional
? keys(local.rules)
: []
)
project = var.parent_id
region = var.region
firewall_policy = google_compute_region_network_firewall_policy.net-regional.0.name
rule_name = local.rules[each.key].name
action = local.rules[each.key].action
description = local.rules[each.key].description
direction = local.rules[each.key].direction
disabled = local.rules[each.key].disabled
enable_logging = local.rules[each.key].enable_logging
priority = local.rules[each.key].priority
target_service_accounts = local.rules[each.key].target_service_accounts
match {
dest_ip_ranges = local.rules[each.key].match.destination_ranges
src_ip_ranges = local.rules[each.key].match.source_ranges
dest_address_groups = (
local.rules[each.key].direction == "EGRESS"
? local.rules[each.key].match.address_groups
: null
)
dest_fqdns = (
local.rules[each.key].direction == "EGRESS"
? local.rules[each.key].match.fqdns
: null
)
dest_region_codes = (
local.rules[each.key].direction == "EGRESS"
? local.rules[each.key].match.region_codes
: null
)
dest_threat_intelligences = (
local.rules[each.key].direction == "EGRESS"
? local.rules[each.key].match.threat_intelligences
: null
)
src_address_groups = (
local.rules[each.key].direction == "INGRESS"
? local.rules[each.key].match.address_groups
: null
)
src_fqdns = (
local.rules[each.key].direction == "INGRESS"
? local.rules[each.key].match.fqdns
: null
)
src_region_codes = (
local.rules[each.key].direction == "INGRESS"
? local.rules[each.key].match.region_codes
: null
)
src_threat_intelligences = (
local.rules[each.key].direction == "INGRESS"
? local.rules[each.key].match.threat_intelligences
: null
)
dynamic "layer4_configs" {
for_each = local.rules[each.key].match.layer4_configs
content {
ip_protocol = layer4_configs.value.protocol
ports = layer4_configs.value.ports
}
}
dynamic "src_secure_tags" {
for_each = toset(coalesce(local.rules[each.key].match.source_tags, []))
content {
name = src_secure_tags.key
}
}
}
dynamic "target_secure_tags" {
for_each = toset(
local.rules[each.key].target_tags == null
? []
: local.rules[each.key].target_tags
)
content {
name = target_secure_tags.value
}
}
}

View File

@@ -0,0 +1,28 @@
/**
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
output "id" {
description = "Fully qualified firewall policy id."
value = (
local.use_hierarchical
? google_compute_firewall_policy.hierarchical.0.id
: (
local.use_regional
? google_compute_region_network_firewall_policy.net-regional.0.id
: google_compute_network_firewall_policy.net-global.0.id
)
)
}

View File

@@ -0,0 +1,140 @@
/**
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
variable "attachments" {
description = "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."
type = map(string)
default = {}
nullable = false
}
variable "description" {
description = "Policy description."
type = string
default = null
}
variable "egress_rules" {
description = "List of egress rule definitions, action can be 'allow', 'deny', 'goto_next'. The match.layer4configs map is in protocol => optional [ports] format."
type = map(object({
priority = number
action = optional(string, "deny")
description = optional(string)
disabled = optional(bool, false)
enable_logging = optional(bool)
target_service_accounts = optional(list(string))
target_tags = optional(list(string))
match = object({
address_groups = optional(list(string))
fqdns = optional(list(string))
region_codes = optional(list(string))
threat_intelligences = optional(list(string))
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_rules : v.match.destination_ranges != null
])
error_message = "Engress rules need destination ranges."
}
validation {
condition = alltrue([
for k, v in var.egress_rules :
contains(["allow", "deny", "goto_next"], v.action)
])
error_message = "Action can only be one of 'allow', 'deny', 'goto_next'."
}
}
variable "ingress_rules" {
description = "List of ingress rule definitions, action can be 'allow', 'deny', 'goto_next'."
type = map(object({
priority = number
action = optional(string, "allow")
description = optional(string)
disabled = optional(bool, false)
enable_logging = optional(bool)
target_service_accounts = optional(list(string))
target_tags = optional(list(string))
match = object({
address_groups = optional(list(string))
fqdns = optional(list(string))
region_codes = optional(list(string))
threat_intelligences = optional(list(string))
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_rules :
v.match.source_ranges != null || v.match.source_tags != null
])
error_message = "Ingress rules need source ranges or tags."
}
validation {
condition = alltrue([
for k, v in var.ingress_rules :
contains(["allow", "deny", "goto_next"], v.action)
])
error_message = "Action can only be one of 'allow', 'deny', 'goto_next'."
}
}
variable "name" {
description = "Policy name."
type = string
nullable = false
}
variable "parent_id" {
description = "Parent node where the policy will be created, `folders/nnn` or `organizations/nnn` for hierarchical policy, project id for a network policy."
type = string
nullable = false
}
variable "region" {
description = "Policy region. Leave null for hierarchical policy, set to 'global' for a global network policy."
type = string
default = null
}
variable "rules_factory_config" {
description = "Configuration for the optional rules factory."
type = object({
cidr_file_path = optional(string)
egress_rules_file_path = optional(string)
ingress_rules_file_path = optional(string)
})
nullable = false
default = {}
}

View File

@@ -0,0 +1,29 @@
# Copyright 2022 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
#
# https://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.
terraform {
required_version = ">= 1.4.4"
required_providers {
google = {
source = "hashicorp/google"
version = ">= 4.76.0" # tftest
}
google-beta = {
source = "hashicorp/google-beta"
version = ">= 4.76.0" # tftest
}
}
}