# Google Cloud VPC Firewall This module allows creation and management of different types of firewall rules for a single VPC network: - custom rules via the `egress_rules` and `ingress_rules` variables - optional predefined rules that simplify prototyping via the `default_rules_config` variable The predefined rules are enabled by default and set to the ranges of the GCP health checkers for HTTP/HTTPS, and the IAP forwarders for SSH. See the relevant section below on how to configure or disable them. - [Examples](#examples) - [Minimal open firewall](#minimal-open-firewall) - [Custom rules](#custom-rules) - [Controlling or turning off default rules](#controlling-or-turning-off-default-rules) - [Overriding default tags and ranges](#overriding-default-tags-and-ranges) - [Disabling predefined rules](#disabling-predefined-rules) - [Including source & destination ranges](#including-source-destination-ranges) - [Rules Factory](#rules-factory) - [Variables](#variables) - [Outputs](#outputs) ## Examples ### Minimal open firewall This is often useful for prototyping or testing infrastructure, allowing open ingress from the private range, enabling SSH to private addresses from IAP, and HTTP/HTTPS from the health checkers. ```hcl module "firewall" { source = "./fabric/modules/net-vpc-firewall" project_id = var.project_id network = var.vpc.name default_rules_config = { admin_ranges = ["10.0.0.0/8"] } } # tftest modules=1 resources=4 inventory=basic.yaml e2e ``` ### Custom rules This is an example of how to define custom rules, with a sample rule allowing open ingress for the NTP protocol to instances with the `ntp-svc` tag. Some implicit defaults are used in the rules variable types and can be controlled by explicitly setting specific attributes: - 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 or set to `null`, to disable the behaviour set ranges to the empty list (`[]`) - rules default to all protocols if not set ```hcl module "firewall" { source = "./fabric/modules/net-vpc-firewall" project_id = var.project_id network = var.vpc.name default_rules_config = { admin_ranges = ["10.0.0.0/8"] } egress_rules = { # 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" ] } 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 = { # implicit allow action 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"] } } } # tftest modules=1 resources=9 inventory=custom-rules.yaml e2e ``` ### Controlling or turning off default rules Predefined rules can be controlled or turned off via the `default_rules_config` variable. #### Overriding default tags and ranges Each protocol rule has a default set of tags and ranges: - the health check range and the `http-server`/`https-server` tag for HTTP/HTTPS, matching tags set via GCP console flags on GCE instances - the IAP forwarders range and `ssh` tag for SSH Default tags and ranges can be overridden for each protocol, like shown here for SSH: ```hcl module "firewall" { source = "./fabric/modules/net-vpc-firewall" project_id = var.project_id network = var.vpc.name default_rules_config = { ssh_ranges = ["10.0.0.0/8"] ssh_tags = ["ssh-default"] } } # tftest modules=1 resources=3 inventory=custom-ssh-default-rule.yaml e2e ``` #### Disabling predefined rules Default rules can be disabled individually by specifying an empty set of ranges: ```hcl module "firewall" { source = "./fabric/modules/net-vpc-firewall" project_id = var.project_id network = var.vpc.name default_rules_config = { ssh_ranges = [] } } # tftest modules=1 resources=2 inventory=no-ssh-default-rules.yaml e2e ``` Or the entire set of rules can be disabled via the `disabled` attribute: ```hcl module "firewall" { source = "./fabric/modules/net-vpc-firewall" project_id = var.project_id network = var.vpc.name default_rules_config = { disabled = true } } # tftest modules=0 resources=0 inventory=no-default-rules.yaml e2e ``` ### Including source & destination ranges Custom rules now support including both source & destination ranges in ingress and egress rules: ```hcl module "firewall" { source = "./fabric/modules/net-vpc-firewall" project_id = var.project_id network = var.vpc.name default_rules_config = { disabled = true } egress_rules = { deny-egress-source-destination-ranges = { description = "Deny egress using source and destination ranges" source_ranges = ["10.132.0.0/20", "10.138.0.0/20"] destination_ranges = ["172.16.0.0/12"] } } ingress_rules = { allow-ingress-source-destination-ranges = { description = "Allow ingress using source and destination ranges" source_ranges = ["172.16.0.0/12"] destination_ranges = ["10.132.0.0/20", "10.138.0.0/20"] } } } # tftest modules=1 resources=2 inventory=local-ranges.yaml e2e ``` ### Rules Factory The module includes a rules factory (see [Resource Factories](../../blueprints/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 = "./fabric/modules/net-vpc-firewall" project_id = var.project_id network = var.vpc.name factories_config = { rules_folder = "configs/firewall/rules" cidr_tpl_file = "configs/firewall/cidrs.yaml" } default_rules_config = { disabled = true } } # tftest modules=1 resources=3 files=lbs,cidrs inventory=factory.yaml ``` ```yaml # tftest-file id=lbs path=configs/firewall/rules/load_balancers.yaml --- # Terraform will be unable to decode this file if it does not contain valid YAML # You can retain `---` (start of the document) to indicate an empty document. ingress: allow-healthchecks: description: Allow ingress from healthchecks. source_ranges: - healthchecks targets: ["lb-backends"] rules: - protocol: tcp ports: - 80 - 443 allow-service-1-to-service-2: description: Allow ingress from service-1 SA targets: ["service-2"] use_service_accounts: true sources: - service-1@project-id.iam.gserviceaccount.com rules: - protocol: tcp ports: - 80 - 443 egress: block-telnet: description: block outbound telnet deny: true rules: - protocol: tcp ports: - 23 ``` ```yaml # tftest-file id=cidrs path=configs/firewall/cidrs.yaml --- # Terraform will be unable to decode this file if it does not contain valid YAML # You can retain `---` (start of the document) to indicate an empty document. healthchecks: - 35.191.0.0/16 - 130.211.0.0/22 - 209.85.152.0/22 - 209.85.204.0/22 ``` Instead of using `factories_config.cidr_tpl_file` file, you can pass CIDR blocks directly in the `named_ranges` variable. This approach could be useful for dynamically generated CIDR blocks from outputs of other resources. ```hcl module "firewall" { source = "./fabric/modules/net-vpc-firewall" project_id = var.project_id network = var.vpc.name factories_config = { rules_folder = "configs/firewall/rules" } default_rules_config = { disabled = true } named_ranges = { healthchecks = [ "35.191.0.0/16", "130.211.0.0/22", "209.85.152.0/22", "209.85.204.0/22", ] } } # tftest modules=1 resources=3 files=lbs inventory=factory.yaml ``` ## Variables | name | description | type | required | default | |---|---|:---:|:---:|:---:| | [network](variables.tf#L111) | Name of the network this set of firewall rules applies to. | string | ✓ | | | [project_id](variables.tf#L116) | Project id of the project that holds the network. | string | ✓ | | | [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. | object({…}) | | {} | | [egress_rules](variables.tf#L37) | List of egress rule definitions, default to deny action. Null destination ranges will be replaced with 0/0. | map(object({…})) | | {} | | [factories_config](variables.tf#L60) | Paths to data files and folders that enable factory functionality. | object({…}) | | {} | | [ingress_rules](variables.tf#L70) | List of ingress rule definitions, default to allow action. Null source ranges will be replaced with 0/0. | map(object({…})) | | {} | | [named_ranges](variables.tf#L94) | Define mapping of names to ranges that can be used in custom rules. | map(list(string)) | | {…} | ## Outputs | name | description | sensitive | |---|---|:---:| | [default_rules](outputs.tf#L17) | Default rule resources. | | | [rules](outputs.tf#L27) | Custom rule resources. | |