From f7a8d9773eba141766d6f7e244c86462dbb1f228 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Thu, 10 Oct 2019 06:36:29 +0200 Subject: [PATCH 1/8] first shot at firewall dynamic rules --- modules/fabric-net-firewall/main.tf | 69 ++++++++++++++++++++++++ modules/fabric-net-firewall/variables.tf | 42 +++++++++++++++ 2 files changed, 111 insertions(+) diff --git a/modules/fabric-net-firewall/main.tf b/modules/fabric-net-firewall/main.tf index 0262b4e0..1ccc2a17 100644 --- a/modules/fabric-net-firewall/main.tf +++ b/modules/fabric-net-firewall/main.tf @@ -104,3 +104,72 @@ resource "google_compute_firewall" "allow-tag-https" { ports = ["443"] } } + +################################################################################ +# dynamic rules # +################################################################################ + +resource "google_compute_firewall" "ingress-dynamic" { + # provider = "google-beta" + for_each = var.ingress_rules + name = each.key + description = each.value.description + direction = "INGRESS" + network = var.network + project = var.project_id + source_ranges = each.value.source_ranges + source_tags = each.value.target_type == "service_accounts" ? null : each.value.source_tags + target_tags = each.value.target_type == "tags" ? each.value.target_values : null + target_service_accounts = each.value.target_type == "service_accounts" ? each.value.target_values : null + disabled = lookup(each.value.extra_attributes, "disabled", false) + priority = lookup(each.value.extra_attributes, "priority", 1000) + # enable_logging = lookup(each.value.extra_attributes, "enable_logging", false) + + dynamic "allow" { + for_each = each.value.allow + content { + protocol = allow.value.protocol + ports = allow.value.ports + } + } + + dynamic "deny" { + for_each = each.value.deny + content { + protocol = deny.value.protocol + ports = deny.value.ports + } + } +} + +/* resource "google_compute_firewall" "egress-dynamic" { + for_each = var.ingress_rules + name = each.key + description = each.value.description + network = var.network + project = var.project_id + source_ranges = each.value.source_ranges + target_tags = each.value.target_type == "tags" ? each.value.target_values : null + target_service_accounts = each.value.target_type == "service_accounts" ? each.value.target_values : null + disabled = lookup(each.value.extra_attributes, "disabled", false) + priority = lookup(each.value.extra_attributes, "priority", 1000) + # logging needs the beta provider + # enable_logging = lookup(each.value.extra_attributes, "enable_logging", false) + + dynamic "allow" { + for_each = each.value.allow + content { + protocol = allow.value.protocol + ports = allow.value.ports + } + } + + dynamic "deny" { + for_each = each.value.deny + content { + protocol = deny.value.protocol + ports = deny.value.ports + } + } +} + */ diff --git a/modules/fabric-net-firewall/variables.tf b/modules/fabric-net-firewall/variables.tf index 493ec43b..b8dc1377 100644 --- a/modules/fabric-net-firewall/variables.tf +++ b/modules/fabric-net-firewall/variables.tf @@ -65,3 +65,45 @@ variable "https_source_ranges" { description = "List of IP CIDR ranges for tag-based HTTPS rule, defaults to 0.0.0.0/0." default = ["0.0.0.0/0"] } + +variable "ingress_rules" { + description = "List of ingress rule definitions." + type = map(object({ + description = string + source_ranges = list(string) + source_tags = list(string) + # target is tags or service_accounts + target_type = string + target_values = list(string) + allow = list(object({ + protocol = string + ports = list(string) + })) + deny = list(object({ + protocol = string + ports = list(string) + })) + extra_attributes = map(string) + })) +} + +variable "egress_rules" { + description = "List of egress rule definitions." + type = map(object({ + description = string + source_ranges = list(string) + source_tags = list(string) + # target is tags or service_accounts + target_type = string + target_values = list(string) + allow = list(object({ + protocol = string + ports = list(string) + })) + deny = list(object({ + protocol = string + ports = list(string) + })) + extra_attributes = map(string) + })) +} From 0db0d90be30acff2be9726ff3e16428c5026abae Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Thu, 10 Oct 2019 08:01:42 +0200 Subject: [PATCH 2/8] fabric-firewall: unify resource for ingress and egress rules, streamline source/target definitions --- modules/fabric-net-firewall/.gitignore | 1 + modules/fabric-net-firewall/main.tf | 62 +++++++----------------- modules/fabric-net-firewall/variables.tf | 44 ++++------------- 3 files changed, 28 insertions(+), 79 deletions(-) create mode 100644 modules/fabric-net-firewall/.gitignore diff --git a/modules/fabric-net-firewall/.gitignore b/modules/fabric-net-firewall/.gitignore new file mode 100644 index 00000000..3f5ca68a --- /dev/null +++ b/modules/fabric-net-firewall/.gitignore @@ -0,0 +1 @@ +terraform.tfvars diff --git a/modules/fabric-net-firewall/main.tf b/modules/fabric-net-firewall/main.tf index 1ccc2a17..5677d8be 100644 --- a/modules/fabric-net-firewall/main.tf +++ b/modules/fabric-net-firewall/main.tf @@ -109,67 +109,39 @@ resource "google_compute_firewall" "allow-tag-https" { # dynamic rules # ################################################################################ -resource "google_compute_firewall" "ingress-dynamic" { +resource "google_compute_firewall" "dynamic" { # provider = "google-beta" - for_each = var.ingress_rules + for_each = var.dynamic_rules name = each.key description = each.value.description - direction = "INGRESS" + direction = each.value.direction network = var.network project = var.project_id - source_ranges = each.value.source_ranges - source_tags = each.value.target_type == "service_accounts" ? null : each.value.source_tags - target_tags = each.value.target_type == "tags" ? each.value.target_values : null - target_service_accounts = each.value.target_type == "service_accounts" ? each.value.target_values : null + source_ranges = each.value.direction == "INGRESS" ? each.value.ranges : null + destination_ranges = each.value.direction == "EGRESS" ? each.value.ranges : null + source_tags = each.value.use_service_accounts ? null : each.value.sources + target_tags = each.value.use_service_accounts ? null : each.value.targets + source_service_accounts = each.value.use_service_accounts ? each.value.sources : null + target_service_accounts = each.value.use_service_accounts ? each.value.targets : null disabled = lookup(each.value.extra_attributes, "disabled", false) priority = lookup(each.value.extra_attributes, "priority", 1000) # enable_logging = lookup(each.value.extra_attributes, "enable_logging", false) dynamic "allow" { - for_each = each.value.allow + for_each = [for rule in each.value.rules : rule if each.value.action == "allow"] + iterator = rule content { - protocol = allow.value.protocol - ports = allow.value.ports + protocol = rule.value.protocol + ports = rule.value.ports } } dynamic "deny" { - for_each = each.value.deny + for_each = [for rule in each.value.rules : rule if each.value.action == "deny"] + iterator = rule content { - protocol = deny.value.protocol - ports = deny.value.ports + protocol = rule.value.protocol + ports = rule.value.ports } } } - -/* resource "google_compute_firewall" "egress-dynamic" { - for_each = var.ingress_rules - name = each.key - description = each.value.description - network = var.network - project = var.project_id - source_ranges = each.value.source_ranges - target_tags = each.value.target_type == "tags" ? each.value.target_values : null - target_service_accounts = each.value.target_type == "service_accounts" ? each.value.target_values : null - disabled = lookup(each.value.extra_attributes, "disabled", false) - priority = lookup(each.value.extra_attributes, "priority", 1000) - # logging needs the beta provider - # enable_logging = lookup(each.value.extra_attributes, "enable_logging", false) - - dynamic "allow" { - for_each = each.value.allow - content { - protocol = allow.value.protocol - ports = allow.value.ports - } - } - - dynamic "deny" { - for_each = each.value.deny - content { - protocol = deny.value.protocol - ports = deny.value.ports - } - } -} - */ diff --git a/modules/fabric-net-firewall/variables.tf b/modules/fabric-net-firewall/variables.tf index b8dc1377..060a1389 100644 --- a/modules/fabric-net-firewall/variables.tf +++ b/modules/fabric-net-firewall/variables.tf @@ -66,41 +66,17 @@ variable "https_source_ranges" { default = ["0.0.0.0/0"] } -variable "ingress_rules" { - description = "List of ingress rule definitions." +variable "dynamic_rules" { + description = "List of dynamic rule definitions." type = map(object({ - description = string - source_ranges = list(string) - source_tags = list(string) - # target is tags or service_accounts - target_type = string - target_values = list(string) - allow = list(object({ - protocol = string - ports = list(string) - })) - deny = list(object({ - protocol = string - ports = list(string) - })) - extra_attributes = map(string) - })) -} - -variable "egress_rules" { - description = "List of egress rule definitions." - type = map(object({ - description = string - source_ranges = list(string) - source_tags = list(string) - # target is tags or service_accounts - target_type = string - target_values = list(string) - allow = list(object({ - protocol = string - ports = list(string) - })) - deny = list(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) })) From f4eff271979608cfd9c7d71bcc1639cf94cb2733 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Fri, 11 Oct 2019 07:30:52 +0200 Subject: [PATCH 3/8] rename custom rules variable, fix tag-based egress --- modules/fabric-net-firewall/main.tf | 6 ++--- modules/fabric-net-firewall/outputs.tf | 31 ++++++++++++++++++++++++ modules/fabric-net-firewall/variables.tf | 4 +-- 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/modules/fabric-net-firewall/main.tf b/modules/fabric-net-firewall/main.tf index 5677d8be..fd620241 100644 --- a/modules/fabric-net-firewall/main.tf +++ b/modules/fabric-net-firewall/main.tf @@ -109,9 +109,9 @@ resource "google_compute_firewall" "allow-tag-https" { # dynamic rules # ################################################################################ -resource "google_compute_firewall" "dynamic" { +resource "google_compute_firewall" "custom" { # provider = "google-beta" - for_each = var.dynamic_rules + for_each = var.custom_rules name = each.key description = each.value.description direction = each.value.direction @@ -119,7 +119,7 @@ resource "google_compute_firewall" "dynamic" { project = var.project_id source_ranges = each.value.direction == "INGRESS" ? each.value.ranges : null destination_ranges = each.value.direction == "EGRESS" ? each.value.ranges : null - source_tags = each.value.use_service_accounts ? null : each.value.sources + source_tags = each.value.use_service_accounts || each.value.direction == "EGRESS" ? null : each.value.sources target_tags = each.value.use_service_accounts ? null : each.value.targets source_service_accounts = each.value.use_service_accounts ? each.value.sources : null target_service_accounts = each.value.use_service_accounts ? each.value.targets : null diff --git a/modules/fabric-net-firewall/outputs.tf b/modules/fabric-net-firewall/outputs.tf index 6546c4a9..6a36296f 100644 --- a/modules/fabric-net-firewall/outputs.tf +++ b/modules/fabric-net-firewall/outputs.tf @@ -32,3 +32,34 @@ output "admin_ranges" { } } +output "custom_ingress_allow_rules" { + description = "Custom ingress rules with allow blocks." + value = [ + for rule in google_compute_firewall.custom : + rule.name if rule.direction == "INGRESS" && length(rule.allow) > 0 + ] +} + +output "custom_ingress_deny_rules" { + description = "Custom ingress rules with deny blocks." + value = [ + for rule in google_compute_firewall.custom : + rule.name if rule.direction == "INGRESS" && length(rule.deny) > 0 + ] +} + +output "custom_egress_allow_rules" { + description = "Custom egress rules with allow blocks." + value = [ + for rule in google_compute_firewall.custom : + rule.name if rule.direction == "EGRESS" && length(rule.allow) > 0 + ] +} + +output "custom_egress_deny_rules" { + description = "Custom egress rules with allow blocks." + value = [ + for rule in google_compute_firewall.custom : + rule.name if rule.direction == "EGRESS" && length(rule.deny) > 0 + ] +} diff --git a/modules/fabric-net-firewall/variables.tf b/modules/fabric-net-firewall/variables.tf index 060a1389..c0b0cc39 100644 --- a/modules/fabric-net-firewall/variables.tf +++ b/modules/fabric-net-firewall/variables.tf @@ -66,8 +66,8 @@ variable "https_source_ranges" { default = ["0.0.0.0/0"] } -variable "dynamic_rules" { - description = "List of dynamic rule definitions." +variable "custom_rules" { + description = "List of custom rule definitions." type = map(object({ description = string direction = string From bcd0a0cdbc888a36b78c7199dea3ef1b44f98f4e Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Fri, 11 Oct 2019 07:36:35 +0200 Subject: [PATCH 4/8] fix service-account based egress rules --- modules/fabric-net-firewall/main.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/fabric-net-firewall/main.tf b/modules/fabric-net-firewall/main.tf index fd620241..098dbd15 100644 --- a/modules/fabric-net-firewall/main.tf +++ b/modules/fabric-net-firewall/main.tf @@ -121,7 +121,7 @@ resource "google_compute_firewall" "custom" { destination_ranges = each.value.direction == "EGRESS" ? each.value.ranges : null source_tags = each.value.use_service_accounts || each.value.direction == "EGRESS" ? null : each.value.sources target_tags = each.value.use_service_accounts ? null : each.value.targets - source_service_accounts = each.value.use_service_accounts ? each.value.sources : null + source_service_accounts = each.value.use_service_accounts && each.value.direction == "INGRESS" ? each.value.sources : null target_service_accounts = each.value.use_service_accounts ? each.value.targets : null disabled = lookup(each.value.extra_attributes, "disabled", false) priority = lookup(each.value.extra_attributes, "priority", 1000) From 3d342c588eb9782cd03a7854a61f2fd36e6d2f83 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Fri, 11 Oct 2019 07:43:12 +0200 Subject: [PATCH 5/8] set default for custom rules, update README inputs/outputs table --- modules/fabric-net-firewall/README.md | 1 + modules/fabric-net-firewall/main.tf | 2 +- modules/fabric-net-firewall/variables.tf | 3 ++- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/fabric-net-firewall/README.md b/modules/fabric-net-firewall/README.md index 158b0a70..58319a41 100644 --- a/modules/fabric-net-firewall/README.md +++ b/modules/fabric-net-firewall/README.md @@ -36,6 +36,7 @@ module "net-firewall" { |------|-------------|:----:|:-----:|:-----:| | admin\_ranges | IP CIDR ranges that have complete access to all subnets. | list | `` | no | | admin\_ranges\_enabled | Enable admin ranges-based rules. | string | `"false"` | no | +| custom\_rules | List of custom rule definitions (refer to variables file for syntax). | map | `` | no | | http\_source\_ranges | List of IP CIDR ranges for tag-based HTTP rule, defaults to 0.0.0.0/0. | list | `` | no | | https\_source\_ranges | List of IP CIDR ranges for tag-based HTTPS rule, defaults to 0.0.0.0/0. | list | `` | no | | internal\_allow | Allow rules for internal ranges. | list | `` | no | diff --git a/modules/fabric-net-firewall/main.tf b/modules/fabric-net-firewall/main.tf index 098dbd15..16f15993 100644 --- a/modules/fabric-net-firewall/main.tf +++ b/modules/fabric-net-firewall/main.tf @@ -120,8 +120,8 @@ resource "google_compute_firewall" "custom" { source_ranges = each.value.direction == "INGRESS" ? each.value.ranges : null destination_ranges = each.value.direction == "EGRESS" ? each.value.ranges : null source_tags = each.value.use_service_accounts || each.value.direction == "EGRESS" ? null : each.value.sources - target_tags = each.value.use_service_accounts ? null : each.value.targets source_service_accounts = each.value.use_service_accounts && each.value.direction == "INGRESS" ? each.value.sources : null + target_tags = each.value.use_service_accounts ? null : each.value.targets target_service_accounts = each.value.use_service_accounts ? each.value.targets : null disabled = lookup(each.value.extra_attributes, "disabled", false) priority = lookup(each.value.extra_attributes, "priority", 1000) diff --git a/modules/fabric-net-firewall/variables.tf b/modules/fabric-net-firewall/variables.tf index c0b0cc39..80249cb9 100644 --- a/modules/fabric-net-firewall/variables.tf +++ b/modules/fabric-net-firewall/variables.tf @@ -67,7 +67,8 @@ variable "https_source_ranges" { } variable "custom_rules" { - description = "List of custom rule definitions." + description = "List of custom rule definitions (refer to variables file for syntax)." + default = {} type = map(object({ description = string direction = string From 13cd1858871dcc6ffe5ddde1d7fc4eb6e7c07902 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Fri, 11 Oct 2019 07:53:04 +0200 Subject: [PATCH 6/8] update firewall submodule README --- modules/fabric-net-firewall/README.md | 44 +++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/modules/fabric-net-firewall/README.md b/modules/fabric-net-firewall/README.md index 58319a41..ec6d289d 100644 --- a/modules/fabric-net-firewall/README.md +++ b/modules/fabric-net-firewall/README.md @@ -1,11 +1,30 @@ -# Google Cloud Simple VPC Firewall Creation +# Google Cloud VPC Firewall -This module allows creation of a minimal VPC firewall, supporting basic configurable rules for IP range-based intra-VPC and administrator ingress, and tag-based SSH, HTTP, and HTTPS ingress. +This module allows creation of a minimal VPC firewall, supporting basic configurable rules for IP range-based intra-VPC and administrator ingress, tag-based SSH/HTTP/HTTPS ingress, and custom rule definitions. -The HTTP and HTTPS rules use the same network tags network tags that are assigned to instances when flaggging the "Allow HTTP[S] traffic" checkbox in the Cloud Console. The SSH rule uses a generic `ssh` tag. +The HTTP and HTTPS rules use the same network tags that are assigned to instances when the "Allow HTTP[S] traffic" checkbox is flagged in the Cloud Console. The SSH rule uses a generic `ssh` tag. All IP source ranges are configurable through variables, and are set by default to `0.0.0.0/0` for tag-based rules. Allowed protocols and/or ports for the intra-VPC rule are also configurable through a variable. +Custom rules are set through a map where key is the rule name, and values must match this custom type: + +```hcl +map(object({ + description = string + direction = string # (INGRESS|EGRESS) + action = string # (allow|deny) + ranges = list(string) # list of IP CIDR ranges + sources = list(string) # tags or SAs (ignored for EGRESS) + targets = list(string) # tags or SAs + use_service_accounts = bool # defaults to false + rules = list(object({ + protocol = string + ports = list(string) + })) + extra_attributes = map(string) # map, optional keys disabled or priority +})) +``` + The resources created/managed by this module are: - one optional ingress rule from internal CIDR ranges, only allowing ICMP by default @@ -13,6 +32,7 @@ The resources created/managed by this module are: - one optional ingress rule for SSH on network tag `ssh` - one optional ingress rule for HTTP on network tag `http-server` - one optional ingress rule for HTTPS on network tag `https-server` +- one or more optional custom rules ## Usage @@ -26,6 +46,24 @@ module "net-firewall" { network = "my-vpc" internal_ranges_enabled = true internal_ranges = ["10.0.0.0/0"] + custom_rules = { + ingress-sample = { + description = "Dummy sample ingress rule, tag-based." + direction = "INGRESS" + action = "allow" + ranges = ["192.168.0.0"] + sources = ["spam-tag"] + targets = ["foo-tag", "egg-tag"] + use_service_accounts = false + rules = [ + { + protocol = "tcp" + ports = [] + } + ] + extra_attributes = {} + } + } } ``` From 8ac8dcbf8ceeed82bf5726178c903af450cb3072 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Fri, 11 Oct 2019 07:58:59 +0200 Subject: [PATCH 7/8] minimal README changes --- modules/fabric-net-firewall/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/fabric-net-firewall/README.md b/modules/fabric-net-firewall/README.md index ec6d289d..408f4629 100644 --- a/modules/fabric-net-firewall/README.md +++ b/modules/fabric-net-firewall/README.md @@ -6,7 +6,7 @@ The HTTP and HTTPS rules use the same network tags that are assigned to instance All IP source ranges are configurable through variables, and are set by default to `0.0.0.0/0` for tag-based rules. Allowed protocols and/or ports for the intra-VPC rule are also configurable through a variable. -Custom rules are set through a map where key is the rule name, and values must match this custom type: +Custom rules are set through a map where keys are rule names, and values use this custom type: ```hcl map(object({ From 52e49a9dd593e026de13c94d547dda8f58e3bd0d Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Fri, 11 Oct 2019 08:00:40 +0200 Subject: [PATCH 8/8] minimal README changes --- modules/fabric-net-firewall/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/fabric-net-firewall/README.md b/modules/fabric-net-firewall/README.md index 408f4629..f34555ef 100644 --- a/modules/fabric-net-firewall/README.md +++ b/modules/fabric-net-firewall/README.md @@ -16,7 +16,7 @@ map(object({ ranges = list(string) # list of IP CIDR ranges sources = list(string) # tags or SAs (ignored for EGRESS) targets = list(string) # tags or SAs - use_service_accounts = bool # defaults to false + use_service_accounts = bool # use tags or SAs in sources/targets rules = list(object({ protocol = string ports = list(string)