From b1ff59299018a17320bd5136790d1923462b27a9 Mon Sep 17 00:00:00 2001 From: Simone Ruffilli Date: Tue, 7 Dec 2021 13:26:21 +0100 Subject: [PATCH 1/5] net-vpc-firewall mini rules-factory --- modules/net-vpc-firewall/README.md | 45 +++++++++++++++++++++++++++ modules/net-vpc-firewall/main.tf | 30 +++++++++++++++++- modules/net-vpc-firewall/variables.tf | 13 ++++++++ 3 files changed, 87 insertions(+), 1 deletion(-) diff --git a/modules/net-vpc-firewall/README.md b/modules/net-vpc-firewall/README.md index 83f8f7cd0..a3ad750fa 100644 --- a/modules/net-vpc-firewall/README.md +++ b/modules/net-vpc-firewall/README.md @@ -81,6 +81,49 @@ module "firewall" { # tftest:modules=1:resources=1 ``` + +### Rules Factory +The module includes a rules factory (see [Resource Factories](../../factories/)) for the massive creation of rules leveraging YaML configuration files. Each configuration file can optionally contain more than one rule which a structure that reflects the `custom_rules` variable. + +```hcl +module "firewall" { + source = "./modules/net-vpc-firewall" + project_id = "my-project" + network = "my-network" + data_folder = "config/firewall" + cidr_template_file = "config/cidr_template.yaml" +} +# tftest:skip +``` + +```yaml +# ./config/firewall/load_balancers.yaml +allow-healthchecks: + description: Allow ingress from healthchecks. + direction: INGRESS + action: allow + sources: [] + ranges: + - $healthchecks + targets: ["lb-backends"] + use_service_accounts: false + rules: + - protocol: tcp + ports: + - 80 + - 443 +``` + +```yaml +# ./config/cidr_template.yaml +healthchecks: + - 35.191.0.0/16 + - 130.211.0.0/22 + - 209.85.152.0/22 + - 209.85.204.0/22 + +``` + ## Variables @@ -89,7 +132,9 @@ module "firewall" { | network | Name of the network this set of firewall rules applies to. | string | ✓ | | | project_id | Project id of the project that holds the network. | string | ✓ | | | *admin_ranges* | IP CIDR ranges that have complete access to all subnets. | list(string) | | [] | +| *cidr_template_file* | Path for optional file containing name->cidr_list map to be used by the rules factory. | string | | null | | *custom_rules* | List of custom rule definitions (refer to variables file for syntax). | map(object({...})) | | {} | +| *data_folder* | Path for optional folder containing firewall rules defined as YaML objects used by the rules factory. | string | | null | | *http_source_ranges* | List of IP CIDR ranges for tag-based HTTP rule, defaults to the health checkers ranges. | list(string) | | ["35.191.0.0/16", "130.211.0.0/22", "209.85.152.0/22", "209.85.204.0/22"] | | *https_source_ranges* | List of IP CIDR ranges for tag-based HTTPS rule, defaults to the health checkers ranges. | list(string) | | ["35.191.0.0/16", "130.211.0.0/22", "209.85.152.0/22", "209.85.204.0/22"] | | *named_ranges* | Names that can be used of valid values for the `ranges` field of `custom_rules` | map(list(string)) | | ... | diff --git a/modules/net-vpc-firewall/main.tf b/modules/net-vpc-firewall/main.tf index c196e4705..aac7b929b 100644 --- a/modules/net-vpc-firewall/main.tf +++ b/modules/net-vpc-firewall/main.tf @@ -15,7 +15,7 @@ */ locals { - custom_rules = { + _custom_rules = { for id, rule in var.custom_rules : id => merge(rule, { # make rules a map so we use it in a for_each @@ -27,8 +27,36 @@ locals { ]) }) } + + cidrs = try({ for name, cidrs in yamldecode(file("${var.cidr_template_file}")) : + name => cidrs + }, {}) + + _factory_rules_raw = flatten([ + for file in try(fileset(var.data_folder, "**/*.yaml"), []) : [ + for key, ruleset in yamldecode(file("${var.data_folder}/${file}")) : + merge(ruleset, { + name = "${key}" + rules = { for index, ports in ruleset.rules : index => ports } + ranges = try(ruleset.ranges, null) == null ? null : flatten( + [for cidr in ruleset.ranges : + can(regex("^\\$", cidr)) + ? local.cidrs[trimprefix(cidr, "$")] + : [cidr] + ]) + extra_attributes = try(ruleset.extra_attributes, {}) + }) + ] + ]) + + _factory_rules = { + for d in local._custom_rules_raw : d["name"] => d + } + + custom_rules = merge(local._custom_rules, local._factory_rules) } + ############################################################################### # rules based on IP ranges ############################################################################### diff --git a/modules/net-vpc-firewall/variables.tf b/modules/net-vpc-firewall/variables.tf index 94755d2e0..7a392a6df 100644 --- a/modules/net-vpc-firewall/variables.tf +++ b/modules/net-vpc-firewall/variables.tf @@ -39,6 +39,18 @@ variable "custom_rules" { default = {} } +variable "cidr_template_file" { + description = "Path for optional file containing name->cidr_list map to be used by the rules factory." + type = string + default = null +} + +variable "data_folder" { + description = "Path for optional folder containing firewall rules defined as YaML objects used by the rules factory." + type = string + default = null +} + variable "http_source_ranges" { description = "List of IP CIDR ranges for tag-based HTTP rule, defaults to the health checkers ranges." type = list(string) @@ -80,3 +92,4 @@ variable "ssh_source_ranges" { type = list(string) default = ["35.235.240.0/20"] } + From 5c8557a29d0ed8504c51a076247b08bf709f8b69 Mon Sep 17 00:00:00 2001 From: Simone Ruffilli Date: Tue, 7 Dec 2021 13:29:01 +0100 Subject: [PATCH 2/5] Paying tributes to the linting gods --- modules/net-vpc-firewall/variables.tf | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/net-vpc-firewall/variables.tf b/modules/net-vpc-firewall/variables.tf index 7a392a6df..7682a3f3c 100644 --- a/modules/net-vpc-firewall/variables.tf +++ b/modules/net-vpc-firewall/variables.tf @@ -20,6 +20,12 @@ variable "admin_ranges" { default = [] } +variable "cidr_template_file" { + description = "Path for optional file containing name->cidr_list map to be used by the rules factory." + type = string + default = null +} + variable "custom_rules" { description = "List of custom rule definitions (refer to variables file for syntax)." type = map(object({ @@ -39,12 +45,6 @@ variable "custom_rules" { default = {} } -variable "cidr_template_file" { - description = "Path for optional file containing name->cidr_list map to be used by the rules factory." - type = string - default = null -} - variable "data_folder" { description = "Path for optional folder containing firewall rules defined as YaML objects used by the rules factory." type = string From 83485040678189bc11032c1e1596a358a965b366 Mon Sep 17 00:00:00 2001 From: Simone Ruffilli Date: Tue, 7 Dec 2021 13:40:35 +0100 Subject: [PATCH 3/5] Fixed small bug on locals --- modules/net-vpc-firewall/main.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/net-vpc-firewall/main.tf b/modules/net-vpc-firewall/main.tf index aac7b929b..23dc05883 100644 --- a/modules/net-vpc-firewall/main.tf +++ b/modules/net-vpc-firewall/main.tf @@ -50,7 +50,7 @@ locals { ]) _factory_rules = { - for d in local._custom_rules_raw : d["name"] => d + for d in local._factory_rules_raw : d["name"] => d } custom_rules = merge(local._custom_rules, local._factory_rules) From 28d84c120a9960a40b69f6c4b8d81f503d9a28b1 Mon Sep 17 00:00:00 2001 From: Simone Ruffilli Date: Tue, 7 Dec 2021 14:40:23 +0100 Subject: [PATCH 4/5] Tests for net-vpc-firewall module --- tests/modules/net_vpc_firewall/__init__.py | 13 +++ .../fixture/config/cidr_template.yaml | 19 ++++ .../config/firewall/load_balancers.yaml | 28 ++++++ .../modules/net_vpc_firewall/fixture/main.tf | 28 ++++++ .../net_vpc_firewall/fixture/variables.tf | 97 +++++++++++++++++++ tests/modules/net_vpc_firewall/test_plan.py | 44 +++++++++ 6 files changed, 229 insertions(+) create mode 100644 tests/modules/net_vpc_firewall/__init__.py create mode 100644 tests/modules/net_vpc_firewall/fixture/config/cidr_template.yaml create mode 100644 tests/modules/net_vpc_firewall/fixture/config/firewall/load_balancers.yaml create mode 100644 tests/modules/net_vpc_firewall/fixture/main.tf create mode 100644 tests/modules/net_vpc_firewall/fixture/variables.tf create mode 100644 tests/modules/net_vpc_firewall/test_plan.py diff --git a/tests/modules/net_vpc_firewall/__init__.py b/tests/modules/net_vpc_firewall/__init__.py new file mode 100644 index 000000000..d46dbae5e --- /dev/null +++ b/tests/modules/net_vpc_firewall/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2021 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. diff --git a/tests/modules/net_vpc_firewall/fixture/config/cidr_template.yaml b/tests/modules/net_vpc_firewall/fixture/config/cidr_template.yaml new file mode 100644 index 000000000..b33125de8 --- /dev/null +++ b/tests/modules/net_vpc_firewall/fixture/config/cidr_template.yaml @@ -0,0 +1,19 @@ +# Copyright 2021 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. + +healthchecks: + - 35.191.0.0/16 + - 130.211.0.0/22 + - 209.85.152.0/22 + - 209.85.204.0/22 diff --git a/tests/modules/net_vpc_firewall/fixture/config/firewall/load_balancers.yaml b/tests/modules/net_vpc_firewall/fixture/config/firewall/load_balancers.yaml new file mode 100644 index 000000000..558e65b10 --- /dev/null +++ b/tests/modules/net_vpc_firewall/fixture/config/firewall/load_balancers.yaml @@ -0,0 +1,28 @@ +# Copyright 2021 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. + +allow-healthchecks: + description: Allow ingress from healthchecks. + direction: INGRESS + action: allow + sources: [] + ranges: + - $healthchecks + targets: ["lb-backends"] + use_service_accounts: false + rules: + - protocol: tcp + ports: + - 80 + - 443 diff --git a/tests/modules/net_vpc_firewall/fixture/main.tf b/tests/modules/net_vpc_firewall/fixture/main.tf new file mode 100644 index 000000000..59201cb23 --- /dev/null +++ b/tests/modules/net_vpc_firewall/fixture/main.tf @@ -0,0 +1,28 @@ +/** + * Copyright 2021 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. + */ + +module "firewall" { + source = "../../../../modules/net-vpc-firewall" + project_id = var.project_id + network = var.network + admin_ranges = var.admin_ranges + http_source_ranges = var.http_source_ranges + https_source_ranges = var.https_source_ranges + ssh_source_ranges = var.ssh_source_ranges + custom_rules = var.custom_rules + data_folder = var.data_folder + cidr_template_file = var.cidr_template_file +} diff --git a/tests/modules/net_vpc_firewall/fixture/variables.tf b/tests/modules/net_vpc_firewall/fixture/variables.tf new file mode 100644 index 000000000..7f261a6f3 --- /dev/null +++ b/tests/modules/net_vpc_firewall/fixture/variables.tf @@ -0,0 +1,97 @@ +/** + * Copyright 2021 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 "admin_ranges" { + description = "IP CIDR ranges that have complete access to all subnets." + type = list(string) + default = [] +} + +variable "cidr_template_file" { + description = "Path for optional file containing name->cidr_list map to be used by the rules factory." + type = string + default = null +} + +variable "custom_rules" { + description = "List of custom rule definitions (refer to variables file for syntax)." + type = map(object({ + description = string + direction = string + action = string # (allow|deny) + ranges = list(string) + sources = list(string) + targets = list(string) + use_service_accounts = bool + rules = list(object({ + protocol = string + ports = list(string) + })) + extra_attributes = map(string) + })) + default = {} +} + +variable "data_folder" { + description = "Path for optional folder containing firewall rules defined as YaML objects used by the rules factory." + type = string + default = null +} + +variable "http_source_ranges" { + description = "List of IP CIDR ranges for tag-based HTTP rule, defaults to the health checkers ranges." + type = list(string) + default = ["35.191.0.0/16", "130.211.0.0/22", "209.85.152.0/22", "209.85.204.0/22"] +} + +variable "https_source_ranges" { + description = "List of IP CIDR ranges for tag-based HTTPS rule, defaults to the health checkers ranges." + type = list(string) + default = ["35.191.0.0/16", "130.211.0.0/22", "209.85.152.0/22", "209.85.204.0/22"] +} + +variable "named_ranges" { + description = "Names that can be used of valid values for the `ranges` field of `custom_rules`" + type = map(list(string)) + default = { + any = ["0.0.0.0/0"] + dns-forwarders = ["35.199.192.0/19"] + health-checkers = ["35.191.0.0/16", "130.211.0.0/22", "209.85.152.0/22", "209.85.204.0/22"] + iap-forwarders = ["35.235.240.0/20"] + private-googleapis = ["199.36.153.8/30"] + restricted-googleapis = ["199.36.153.4/30"] + rfc1918 = ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"] + } +} + +variable "network" { + description = "Name of the network this set of firewall rules applies to." + type = string + default = "vpc" +} + +variable "project_id" { + description = "Project id of the project that holds the network." + type = string + default = "project" +} + +variable "ssh_source_ranges" { + description = "List of IP CIDR ranges for tag-based SSH rule, defaults to the IAP forwarders range." + type = list(string) + default = ["35.235.240.0/20"] +} + diff --git a/tests/modules/net_vpc_firewall/test_plan.py b/tests/modules/net_vpc_firewall/test_plan.py new file mode 100644 index 000000000..8159a7fcd --- /dev/null +++ b/tests/modules/net_vpc_firewall/test_plan.py @@ -0,0 +1,44 @@ +# Copyright 2021 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. + + +import os +import pytest + + +FIXTURES_DIR = os.path.join(os.path.dirname(__file__), 'fixture') + + +def test_vpc_firewall_simple(plan_runner): + "Test vpc with no extra options." + _, resources = plan_runner(FIXTURES_DIR) + assert len(resources) == 3 + assert set([r['type'] for r in resources]) == set( + ['google_compute_firewall']) + assert set([r['values']['name'] for r in resources]) == set( + ['vpc-ingress-tag-http', 'vpc-ingress-tag-https', 'vpc-ingress-tag-ssh']) + assert set([r['values']['project'] for r in resources]) == set(['project']) + assert set([r['values']['network'] for r in resources]) == set(['vpc']) + + +def test_vpc_firewall_factory(plan_runner): + "Test shared vpc variables." + _, resources = plan_runner( + FIXTURES_DIR, data_folder="config/firewall", cidr_template_file="config/cidr_template.yaml") + assert len(resources) == 4 + factory_rule = [r for r in resources if r["values"] + ["name"] == "allow-healthchecks"][0]["values"] + assert set(factory_rule["source_ranges"]) == set( + ["130.211.0.0/22", "209.85.152.0/22", "209.85.204.0/22", "35.191.0.0/16"]) + assert set(factory_rule["target_tags"]) == set(["lb-backends"]) From dfe83c2cdfa95246db0c282a9831da71fa7fb15b Mon Sep 17 00:00:00 2001 From: Simone Ruffilli Date: Tue, 7 Dec 2021 17:46:49 +0100 Subject: [PATCH 5/5] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 108b5a993..e0e0181f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ All notable changes to this project will be documented in this file. ## [Unreleased] - new `cloud-run` module - added gVNIC support to `compute-vm` module +- added a rule factory to `net-vpc-firewall` module +- added a subnet factory to `net-vpc` module ## [8.0.0] - 2021-10-21