Skip to content

Commit

Permalink
Allow setting no ranges in firewall module custom rules (#1073)
Browse files Browse the repository at this point in the history
* allow setting no ranges in custom firewall rules

* fix blueprint

* fix example

* fix example
  • Loading branch information
ludoo committed Dec 23, 2022
1 parent 53135cd commit 66a4020
Show file tree
Hide file tree
Showing 18 changed files with 301 additions and 132 deletions.
6 changes: 4 additions & 2 deletions blueprints/data-solutions/sqlserver-alwayson/vpc.tf
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ module "firewall" {
ingress_rules = {
"${var.prefix}-allow-all-between-wsfc-nodes" = {
description = "Allow all between WSFC nodes"
source_ranges = []
sources = [module.compute-service-account.email]
targets = [module.compute-service-account.email]
use_service_accounts = true
Expand All @@ -96,6 +97,7 @@ module "firewall" {
}
"${var.prefix}-allow-all-between-wsfc-witness" = {
description = "Allow all between WSFC witness nodes"
source_ranges = []
sources = [module.compute-service-account.email]
targets = [module.witness-service-account.email]
use_service_accounts = true
Expand All @@ -108,7 +110,7 @@ module "firewall" {
"${var.prefix}-allow-sql-to-wsfc-nodes" = {
description = "Allow SQL connections to WSFC nodes"
targets = [module.compute-service-account.email]
ranges = var.sql_client_cidrs
source_ranges = var.sql_client_cidrs
use_service_accounts = true
rules = [
{ protocol = "tcp", ports = [1433] },
Expand All @@ -117,7 +119,7 @@ module "firewall" {
"${var.prefix}-allow-health-check-to-wsfc-nodes" = {
description = "Allow health checks to WSFC nodes"
targets = [module.compute-service-account.email]
ranges = var.health_check_ranges
source_ranges = var.health_check_ranges
use_service_accounts = true
rules = [
{ protocol = "tcp" }
Expand Down
42 changes: 25 additions & 17 deletions modules/net-vpc-firewall/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Some implicit defaults are used in the rules variable types and can be controlle

- action is controlled via the `deny` attribute which defaults to `true` for egress and `false` for ingress
- priority defaults to `1000`
- destination ranges (for egress) and source ranges (for ingress) default to `["0.0.0.0/0"]` if not explicitly set
- destination ranges (for egress) and source ranges (for ingress) default to `["0.0.0.0/0"]` if not explicitly set or set to `null`, to disable the behaviour set ranges to the empty list (`[]`)
- rules default to all protocols if not set

```hcl
Expand All @@ -45,31 +45,39 @@ module "firewall" {
admin_ranges = ["10.0.0.0/8"]
}
egress_rules = {
# implicit `deny` action
# implicit deny action
allow-egress-rfc1918 = {
deny = false
description = "Allow egress to RFC 1918 ranges."
destination_ranges = [
"10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"
]
# implicit { protocol = "all" } rule
}
allow-egress-tag = {
deny = false
description = "Allow egress from a specific tag to 0/0."
targets = ["target-tag"]
}
deny-egress-all = {
description = "Block egress."
# implicit ["0.0.0.0/0"] destination ranges
# implicit { protocol = "all" } rule
}
}
ingress_rules = {
# implicit `allow` action
# implicit allow action
allow-ingress-ntp = {
description = "Allow NTP service based on tag."
source_ranges = ["0.0.0.0/0"]
targets = ["ntp-svc"]
rules = [{ protocol = "udp", ports = [123] }]
description = "Allow NTP service based on tag."
targets = ["ntp-svc"]
rules = [{ protocol = "udp", ports = [123] }]
}
allow-ingress-tag = {
description = "Allow ingress from a specific tag."
source_ranges = []
sources = ["client-tag"]
targets = ["target-tag"]
}
}
}
# tftest modules=1 resources=7
# tftest modules=1 resources=9
```

### Controlling or turning off default rules
Expand Down Expand Up @@ -194,13 +202,13 @@ healthchecks:

| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
| [network](variables.tf#L109) | Name of the network this set of firewall rules applies to. | <code>string</code> || |
| [project_id](variables.tf#L114) | Project id of the project that holds the network. | <code>string</code> || |
| [network](variables.tf#L108) | Name of the network this set of firewall rules applies to. | <code>string</code> || |
| [project_id](variables.tf#L113) | Project id of the project that holds the network. | <code>string</code> || |
| [default_rules_config](variables.tf#L17) | Optionally created convenience rules. Set the 'disabled' attribute to true, or individual rule attributes to empty lists to disable. | <code title="object&#40;&#123;&#10; admin_ranges &#61; optional&#40;list&#40;string&#41;&#41;&#10; disabled &#61; optional&#40;bool, false&#41;&#10; http_ranges &#61; optional&#40;list&#40;string&#41;, &#91;&#10; &#34;35.191.0.0&#47;16&#34;, &#34;130.211.0.0&#47;22&#34;, &#34;209.85.152.0&#47;22&#34;, &#34;209.85.204.0&#47;22&#34;&#93;&#10; &#41;&#10; http_tags &#61; optional&#40;list&#40;string&#41;, &#91;&#34;http-server&#34;&#93;&#41;&#10; https_ranges &#61; optional&#40;list&#40;string&#41;, &#91;&#10; &#34;35.191.0.0&#47;16&#34;, &#34;130.211.0.0&#47;22&#34;, &#34;209.85.152.0&#47;22&#34;, &#34;209.85.204.0&#47;22&#34;&#93;&#10; &#41;&#10; https_tags &#61; optional&#40;list&#40;string&#41;, &#91;&#34;https-server&#34;&#93;&#41;&#10; ssh_ranges &#61; optional&#40;list&#40;string&#41;, &#91;&#34;35.235.240.0&#47;20&#34;&#93;&#41;&#10; ssh_tags &#61; optional&#40;list&#40;string&#41;, &#91;&#34;ssh&#34;&#93;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
| [egress_rules](variables.tf#L37) | List of egress rule definitions, default to deny action. | <code title="map&#40;object&#40;&#123;&#10; deny &#61; optional&#40;bool, true&#41;&#10; description &#61; optional&#40;string&#41;&#10; destination_ranges &#61; optional&#40;list&#40;string&#41;&#41;&#10; disabled &#61; optional&#40;bool, false&#41;&#10; enable_logging &#61; optional&#40;object&#40;&#123;&#10; include_metadata &#61; optional&#40;bool&#41;&#10; &#125;&#41;&#41;&#10; priority &#61; optional&#40;number, 1000&#41;&#10; sources &#61; optional&#40;list&#40;string&#41;&#41;&#10; targets &#61; optional&#40;list&#40;string&#41;&#41;&#10; use_service_accounts &#61; optional&#40;bool, false&#41;&#10; rules &#61; optional&#40;list&#40;object&#40;&#123;&#10; protocol &#61; string&#10; ports &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;, &#91;&#123; protocol &#61; &#34;all&#34; &#125;&#93;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [factories_config](variables.tf#L60) | Paths to data files and folders that enable factory functionality. | <code title="object&#40;&#123;&#10; cidr_tpl_file &#61; optional&#40;string&#41;&#10; rules_folder &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [ingress_rules](variables.tf#L69) | List of ingress rule definitions, default to allow action. | <code title="map&#40;object&#40;&#123;&#10; deny &#61; optional&#40;bool, false&#41;&#10; description &#61; optional&#40;string&#41;&#10; disabled &#61; optional&#40;bool, false&#41;&#10; enable_logging &#61; optional&#40;object&#40;&#123;&#10; include_metadata &#61; optional&#40;bool&#41;&#10; &#125;&#41;&#41;&#10; priority &#61; optional&#40;number, 1000&#41;&#10; source_ranges &#61; optional&#40;list&#40;string&#41;&#41;&#10; sources &#61; optional&#40;list&#40;string&#41;&#41;&#10; targets &#61; optional&#40;list&#40;string&#41;&#41;&#10; use_service_accounts &#61; optional&#40;bool, false&#41;&#10; rules &#61; optional&#40;list&#40;object&#40;&#123;&#10; protocol &#61; string&#10; ports &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;, &#91;&#123; protocol &#61; &#34;all&#34; &#125;&#93;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [named_ranges](variables.tf#L92) | Define mapping of names to ranges that can be used in custom rules. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code title="&#123;&#10; any &#61; &#91;&#34;0.0.0.0&#47;0&#34;&#93;&#10; dns-forwarders &#61; &#91;&#34;35.199.192.0&#47;19&#34;&#93;&#10; health-checkers &#61; &#91;&#10; &#34;35.191.0.0&#47;16&#34;, &#34;130.211.0.0&#47;22&#34;, &#34;209.85.152.0&#47;22&#34;, &#34;209.85.204.0&#47;22&#34;&#10; &#93;&#10; iap-forwarders &#61; &#91;&#34;35.235.240.0&#47;20&#34;&#93;&#10; private-googleapis &#61; &#91;&#34;199.36.153.8&#47;30&#34;&#93;&#10; restricted-googleapis &#61; &#91;&#34;199.36.153.4&#47;30&#34;&#93;&#10; rfc1918 &#61; &#91;&#34;10.0.0.0&#47;8&#34;, &#34;172.16.0.0&#47;12&#34;, &#34;192.168.0.0&#47;16&#34;&#93;&#10;&#125;">&#123;&#8230;&#125;</code> |
| [egress_rules](variables.tf#L37) | List of egress rule definitions, default to deny action. Null destination ranges will be replaced with 0/0. | <code title="map&#40;object&#40;&#123;&#10; deny &#61; optional&#40;bool, true&#41;&#10; description &#61; optional&#40;string&#41;&#10; destination_ranges &#61; optional&#40;list&#40;string&#41;&#41;&#10; disabled &#61; optional&#40;bool, false&#41;&#10; enable_logging &#61; optional&#40;object&#40;&#123;&#10; include_metadata &#61; optional&#40;bool&#41;&#10; &#125;&#41;&#41;&#10; priority &#61; optional&#40;number, 1000&#41;&#10; targets &#61; optional&#40;list&#40;string&#41;&#41;&#10; use_service_accounts &#61; optional&#40;bool, false&#41;&#10; rules &#61; optional&#40;list&#40;object&#40;&#123;&#10; protocol &#61; string&#10; ports &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;, &#91;&#123; protocol &#61; &#34;all&#34; &#125;&#93;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [factories_config](variables.tf#L59) | Paths to data files and folders that enable factory functionality. | <code title="object&#40;&#123;&#10; cidr_tpl_file &#61; optional&#40;string&#41;&#10; rules_folder &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [ingress_rules](variables.tf#L68) | List of ingress rule definitions, default to allow action. Null source ranges will be replaced with 0/0. | <code title="map&#40;object&#40;&#123;&#10; deny &#61; optional&#40;bool, false&#41;&#10; description &#61; optional&#40;string&#41;&#10; disabled &#61; optional&#40;bool, false&#41;&#10; enable_logging &#61; optional&#40;object&#40;&#123;&#10; include_metadata &#61; optional&#40;bool&#41;&#10; &#125;&#41;&#41;&#10; priority &#61; optional&#40;number, 1000&#41;&#10; source_ranges &#61; optional&#40;list&#40;string&#41;&#41;&#10; sources &#61; optional&#40;list&#40;string&#41;&#41;&#10; targets &#61; optional&#40;list&#40;string&#41;&#41;&#10; use_service_accounts &#61; optional&#40;bool, false&#41;&#10; rules &#61; optional&#40;list&#40;object&#40;&#123;&#10; protocol &#61; string&#10; ports &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;, &#91;&#123; protocol &#61; &#34;all&#34; &#125;&#93;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [named_ranges](variables.tf#L91) | Define mapping of names to ranges that can be used in custom rules. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code title="&#123;&#10; any &#61; &#91;&#34;0.0.0.0&#47;0&#34;&#93;&#10; dns-forwarders &#61; &#91;&#34;35.199.192.0&#47;19&#34;&#93;&#10; health-checkers &#61; &#91;&#10; &#34;35.191.0.0&#47;16&#34;, &#34;130.211.0.0&#47;22&#34;, &#34;209.85.152.0&#47;22&#34;, &#34;209.85.204.0&#47;22&#34;&#10; &#93;&#10; iap-forwarders &#61; &#91;&#34;35.235.240.0&#47;20&#34;&#93;&#10; private-googleapis &#61; &#91;&#34;199.36.153.8&#47;30&#34;&#93;&#10; restricted-googleapis &#61; &#91;&#34;199.36.153.4&#47;30&#34;&#93;&#10; rfc1918 &#61; &#91;&#34;10.0.0.0&#47;8&#34;, &#34;172.16.0.0&#47;12&#34;, &#34;192.168.0.0&#47;16&#34;&#93;&#10;&#125;">&#123;&#8230;&#125;</code> |

## Outputs

Expand Down
34 changes: 22 additions & 12 deletions modules/net-vpc-firewall/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,23 @@ locals {
for name, rule in local._rules :
name => merge(rule, {
action = rule.deny == true ? "DENY" : "ALLOW"
destination_ranges = flatten([
for range in coalesce(try(rule.destination_ranges, null), []) :
try(local._named_ranges[range], range)
])
destination_ranges = (
try(rule.destination_ranges, null) == null
? null
: flatten([
for range in rule.destination_ranges :
try(local._named_ranges[range], range)
])
)
rules = { for k, v in rule.rules : k => v }
source_ranges = flatten([
for range in coalesce(try(rule.source_ranges, null), []) :
try(local._named_ranges[range], range)
])
source_ranges = (
try(rule.source_ranges, null) == null
? null
: flatten([
for range in rule.source_ranges :
try(local._named_ranges[range], range)
])
)
})
}
}
Expand All @@ -89,18 +97,20 @@ resource "google_compute_firewall" "custom-rules" {
source_ranges = (
each.value.direction == "INGRESS"
? (
coalesce(each.value.source_ranges, []) == []
each.value.source_ranges == null
? ["0.0.0.0/0"]
: each.value.source_ranges
) : null
)
: null
)
destination_ranges = (
each.value.direction == "EGRESS"
? (
coalesce(each.value.destination_ranges, []) == []
each.value.destination_ranges == null
? ["0.0.0.0/0"]
: each.value.destination_ranges
) : null
)
: null
)
source_tags = (
each.value.use_service_accounts || each.value.direction == "EGRESS"
Expand Down
5 changes: 2 additions & 3 deletions modules/net-vpc-firewall/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ variable "default_rules_config" {
}

variable "egress_rules" {
description = "List of egress rule definitions, default to deny action."
description = "List of egress rule definitions, default to deny action. Null destination ranges will be replaced with 0/0."
type = map(object({
deny = optional(bool, true)
description = optional(string)
Expand All @@ -45,7 +45,6 @@ variable "egress_rules" {
include_metadata = optional(bool)
}))
priority = optional(number, 1000)
sources = optional(list(string))
targets = optional(list(string))
use_service_accounts = optional(bool, false)
rules = optional(list(object({
Expand All @@ -67,7 +66,7 @@ variable "factories_config" {
}

variable "ingress_rules" {
description = "List of ingress rule definitions, default to allow action."
description = "List of ingress rule definitions, default to allow action. Null source ranges will be replaced with 0/0."
type = map(object({
deny = optional(bool, false)
description = optional(string)
Expand Down
4 changes: 4 additions & 0 deletions tests/modules/net_vpc_firewall/auto-rules.tfvars
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
default_rules_config = {
admin_ranges = ["10.0.0.0/8"]
https_ranges = []
}
44 changes: 44 additions & 0 deletions tests/modules/net_vpc_firewall/auto-rules.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# 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
#
# 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_compute_firewall.allow-admins[0]:
source_ranges:
- 10.0.0.0/8
google_compute_firewall.allow-tag-http[0]:
allow:
- ports:
- "80"
protocol: tcp
source_ranges:
- 130.211.0.0/22
- 209.85.152.0/22
- 209.85.204.0/22
- 35.191.0.0/16
google_compute_firewall.allow-tag-ssh[0]:
allow:
- ports:
- "22"
protocol: tcp
source_ranges:
- 35.235.240.0/20

counts:
google_compute_firewall: 3
modules: 0
resources: 3

outputs:
default_rules: __missing__
rules: {}
2 changes: 2 additions & 0 deletions tests/modules/net_vpc_firewall/common.tfvars
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
project_id = "test-project"
network = "test-network"
33 changes: 33 additions & 0 deletions tests/modules/net_vpc_firewall/custom-rules.tfvars
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
default_rules_config = {
disabled = true
}
egress_rules = {
allow-egress-rfc1918 = {
deny = false
description = "Allow egress to RFC 1918 ranges."
destination_ranges = [
"10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"
]
}
allow-egress-tag = {
deny = false
description = "Allow egress from a specific tag to 0/0."
targets = ["target-tag"]
}
deny-egress-all = {
description = "Block egress."
}
}
ingress_rules = {
allow-ingress-ntp = {
description = "Allow NTP service based on tag."
targets = ["ntp-svc"]
rules = [{ protocol = "udp", ports = [123] }]
}
allow-ingress-tag = {
description = "Allow ingress from a specific tag."
source_ranges = []
sources = ["client-tag"]
targets = ["target-tag"]
}
}
83 changes: 83 additions & 0 deletions tests/modules/net_vpc_firewall/custom-rules.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# 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
#
# 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_compute_firewall.custom-rules["allow-egress-rfc1918"]:
allow:
- ports: []
protocol: all
deny: []
description: Allow egress to RFC 1918 ranges.
destination_ranges:
- 10.0.0.0/8
- 172.16.0.0/12
- 192.168.0.0/16
direction: EGRESS
google_compute_firewall.custom-rules["allow-egress-tag"]:
allow:
- ports: []
protocol: all
deny: []
description: Allow egress from a specific tag to 0/0.
destination_ranges:
- 0.0.0.0/0
direction: EGRESS
target_tags:
- target-tag
google_compute_firewall.custom-rules["allow-ingress-ntp"]:
allow:
- ports:
- "123"
protocol: udp
deny: []
description: Allow NTP service based on tag.
direction: INGRESS
source_ranges:
- 0.0.0.0/0
source_service_accounts: null
source_tags: null
target_tags:
- ntp-svc
google_compute_firewall.custom-rules["allow-ingress-tag"]:
allow:
- ports: []
protocol: all
deny: []
description: Allow ingress from a specific tag.
direction: INGRESS
source_ranges: null
source_tags:
- client-tag
target_tags:
- target-tag
google_compute_firewall.custom-rules["deny-egress-all"]:
allow: []
deny:
- ports: []
protocol: all
description: Block egress.
direction: EGRESS

counts:
google_compute_firewall: 5
modules: 0
resources: 5

outputs:
default_rules:
admin: []
http: []
https: []
ssh: []
rules: __missing__
7 changes: 7 additions & 0 deletions tests/modules/net_vpc_firewall/factory.tfvars
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
default_rules_config = {
disabled = true
}
factories_config = {
cidr_tpl_file = "../../tests/modules/net_vpc_firewall/data/cidr_template.yaml"
rules_folder = "../../tests/modules/net_vpc_firewall/data/firewall"
}

0 comments on commit 66a4020

Please sign in to comment.