Simplify Google Cloud firewall delegation with Terraform

Jayan Menon
3 min readDec 18, 2020

Came across a somewhat unique use case at a client..

Security team wants the developers to be able to define their own firewall rules within the development VPC. They will be assigned a set of priority numbers which is preassigned for their set of rules. Blanket restrictions at higher priorities and inherited firewall rules restrict the developers from opening up full access to their environment. However, they don’t want to provide developers with access to the Shared VPC host project in order to create these rules.

Following are basic assumptions for this sample scenario :

  • All the rules will be for Ingress access to a development network.
  • All the rules are based on source and target service accounts, or source IP ranges.
  • Pipeline policy checks (not included here) validates the rules for conditions like 0.0.0.0/0 being used as the source range.
  • Rule priorities are predefined with a range based on project.

Enter Terraform..

The key purpose of this design is to allow the “developer” to create firewall rules relevant to their project without interacting with the UI. This is achieved by using a JSON formatted file that includes all the firewall rules. The file will be parsed by a Terraform script that generates a plan that contains each rule as a separate resource.

For the example scenario below, only two kinds of firewall rules are included for simplicity.

  • Rules with the source and targets defined by two service accounts
  • Rules with the source as an IP range and target as a service account.

The Terraform code will include a single resource definition,that decides the type of rule based on which of the fields are available in the JSON file.

Terraform will iterate through the rules in the JSON file using a for_each loop and create or update all the rules defined in the file. Any rule that was created using this set of source files can also be deleted by removing them from the rules file. Terraform will synchronize the environment with the existing state file.

vpc_main_fw_rules.json

{
"rules": {
"rule1": {
"protocol": "tcp",
"ports": "1433",
"tgtsvcs": "dbsvc@myproject.iam.googleapis.com",
"srcsvcs": "websvc@myproject.iam.googleapis.com"
},
"rule2": {
"protocol": "tcp",
"ports": "1514,1515",
"tgtsvcs": "websvc@myproject.iam.googleapis.com",
"srcsvcs": "appsvc@myproject.iam.googleapis.com"
}
"rule3": {
"protocol": "tcp",
"ports": "80,443",
"tgtsvcs": "mywebsvc@myproject.iam.googleapis.com",
"srcranges": "10.0.0.0/8,172.16.0.0/12"
},
"rule4": {
"protocol": "udp",
"ports": "53,389,636",
"tgtsvcs": "domain@myproject.iam.googleapis.com",
"srcranges": "10.0.0.0/8"
},
"rule5": {
"protocol": "tcp",
"ports": "53,389,636",
"tgtsvcs": "domain@myproject.iam.googleapis.com",
"srcranges": "10.0.0.0/8"
}
}
}

Following is sample Terraform code that parses the JSON and creates the resources:

variables.tf

variable "rules_file" {
description = "File name for the rules file"
default = "vpc_main_fw_rules.json"
}
variable "app_project" {
description = "Project that the rule is meant for"
}
variable "vpc_name" {
description = "VPC Name"
}
variable "priority" {
description = "Rule priority"
}
variable "svpc_project" {
description = "Project where VPC is located (Shared VPC Host)"
}

main.tf

locals {
rules_list = jsondecode(file(var.rules_file))
}
resource "google_compute_firewall" "vpc_main_fw" {project = var.svpc_project
network = var.vpc_name
priority = var.priority
direction = "INGRESS"
for_each = local.rules_list.rules
name = "${var.app_project}-allow-${each.key}"
allow {
protocol = each.value.protocol
ports = split(",", each.value.ports)
}
source_ranges = contains(keys(each.value), "srcranges") ? split(",", each.value.srcranges) : null
source_service_accounts = contains(keys(each.value), "srcsvcs") ? split(",", each.value.srcsvcs) : null
target_service_accounts = split(",", each.value.tgtsvcs)
}

Now, this scenario allows traffic from IP ranges/service accounts to target service accounts only. The decision on which field is populated for the source of the rule is done by checking the available keys in the JSON map.

The code may be enhanced to use additional validation checks to make sure the required fields are available in each rule in the JSON file, and expand to use the other firewall rule scenarios that use network tags or DENY rules instead of ALLOW.

You can also get more granular with priority values by including it as a field in the JSON file. It might be required to do that once you include DENY rules as the priority will then matter in the way a rule is evaluated.

So there you go..

Delegated firewall rule management for GCP Shared VPCs. Although, I would really plan on a policy engine and additional design validations before adding something like this to a production network, just sayin..

Add your comments/suggestions below. Thanks for reading.

--

--

Jayan Menon

Cloud Architect at Maven Wave Partners — designing Enterprise solutions for GCP, AWS, Azure. LinkedIn: https://www.linkedin.com/in/jmoolayil/