diff --git a/examples/autoscale/README.md b/examples/autoscale/README.md index f0d2fb4e..63e59f02 100644 --- a/examples/autoscale/README.md +++ b/examples/autoscale/README.md @@ -1,27 +1,71 @@ # Deployment of Palo Alto Networks VM-Series Firewalls with Autoscaling -The firewalls are created and destroyed by the GCP managed instance group. +This example deploys VM-Series firewalls into a managed instance group. The VM-Series firewalls can be scaled horizontally based on custom PAN-OS metrics delivered to Google Stackdriver. + +For managed instance group deployments, it is highly recommended to bootstrap the VM-Series firewalls to Panorama for automatic configuration. + +## Instructions + +1. Set up Panorama on-premises or in a VPC network (consider using `examples/panorama`). + * Panorama must have network connectivity to the management VPC network created in this example build. + * If you already have a management VPC network, perform the following in `main.tf`: + 1. Replace `module "vpc_mgmt"` with `data google_compute_subnetwork` to pull your existing management subnetwork. + 2. Within the the VM-Series `autoscale` module, set the management NIC to use your data resource in the previous step. For example: + +

+data "google_compute_subnetwork" "mgmt" {
+    name   = "default-us-east1"
+    region = var.region
+}
+
+....
+....
+
+module "autoscale" {
+source = "../../modules/autoscale"
+
+zones = {
+    zone1 = data.google_compute_zones.main.names[0]
+    zone2 = data.google_compute_zones.main.names[1]
+}
+
+prefix                = "${local.prefix}vmseries-mig"
+deployment_name       = "${local.prefix}vmseries-mig-deployment"
+machine_type          = var.vmseries_machine_type
+image                 = var.vmseries_image_name
+pool                  = module.extlb.target_pool
+scopes                = ["https://www.googleapis.com/auth/cloud-platform"]
+service_account_email = module.iam_service_account.email
+min_replicas_per_zone = var.vmseries_per_zone_min  // min firewalls per zone.
+max_replicas_per_zone = var.vmseries_per_zone_max  // max firewalls per zone.
+autoscaler_metrics    = var.autoscaler_metrics
+
+network_interfaces = [
+    {
+        subnetwork       = module.vpc_untrust.subnets_self_links[0]
+        create_public_ip = true
+    },
+    {
+        subnetwork       = data.google_compute_subnetwork.mgmt.self_link
+        create_public_ip = false 
+    },
+    {
+        subnetwork       = module.vpc_trust.subnets_self_links[0]
+        create_public_ip = false
+    }
+]
+
+....
+....
+
+ +3. On Panorama, create a Device Group, Template Stack, and generate a VM Auth Key. +4. Copy the example.tfvars into terraform.tfvars. Modify the values within terraform.tfvars to match your deployment. + + +## Deploy Terraform -For enhanced security, the firewalls' management interfaces are unreachable through public IP addresses (there is however a jumphost to aid initial troubleshooting). - -## Caveat - -1. The auto-scaling happens independently in each zone (it appears to be a limitation of GCP plugin 2.0.0 on Panorama, it simply does not check for the regional instance groups). The test was on Panorama 9.1.4. -2. The PanOS custom GCP metrics like `panSessionActive` require more work. See the GCP Metric Explorer. - -## Instruction - -- Set up Panorama and its VPC (consider using `examples/panorama`). -- Configure Panorama. This example assumes it exists with proper settings. -- Optionally, restart Panorama with `request restart system` to ensure the vm-auth-key is saved properly. -- Go to the main directory of the example (i.e. where this `README.md` is placed). -- Copy the `example.tfvars` into `terraform.tfvars` and modify it to your needs. -- Generate the SSH keys in the example's directory e.g.: `ssh-keygen -t rsa -C admin -N '' -f id_rsa` -- Manually edit the settings in `bootstrap_files/authcodes` -- Manually edit the settings in `bootstrap_files/init-cfg.txt` -- Deploy Terraform: - -```sh +``` terraform init terraform plan terraform apply @@ -42,57 +86,49 @@ terraform apply | Name | Version | |------|---------| | [google](#provider\_google) | ~> 3.48 | -| [null](#provider\_null) | ~> 2.1 | ## Modules | Name | Source | Version | |------|--------|---------| | [autoscale](#module\_autoscale) | ../../modules/autoscale | n/a | -| [bootstrap](#module\_bootstrap) | ../../modules/bootstrap/ | n/a | | [extlb](#module\_extlb) | ../../modules/lb_external/ | n/a | | [iam\_service\_account](#module\_iam\_service\_account) | ../../modules/iam_service_account/ | n/a | | [intlb](#module\_intlb) | ../../modules/lb_internal/ | n/a | -| [jumphost](#module\_jumphost) | ../../modules/vmseries | n/a | -| [jumpvpc](#module\_jumpvpc) | ../../modules/vpc | n/a | | [mgmt\_cloud\_nat](#module\_mgmt\_cloud\_nat) | terraform-google-modules/cloud-nat/google | =1.2 | -| [vpc](#module\_vpc) | ../../modules/vpc | n/a | +| [vpc\_mgmt](#module\_vpc\_mgmt) | terraform-google-modules/network/google | ~> 4.0 | +| [vpc\_trust](#module\_vpc\_trust) | terraform-google-modules/network/google | ~> 4.0 | +| [vpc\_untrust](#module\_vpc\_untrust) | terraform-google-modules/network/google | ~> 4.0 | ## Resources | Name | Type | |------|------| -| [google_compute_firewall.this](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_firewall) | resource | -| [null_resource.jumphost_ssh_priv_key](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource | -| [google_compute_zones.this](https://registry.terraform.io/providers/hashicorp/google/latest/docs/data-sources/compute_zones) | data source | +| [google_compute_zones.main](https://registry.terraform.io/providers/hashicorp/google/latest/docs/data-sources/compute_zones) | data source | ## Inputs | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| +| [allowed\_sources](#input\_allowed\_sources) | A list of IP addresses to be added to the management network's ingress firewall rule. The IP addresses will be able to access to the VM-Series management interface. | `list(string)` | `null` | no | | [autoscaler\_metrics](#input\_autoscaler\_metrics) | The map with the keys being metrics identifiers (e.g. custom.googleapis.com/VMSeries/panSessionUtilization).
Each of the contained objects has attribute `target` which is a numerical threshold for a scale-out or a scale-in.
Each zonal group grows until it satisfies all the targets.

Additional optional attribute `type` defines the metric as either `GAUGE` (the default), `DELTA_PER_SECOND`, or `DELTA_PER_MINUTE`.
For full specification, see the `metric` inside the [provider doc](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_autoscaler). | `map` |
{
"custom.googleapis.com/VMSeries/panSessionActive": {
"target": 100
}
}
| no | -| [extlb\_healthcheck\_port](#input\_extlb\_healthcheck\_port) | n/a | `number` | `80` | no | -| [extlb\_name](#input\_extlb\_name) | n/a | `string` | `"as4-fw-extlb"` | no | -| [fw\_machine\_type](#input\_fw\_machine\_type) | n/a | `string` | `"n1-standard-4"` | no | -| [fw\_network\_ordering](#input\_fw\_network\_ordering) | A list of names from the `networks[*].name` attributes. | `list` | `[]` | no | -| [intlb\_global\_access](#input\_intlb\_global\_access) | (Optional) If true, clients can access ILB from all regions. By default false, only allow from the ILB's local region; useful if the ILB is a next hop of a route. | `bool` | `false` | no | -| [intlb\_name](#input\_intlb\_name) | n/a | `string` | `"as4-fw-intlb"` | no | -| [intlb\_network](#input\_intlb\_network) | Name of the defined network that will host the Internal Load Balancer. One of the names from the `networks[*].name` attribute. | `any` | n/a | yes | -| [mgmt\_network](#input\_mgmt\_network) | Name of the network to create for firewall management. One of the names from the `networks[*].name` attribute. | `any` | n/a | yes | -| [mgmt\_sources](#input\_mgmt\_sources) | n/a | `list(string)` |
[
"0.0.0.0/0"
]
| no | -| [networks](#input\_networks) | The list of maps describing the VPC networks and subnetworks | `any` | n/a | yes | -| [prefix](#input\_prefix) | Prefix to GCP resource names, an arbitrary string | `string` | `"as4"` | no | -| [private\_key\_path](#input\_private\_key\_path) | Local path to private SSH key. To generate the key pair use `ssh-keygen -t rsa -C admin -N '' -f id_rsa` | `any` | `null` | no | -| [project](#input\_project) | GCP Project ID | `string` | n/a | yes | +| [cidr\_mgmt](#input\_cidr\_mgmt) | The CIDR range of the management subnetwork. | `string` | `null` | no | +| [cidr\_trust](#input\_cidr\_trust) | The CIDR range of the trust subnetwork. | `string` | `null` | no | +| [cidr\_untrust](#input\_cidr\_untrust) | The CIDR range of the untrust subnetwork. | `string` | `null` | no | +| [panorama\_address](#input\_panorama\_address) | The Panorama IP/Domain address. The Panorama address must be reachable from the management VPC.
This build assumes Panorama is reachable via the internet. The management VPC network uses a
NAT gateway to communicate to Panorama's external IP addresses. | `string` | n/a | yes | +| [panorama\_device\_group](#input\_panorama\_device\_group) | The name of the Panorama device group that will bootstrap the VM-Series firewalls. | `string` | n/a | yes | +| [panorama\_template\_stack](#input\_panorama\_template\_stack) | The name of the Panorama template stack that will bootstrap the VM-Series firewalls. | `string` | n/a | yes | +| [panorama\_vm\_auth\_key](#input\_panorama\_vm\_auth\_key) | Panorama VM authorization key. To generate, follow this guide https://docs.paloaltonetworks.com/vm-series/10-1/vm-series-deployment/bootstrap-the-vm-series-firewall/generate-the-vm-auth-key-on-panorama.html | `string` | n/a | yes | +| [prefix](#input\_prefix) | Prefix to GCP resource names, an arbitrary string | `string` | `null` | no | | [project\_id](#input\_project\_id) | GCP Project ID | `string` | n/a | yes | -| [public\_key\_path](#input\_public\_key\_path) | Local path to public SSH key. To generate the key pair use `ssh-keygen -t rsa -C admin -N '' -f id_rsa` If you do not have a public key, run `ssh-keygen -f ~/.ssh/demo-key -t rsa -C admin` | `string` | `"id_rsa.pub"` | no | -| [region](#input\_region) | GCP Region | `string` | `"europe-west4"` | no | -| [service\_account](#input\_service\_account) | IAM Service Account for running firewall instances (just the identifier, without `@domain` part) | `string` | `"paloaltonetworks-fw"` | no | -| [vmseries\_image](#input\_vmseries\_image) | Link to VM-Series PAN-OS image. Can be either a full self\_link, or one of the shortened forms per the [provider doc](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_instance#image). | `string` | `"https://www.googleapis.com/compute/v1/projects/paloaltonetworksgcp-public/global/images/vmseries-byol-912"` | no | +| [public\_key\_path](#input\_public\_key\_path) | Local path to public SSH key. To generate the key pair use `ssh-keygen -t rsa -C admin -N '' -f id_rsa` If you do not have a public key, run `ssh-keygen -f ~/.ssh/demo-key -t rsa -C admin` | `string` | `"~/.ssh/gcp-demo.pub"` | no | +| [region](#input\_region) | GCP Region | `string` | `"us-east1"` | no | +| [vmseries\_image\_name](#input\_vmseries\_image\_name) | Link to VM-Series PAN-OS image. Can be either a full self\_link, or one of the shortened forms per the [provider doc](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_instance#image). | `string` | `"https://www.googleapis.com/compute/v1/projects/paloaltonetworksgcp-public/global/images/vmseries-flex-byol-1014"` | no | +| [vmseries\_machine\_type](#input\_vmseries\_machine\_type) | The Google Compute instance type to run the VM-Series firewall. N1 and N2 instance types are supported. | `string` | `"n1-standard-4"` | no | +| [vmseries\_per\_zone\_max](#input\_vmseries\_per\_zone\_max) | The max number of firewalls to run in each zone. | `number` | `2` | no | +| [vmseries\_per\_zone\_min](#input\_vmseries\_per\_zone\_min) | The minimum number of firewalls to run in each zone. | `number` | `1` | no | ## Outputs -| Name | Description | -|------|-------------| -| [jumphost\_ssh\_command](#output\_jumphost\_ssh\_command) | n/a | +No outputs. diff --git a/examples/autoscale/example.tfvars b/examples/autoscale/example.tfvars index 6b882e00..f220cd1f 100644 --- a/examples/autoscale/example.tfvars +++ b/examples/autoscale/example.tfvars @@ -1,43 +1,15 @@ -public_key_path = "id_rsa.pub" -private_key_path = "id_rsa" - -project_id = "gcp-gcs-pso" -project = "gcp-gcs-pso" -region = "us-central1" - -networks = [ - { - name = "as4-untrust" - subnetwork_name = "as4-untrust" - ip_cidr_range = "192.168.1.0/24" - # Where from are you going to access the example site?: - allowed_sources = ["199.167.52.0/22", "8.47.64.2/32", "208.184.7.0/24", "67.154.150.32/28", "208.184.44.128/27", "64.0.175.110/32", "64.124.146.186/32", "35.191.0.0/16", "130.211.0.0/22", "209.85.152.0/22", "209.85.204.0/22", "124.33.177.32/28", "150.249.195.35/32", "203.116.44.82/32", "118.201.32.208/28", "111.223.77.192/27", "119.73.179.160/28", "125.17.6.254/32", "115.114.47.125/32", "96.92.92.64/28", "63.226.86.16/32", "213.39.97.34/32", "18.130.7.245/32", "84.207.227.0/28", "84.207.230.24/29", "213.208.209.160/30", "155.160.255.8/29", "182.74.171.144/29", "119.225.22.94/32", "13.239.13.13/32", "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"] - # These are for GCP healthchecks: "35.191.0.0/16", "130.211.0.0/22", "209.85.152.0/22", "209.85.204.0/22", "169.254.169.254/32" - }, - { - # Panorama's VPC, it should already exist - provide the names of already existing network/subnetwork. - name = "as4-mgmt" - subnetwork_name = "as4-mgmt" - #create_network = false - #create_subnetwork = false - # Where from are you going to manage the example firewalls?: - allowed_sources = ["199.167.52.0/22", "8.47.64.2/32", "208.184.7.0/24", "67.154.150.32/28", "208.184.44.128/27", "64.0.175.110/32", "64.124.146.186/32", "35.191.0.0/16", "130.211.0.0/22", "209.85.152.0/22", "209.85.204.0/22", "124.33.177.32/28", "150.249.195.35/32", "203.116.44.82/32", "118.201.32.208/28", "111.223.77.192/27", "119.73.179.160/28", "125.17.6.254/32", "115.114.47.125/32", "96.92.92.64/28", "63.226.86.16/32", "213.39.97.34/32", "18.130.7.245/32", "84.207.227.0/28", "84.207.230.24/29", "213.208.209.160/30", "155.160.255.8/29", "182.74.171.144/29", "119.225.22.94/32", "13.239.13.13/32", "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"] - }, - { - name = "as4-trust" - subnetwork_name = "as4-trust" - ip_cidr_range = "192.168.2.0/24" - }, -] - -fw_network_ordering = [ - "as4-untrust", - "as4-mgmt", - "as4-trust", -] - -mgmt_network = "as4-mgmt" -intlb_network = "as4-trust" -intlb_global_access = true - -service_account = "paloaltonetworks-fw" +project_id = null +public_key_path = "~/.ssh/gcp-demo.pub" +region = "us-central1" +prefix = "panw" +cidr_mgmt = "192.168.0.0/28" +cidr_untrust = "192.168.1.0/28" +cidr_trust = "192.168.2.0/28" +allowed_sources = ["0.0.0.0/0"] +panorama_address = "74.97.22.10" +panorama_device_group = "gcp-transit" +panorama_template_stack = "gcp-transit_stack" +panorama_vm_auth_key = "024955173420120" +vmseries_image_name = "https://www.googleapis.com/compute/v1/projects/paloaltonetworksgcp-public/global/images/vmseries-flex-bundle2-1014" +vmseries_per_zone_min = 1 +vmseries_per_zone_max = 2 diff --git a/examples/autoscale/jumphost.tf b/examples/autoscale/jumphost.tf deleted file mode 100644 index 9dee4d88..00000000 --- a/examples/autoscale/jumphost.tf +++ /dev/null @@ -1,69 +0,0 @@ -module "jumpvpc" { - source = "../../modules/vpc" - networks = [ - { - name = "pso-customer-panorama" - subnetwork_name = "pso-customer-panorama-jumphost" - create_network = false - ip_cidr_range = "192.168.13.0/24" - }, - ] - region = "europe-west4" -} - -resource "google_compute_firewall" "this" { - for_each = module.jumpvpc.networks - - name = "${each.value.name}-jumpbox-ingress" - network = each.value.self_link - direction = "INGRESS" - source_ranges = var.networks[0].allowed_sources - target_tags = ["jumphost"] - - allow { - protocol = "tcp" - ports = ["22", "443"] - } -} - -# Spawn the VM-series firewall as a Google Cloud Engine Instance. -module "jumphost" { - source = "../../modules/vmseries" - - name = "as4-jumphost01" - zone = "us-central1-c" - ssh_keys = "admin:${file(var.public_key_path)}" - custom_image = "https://www.googleapis.com/compute/v1/projects/centos-cloud/global/images/centos-7-v20220303" - tags = ["jumphost"] - service_account = module.iam_service_account.email - network_interfaces = [ - { - name = "as4-jumphost01-mgmt" - subnetwork = try(module.vpc.subnetworks[var.mgmt_network].self_link, null) - create_public_ip = true - } - ] -} - -resource "null_resource" "jumphost_ssh_priv_key" { - for_each = module.jumphost.public_ips[0] - - connection { - type = "ssh" - user = "admin" - private_key = file(var.private_key_path) - host = each.value - } - - provisioner "file" { - source = var.private_key_path - destination = "key" - } - - provisioner "remote-exec" { - inline = [ - "chmod go-rwx -R key", - "echo 'Manage firewalls: ssh -i key admin@internal_ip_of_firewall'", - ] - } -} diff --git a/examples/autoscale/main.tf b/examples/autoscale/main.tf index 3b98599b..17b74afd 100644 --- a/examples/autoscale/main.tf +++ b/examples/autoscale/main.tf @@ -1,106 +1,224 @@ -data "google_compute_zones" "this" {} +locals { + prefix = var.prefix != null && var.prefix != "" ? "${var.prefix}-" : "" +} -#----------------------------------------------------------------------------------------------- -# Dedicated IAM service account for running GCP instances of Palo Alto Networks VM-Series. -# Applying this module requires IAM roles Security Admin and Service Account Admin or their equivalents. -# The account will not only be used for running the instances, but also for their GCP plugin access. +data "google_compute_zones" "main" {} -module "iam_service_account" { - source = "../../modules/iam_service_account/" - service_account_id = var.service_account +#----------------------------------------------------------------------------------------------- +# Create Mgmt, untrust, and trust VPC networks. +#----------------------------------------------------------------------------------------------- +/* + It is recommended to have your management network already configured with network access + to Panorama. All autoscale deployments require Panorama. It is recommended to have the + management network preconfigured with network access to Panorama. +*/ + +module "vpc_mgmt" { + source = "terraform-google-modules/network/google" + version = "~> 4.0" + project_id = var.project_id + network_name = "${local.prefix}mgmt-vpc" + routing_mode = "GLOBAL" + + subnets = [ + { + subnet_name = "${local.prefix}${var.region}-mgmt" + subnet_ip = var.cidr_mgmt + subnet_region = var.region + } + ] + + firewall_rules = [ + { + name = "${local.prefix}vmseries-mgmt" + direction = "INGRESS" + priority = "100" + description = "Allow ingress access to VM-Series management interface" + ranges = var.allowed_sources + allow = [ + { + protocol = "tcp" + ports = ["22", "443", "3978"] + } + ] + } + ] } -# Create buckets for bootstrapping the fresh firewall VM. -module "bootstrap" { - source = "../../modules/bootstrap/" +module "vpc_untrust" { + source = "terraform-google-modules/network/google" + version = "~> 4.0" + project_id = var.project_id + network_name = "${local.prefix}untrust-vpc" + routing_mode = "GLOBAL" - service_account = module.iam_service_account.email - files = { - "bootstrap_files/init-cfg.txt" = "config/init-cfg.txt" - "bootstrap_files/authcodes" = "license/authcodes" - } + subnets = [ + { + subnet_name = "${local.prefix}${var.region}-untrust" + subnet_ip = var.cidr_untrust + subnet_region = var.region + } + ] + + firewall_rules = [ + { + name = "${local.prefix}allow-all-untrust" + direction = "INGRESS" + priority = "100" + ranges = ["0.0.0.0/0"] + allow = [ + { + protocol = "all" + ports = [] + } + ] + } + ] } -#----------------------------------------------------------------------------------------------- -# VPC Networks +module "vpc_trust" { + source = "terraform-google-modules/network/google" + version = "~> 4.0" + project_id = var.project_id + network_name = "${local.prefix}trust-vpc" + routing_mode = "GLOBAL" + delete_default_internet_gateway_routes = true -module "vpc" { - source = "../../modules/vpc" + subnets = [ + { + subnet_name = "${local.prefix}${var.region}-trust" + subnet_ip = var.cidr_trust + subnet_region = var.region + } + ] - networks = var.networks + firewall_rules = [ + { + name = "${local.prefix}allow-all-trust" + direction = "INGRESS" + priority = "100" + ranges = ["0.0.0.0/0"] + allow = [ + { + protocol = "all" + ports = [] + } + ] + } + ] } - #----------------------------------------------------------------------------------------------- # Firewalls with auto-scaling. +#----------------------------------------------------------------------------------------------- +/* + Dedicated IAM service account for running GCP instances of Palo Alto Networks VM-Series. + The account is used for running the instances and for also for their GCP plugin access. +*/ +module "iam_service_account" { + source = "../../modules/iam_service_account/" + + service_account_id = "${local.prefix}vmseries-mig-sa" +} + +# Create VM-Series managed instance group for autoscaling module "autoscale" { source = "../../modules/autoscale" zones = { - zone1 = data.google_compute_zones.this.names[0] - zone2 = data.google_compute_zones.this.names[1] + zone1 = data.google_compute_zones.main.names[0] + zone2 = data.google_compute_zones.main.names[1] } - subnetworks = [for v in var.fw_network_ordering : module.vpc.subnetworks[v].name] - - prefix = var.prefix - deployment_name = var.prefix - machine_type = var.fw_machine_type - mgmt_interface_swap = "enable" - ssh_key = fileexists(var.public_key_path) ? "admin:${file(var.public_key_path)}" : "" - image = var.vmseries_image - nic0_public_ip = true - nic1_public_ip = false - nic2_public_ip = false + prefix = "${local.prefix}vmseries-mig" + deployment_name = "${local.prefix}vmseries-mig-deployment" + machine_type = var.vmseries_machine_type + image = var.vmseries_image_name pool = module.extlb.target_pool - bootstrap_bucket = module.bootstrap.bucket_name scopes = ["https://www.googleapis.com/auth/cloud-platform"] - service_account = module.iam_service_account.email - max_replicas_per_zone = 2 + service_account_email = module.iam_service_account.email + min_replicas_per_zone = var.vmseries_per_zone_min // min firewalls per zone. + max_replicas_per_zone = var.vmseries_per_zone_max // max firewalls per zone. autoscaler_metrics = var.autoscaler_metrics + + network_interfaces = [ + { + subnetwork = module.vpc_untrust.subnets_self_links[0] + create_public_ip = true // Used for outbound internet traffic. + }, + { + subnetwork = module.vpc_mgmt.subnets_self_links[0] + create_public_ip = false // Set to true if you want to access the firewalls over the internet (not recommended). + }, + { + subnetwork = module.vpc_trust.subnets_self_links[0] + create_public_ip = false + } + ] + + metadata = { + type = "dhcp-client" + op-command-modes = "mgmt-interface-swap" + vm-auth-key = var.panorama_vm_auth_key + panorama-server = var.panorama_address + dgname = var.panorama_device_group + tplname = var.panorama_template_stack + dhcp-send-hostname = "yes" + dhcp-send-client-id = "yes" + dhcp-accept-server-hostname = "yes" + dhcp-accept-server-domain = "yes" + dns-primary = "8.8.8.8" + dns-secondary = "4.2.2.2" + } + + # Example of full bootstrap with Google storage bucket. + /* + metadata = { + mgmt-interface-swap = "enable" + vmseries-bootstrap-gce-storagebucket = "my-google-bootstrap-bucket" + serial-port-enable = true + ssh-keys = "~/.ssh/vmseries-ssh-key.pub" + } + */ named_ports = [ { name = "http" port = "80" }, ] - - dependencies = [ - module.bootstrap.completion, - ] } + +#----------------------------------------------------------------------------------------------- +# Internal Network Balancer to distribute traffic to VM-Series trust interfaces #----------------------------------------------------------------------------------------------- -# Regional Internal TCP/UDP Load Balancer -# -# It is not strictly required part of this example. -# It's here just to show how to integrate it with auto-scaling. +/* + The load balancers are not required for this example. It is here to provide an example + of how to use the load balancer modules. +*/ module "intlb" { source = "../../modules/lb_internal/" - name = var.intlb_name - network = module.vpc.networks[var.intlb_network].name - subnetwork = module.vpc.subnetworks[var.intlb_network].name - all_ports = true - backends = module.autoscale.backends - - allow_global_access = var.intlb_global_access + name = "${local.prefix}intlb" + network = module.vpc_trust.network_id + subnetwork = module.vpc_trust.subnets_self_links[0] + all_ports = true + backends = module.autoscale.backends + allow_global_access = true } #----------------------------------------------------------------------------------------------- -# Regional External Network Load Balancer -# -# It is not strictly required part of this example. -# It's here just to show how to integrate it with auto-scaling. +# External Network Load Balancer to distribute traffic to VM-Series untrust interfaces +#----------------------------------------------------------------------------------------------- module "extlb" { source = "../../modules/lb_external/" - name = var.extlb_name - rules = { (var.extlb_name) = { port_range = 80 } } + name = "${local.prefix}extlb" + rules = { ("${local.prefix}rule0") = { port_range = 80 } } health_check_http_port = 80 health_check_http_request_path = "/" @@ -108,15 +226,20 @@ module "extlb" { # ----------------------------------------------------------------------------------------------- # Cloud Nat for the management interfaces. -# Needed to reach bootstrap bucket or to log to Cortex DataLake. +# ----------------------------------------------------------------------------------------------- +/* + Cloud NAT is required in teh management network to provide connectivity to Cortex Data Lake + and to license the VM-Series from the PANW licensing servers. +*/ + module "mgmt_cloud_nat" { source = "terraform-google-modules/cloud-nat/google" version = "=1.2" - name = "mgmt" - project_id = "gcp-gcs-pso" # FIXME vars? other module? - region = "europe-west4" + name = "${local.prefix}mgmt-nat" + project_id = var.project_id + region = var.region create_router = true - router = "mgmt" - network = var.mgmt_network + router = "${local.prefix}mgmt-router" + network = module.vpc_mgmt.network_id } diff --git a/examples/autoscale/outputs.tf b/examples/autoscale/outputs.tf index 1a521e5c..e69de29b 100644 --- a/examples/autoscale/outputs.tf +++ b/examples/autoscale/outputs.tf @@ -1,3 +0,0 @@ -output "jumphost_ssh_command" { - value = "ssh -i ${var.private_key_path} admin@${module.jumphost.public_ips[0]}" -} \ No newline at end of file diff --git a/examples/autoscale/variables.tf b/examples/autoscale/variables.tf index 839af4ae..b95af432 100644 --- a/examples/autoscale/variables.tf +++ b/examples/autoscale/variables.tf @@ -3,11 +3,6 @@ variable "project_id" { type = string } -variable "project" { - description = "GCP Project ID" - type = string -} - #variable "auth_file" { # description = "GCP Project auth JSON file" # type = string @@ -15,75 +10,44 @@ variable "project" { variable "region" { description = "GCP Region" - default = "europe-west4" + default = "us-east1" type = string } variable "public_key_path" { description = "Local path to public SSH key. To generate the key pair use `ssh-keygen -t rsa -C admin -N '' -f id_rsa` If you do not have a public key, run `ssh-keygen -f ~/.ssh/demo-key -t rsa -C admin`" - default = "id_rsa.pub" -} - -variable "private_key_path" { - description = "Local path to private SSH key. To generate the key pair use `ssh-keygen -t rsa -C admin -N '' -f id_rsa` " - default = null + default = "~/.ssh/gcp-demo.pub" } -variable "vmseries_image" { +variable "vmseries_image_name" { description = "Link to VM-Series PAN-OS image. Can be either a full self_link, or one of the shortened forms per the [provider doc](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_instance#image)." - default = "https://www.googleapis.com/compute/v1/projects/paloaltonetworksgcp-public/global/images/vmseries-byol-912" + default = "https://www.googleapis.com/compute/v1/projects/paloaltonetworksgcp-public/global/images/vmseries-flex-byol-1014" type = string } -variable "fw_machine_type" { - default = "n1-standard-4" -} - -variable "prefix" { - description = "Prefix to GCP resource names, an arbitrary string" - default = "as4" +variable "vmseries_machine_type" { + description = "The Google Compute instance type to run the VM-Series firewall. N1 and N2 instance types are supported." + default = "n1-standard-4" type = string } -variable "extlb_name" { - default = "as4-fw-extlb" -} - -variable "extlb_healthcheck_port" { - type = number - default = 80 -} - -variable "intlb_name" { - default = "as4-fw-intlb" -} - -variable "mgmt_sources" { - default = ["0.0.0.0/0"] - type = list(string) -} - -variable "networks" { - description = "The list of maps describing the VPC networks and subnetworks" -} -variable "fw_network_ordering" { - description = "A list of names from the `networks[*].name` attributes." - default = [] +variable "vmseries_per_zone_max" { + description = "The max number of firewalls to run in each zone." + default = 2 + type = number } -variable "mgmt_network" { - description = "Name of the network to create for firewall management. One of the names from the `networks[*].name` attribute." +variable "vmseries_per_zone_min" { + description = "The minimum number of firewalls to run in each zone." + default = 1 + type = number } -variable "intlb_network" { - description = "Name of the defined network that will host the Internal Load Balancer. One of the names from the `networks[*].name` attribute." -} - -variable "intlb_global_access" { - description = "(Optional) If true, clients can access ILB from all regions. By default false, only allow from the ILB's local region; useful if the ILB is a next hop of a route." - default = false - type = bool +variable "prefix" { + description = "Prefix to GCP resource names, an arbitrary string" + default = null + type = string } variable "autoscaler_metrics" { @@ -102,8 +66,50 @@ variable "autoscaler_metrics" { } } -variable "service_account" { - description = "IAM Service Account for running firewall instances (just the identifier, without `@domain` part)" - default = "paloaltonetworks-fw" +variable "allowed_sources" { + description = "A list of IP addresses to be added to the management network's ingress firewall rule. The IP addresses will be able to access to the VM-Series management interface." + type = list(string) + default = null +} + +variable "cidr_mgmt" { + description = "The CIDR range of the management subnetwork." + type = string + default = null +} + +variable "cidr_untrust" { + description = "The CIDR range of the untrust subnetwork." + type = string + default = null +} + +variable "cidr_trust" { + description = "The CIDR range of the trust subnetwork." + type = string + default = null +} + +variable "panorama_vm_auth_key" { + description = "Panorama VM authorization key. To generate, follow this guide https://docs.paloaltonetworks.com/vm-series/10-1/vm-series-deployment/bootstrap-the-vm-series-firewall/generate-the-vm-auth-key-on-panorama.html" + type = string +} + +variable "panorama_address" { + description = <<-EOF + The Panorama IP/Domain address. The Panorama address must be reachable from the management VPC. + This build assumes Panorama is reachable via the internet. The management VPC network uses a + NAT gateway to communicate to Panorama's external IP addresses. + EOF + type = string +} + +variable "panorama_device_group" { + description = "The name of the Panorama device group that will bootstrap the VM-Series firewalls." + type = string +} + +variable "panorama_template_stack" { + description = "The name of the Panorama template stack that will bootstrap the VM-Series firewalls." type = string } diff --git a/examples/autoscale/versions.tf b/examples/autoscale/versions.tf index f18215f8..c8b4c4aa 100644 --- a/examples/autoscale/versions.tf +++ b/examples/autoscale/versions.tf @@ -9,6 +9,6 @@ terraform { } provider "google" { - project = var.project + project = var.project_id region = var.region } diff --git a/modules/autoscale/README.md b/modules/autoscale/README.md index a1e14378..2de6253b 100644 --- a/modules/autoscale/README.md +++ b/modules/autoscale/README.md @@ -15,7 +15,6 @@ | Name | Version | |------|---------| | [google](#provider\_google) | ~> 3.48 | -| [null](#provider\_null) | ~> 2.1 | | [random](#provider\_random) | ~> 2.3 | ## Modules @@ -32,45 +31,33 @@ No modules. | [google_pubsub_subscription.this](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/pubsub_subscription) | resource | | [google_pubsub_subscription_iam_member.this](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/pubsub_subscription_iam_member) | resource | | [google_pubsub_topic.this](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/pubsub_topic) | resource | -| [null_resource.dependency_getter](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource | | [random_id.autoscaler](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/id) | resource | | [google_compute_default_service_account.this](https://registry.terraform.io/providers/hashicorp/google/latest/docs/data-sources/compute_default_service_account) | data source | -| [google_project.this](https://registry.terraform.io/providers/hashicorp/google/latest/docs/data-sources/project) | data source | ## Inputs | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| | [autoscaler\_metrics](#input\_autoscaler\_metrics) | The map with the keys being metrics identifiers (e.g. custom.googleapis.com/VMSeries/panSessionUtilization).
Each of the contained objects has attribute `target` which is a numerical threshold for a scale-out or a scale-in.
Each zonal group grows until it satisfies all the targets.

Additional optional attribute `type` defines the metric as either `GAUGE` (the default), `DELTA_PER_SECOND`, or `DELTA_PER_MINUTE`.
For full specification, see the `metric` inside the [provider doc](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_autoscaler). | `map` |
{
"custom.googleapis.com/VMSeries/panSessionThroughputKbps": {
"target": 700000
},
"custom.googleapis.com/VMSeries/panSessionUtilization": {
"target": 70
}
}
| no | -| [bootstrap\_bucket](#input\_bootstrap\_bucket) | n/a | `string` | `""` | no | -| [cooldown\_period](#input\_cooldown\_period) | How much tame does it take for a spawned PA-VM to become functional on the initialization boot | `number` | `720` | no | -| [dependencies](#input\_dependencies) | n/a | `list(string)` | `[]` | no | +| [cooldown\_period](#input\_cooldown\_period) | How much tame does it take for a spawned PA-VM to become functional on the initialization boot | `number` | `480` | no | | [deployment\_name](#input\_deployment\_name) | Deployment Name that matches what is specified in Panorama GCP Plugin | `string` | n/a | yes | | [disk\_type](#input\_disk\_type) | n/a | `string` | `"pd-ssd"` | no | | [image](#input\_image) | Link to VM-Series PAN-OS image. Can be either a full self\_link, or one of the shortened forms per the [provider doc](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_instance#image). | `string` | `"https://www.googleapis.com/compute/v1/projects/paloaltonetworksgcp-public/global/images/vmseries-byol-912"` | no | | [machine\_type](#input\_machine\_type) | n/a | `string` | n/a | yes | | [max\_replicas\_per\_zone](#input\_max\_replicas\_per\_zone) | Maximum number of VM-series instances per *each* of the zones | `number` | `1` | no | -| [mgmt\_interface\_swap](#input\_mgmt\_interface\_swap) | n/a | `string` | `""` | no | +| [metadata](#input\_metadata) | Metadata for VM-Series firewall. Commented examples below show two examples: 1. partial bootstrap to Panorama 2. Full configuration bootstrap from Google storage bucket. | `map(string)` | `{}` | no | | [min\_cpu\_platform](#input\_min\_cpu\_platform) | n/a | `string` | `"Intel Broadwell"` | no | | [min\_replicas\_per\_zone](#input\_min\_replicas\_per\_zone) | Minimum number of VM-series instances per *each* of the zones | `number` | `1` | no | | [named\_ports](#input\_named\_ports) | (Optional) The list of named ports:
named_ports = [
{
name = "http"
port = "80"
},
{
name = "app42"
port = "4242"
},
]
The name identifies the backend port to receive the traffic from the global load balancers. | `list` | `[]` | no | -| [nic0\_ip](#input\_nic0\_ip) | n/a | `list(string)` |
[
""
]
| no | -| [nic0\_public\_ip](#input\_nic0\_public\_ip) | n/a | `bool` | `false` | no | -| [nic1\_ip](#input\_nic1\_ip) | n/a | `list(string)` |
[
""
]
| no | -| [nic1\_public\_ip](#input\_nic1\_public\_ip) | n/a | `bool` | `false` | no | -| [nic2\_ip](#input\_nic2\_ip) | n/a | `list(string)` |
[
""
]
| no | -| [nic2\_public\_ip](#input\_nic2\_public\_ip) | n/a | `bool` | `false` | no | +| [network\_interfaces](#input\_network\_interfaces) | List of the network interface specifications.
Available options:
- `subnetwork` - (Required\|string) Self-link of a subnetwork to create interface in.
- `create_public_ip` - (Optional\|boolean) Whether to reserve public IP for the interface. Ignored if `public_ip` is provided. Defaults to 'false'. | `list(any)` | n/a | yes | | [pool](#input\_pool) | The self\_link of google\_compute\_target\_pool where the auto-created instances will be placed for healtchecking of External Load Balancer | `string` | `null` | no | | [prefix](#input\_prefix) | Prefix to various GCP resource names | `string` | n/a | yes | -| [region](#input\_region) | GCP region to deploy to, if not set the default provider region is used. | `string` | `null` | no | +| [region](#input\_region) | The Google Cloud region for the resources. If null is provided, provider region will be used. | `string` | `null` | no | | [scale\_in\_control\_replicas\_fixed](#input\_scale\_in\_control\_replicas\_fixed) | Fixed number of VM instances that can be killed in each zone within the scale-in time window.
See `scale_in_control` in the [provider doc](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_autoscaler). | `number` | `1` | no | | [scale\_in\_control\_time\_window\_sec](#input\_scale\_in\_control\_time\_window\_sec) | How many seconds autoscaling should look into the past when scaling in (down).
Default 30 minutes corresponds to the default custom metrics period of 5 minutes
and also to the considerable init time of a fresh instance. | `number` | `1800` | no | | [scopes](#input\_scopes) | n/a | `list(string)` |
[
"https://www.googleapis.com/auth/compute.readonly",
"https://www.googleapis.com/auth/cloud.useraccounts.readonly",
"https://www.googleapis.com/auth/devstorage.read_only",
"https://www.googleapis.com/auth/logging.write",
"https://www.googleapis.com/auth/monitoring.write"
]
| no | -| [service\_account](#input\_service\_account) | IAM Service Account for running firewall instance (just the email) | `string` | `null` | no | -| [ssh\_key](#input\_ssh\_key) | n/a | `string` | `""` | no | -| [subnetworks](#input\_subnetworks) | n/a | `list(string)` | n/a | yes | +| [service\_account\_email](#input\_service\_account\_email) | IAM Service Account for running firewall instance (just the email) | `string` | `null` | no | | [tags](#input\_tags) | n/a | `list(string)` | `[]` | no | -| [update\_policy\_min\_ready\_sec](#input\_update\_policy\_min\_ready\_sec) | After underlying template changes (e.g. PAN-OS upgrade) and the new instance is being spawned,
how long to wait after it becomes online.
See `update_policy` in the [provider doc](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_instance_group_manager)." | `number` | `720` | no | | [update\_policy\_type](#input\_update\_policy\_type) | What to do when the underlying template changes (e.g. PAN-OS upgrade).
OPPORTUNISTIC is the only recommended value. Also PROACTIVE is allowed: it immediately
starts to re-create/delete instances and since this is not coordinated with
the instance group manager in other zone, it can easily lead to total outage.
It is thus feasible only in dev environments. Real environments should
perform a "Rolling Update" in GCP web interface. | `string` | `"OPPORTUNISTIC"` | no | | [zones](#input\_zones) | Map of zone names for the zonal IGMs | `map(string)` | `{}` | no | diff --git a/modules/autoscale/main.tf b/modules/autoscale/main.tf index 31e17007..132c59e7 100644 --- a/modules/autoscale/main.tf +++ b/modules/autoscale/main.tf @@ -12,60 +12,30 @@ terraform { } } -resource "null_resource" "dependency_getter" { - provisioner "local-exec" { - command = "echo ${length(var.dependencies)}" - } -} - resource "google_compute_instance_template" "this" { name_prefix = var.prefix machine_type = var.machine_type min_cpu_platform = var.min_cpu_platform can_ip_forward = true tags = var.tags - - metadata = { - mgmt-interface-swap = var.mgmt_interface_swap - vmseries-bootstrap-gce-storagebucket = var.bootstrap_bucket - serial-port-enable = true - ssh-keys = var.ssh_key - } + metadata = var.metadata service_account { scopes = var.scopes - email = var.service_account - } - - network_interface { - - dynamic "access_config" { - for_each = var.nic0_public_ip ? [""] : [] - content {} - } - network_ip = var.nic0_ip[0] - subnetwork = var.subnetworks[0] - } - - network_interface { - dynamic "access_config" { - for_each = var.nic1_public_ip ? [""] : [] - content {} - } - network_ip = var.nic1_ip[0] - subnetwork = var.subnetworks[1] + email = var.service_account_email } + // Create multiple interfaces (max 8) dynamic "network_interface" { - for_each = try([var.subnetworks[2]], []) + for_each = var.network_interfaces content { + subnetwork = network_interface.value.subnetwork + dynamic "access_config" { - for_each = var.nic2_public_ip ? [""] : [] + for_each = try(network_interface.value.create_public_ip, false) ? ["one"] : [] content {} } - network_ip = var.nic2_ip[0] - subnetwork = var.subnetworks[2] } } @@ -103,8 +73,9 @@ resource "google_compute_instance_group_manager" "this" { } update_policy { - type = var.update_policy_type - min_ready_sec = var.update_policy_min_ready_sec + type = var.update_policy_type + // Currently in google-beta provider. Will merge when it becomes GA. + #min_ready_sec = var.update_policy_min_ready_sec max_surge_fixed = 1 minimal_action = "REPLACE" } @@ -116,10 +87,6 @@ resource "google_compute_instance_group_manager" "this" { port = named_port.value.port } } - - depends_on = [ - null_resource.dependency_getter - ] } resource "random_id" "autoscaler" { @@ -162,32 +129,23 @@ resource "google_compute_autoscaler" "this" { } } -data "google_project" "this" {} - -locals { - // Bug: `terraform plan` fails on an empty tfstate, because it says google_project.this.project_id is null - // Workaround: use this.id instead - project_id = replace(data.google_project.this.id, "projects/", "") -} - #--------------------------------------------------------------------------------- # Pub-Sub is intended to be used by various cloud applications to register # new ip/port that would be consumed by Panorama and automatically onboarded. resource "google_pubsub_topic" "this" { - name = "${var.deployment_name}-${local.project_id}-panorama-apps-deployment" + name = "${var.deployment_name}-panorama-apps-deployment" } - resource "google_pubsub_subscription" "this" { - name = "${var.deployment_name}-${local.project_id}-panorama-plugin-subscription" + name = "${var.deployment_name}-panorama-plugin-subscription" topic = google_pubsub_topic.this.id } resource "google_pubsub_subscription_iam_member" "this" { subscription = google_pubsub_subscription.this.id role = "roles/pubsub.subscriber" - member = "serviceAccount:${coalesce(var.service_account, data.google_compute_default_service_account.this.email)}" + member = "serviceAccount:${coalesce(var.service_account_email, data.google_compute_default_service_account.this.email)}" } data "google_compute_default_service_account" "this" {} diff --git a/modules/autoscale/variables.tf b/modules/autoscale/variables.tf index 9a0d8c82..141b196c 100644 --- a/modules/autoscale/variables.tf +++ b/modules/autoscale/variables.tf @@ -3,20 +3,16 @@ variable "prefix" { type = string } -variable "subnetworks" { - type = list(string) +variable "region" { + description = "The Google Cloud region for the resources. If null is provided, provider region will be used." + default = null + type = string } variable "machine_type" { type = string } -variable "region" { - description = "GCP region to deploy to, if not set the default provider region is used." - default = null - type = string -} - variable "zones" { description = "Map of zone names for the zonal IGMs" default = {} @@ -38,16 +34,6 @@ variable "disk_type" { default = "pd-ssd" } -variable "bootstrap_bucket" { - type = string - default = "" -} - -variable "ssh_key" { - type = string - default = "" -} - variable "scopes" { type = list(string) @@ -71,44 +57,12 @@ variable "tags" { default = [] } -variable "dependencies" { - type = list(string) - default = [] -} - -variable "nic0_ip" { - type = list(string) - default = [""] -} - -variable "nic1_ip" { - type = list(string) - default = [""] -} - -variable "nic2_ip" { - type = list(string) - default = [""] -} - -variable "mgmt_interface_swap" { - default = "" -} - -variable "nic0_public_ip" { - type = bool - default = false -} - -variable "nic1_public_ip" { - type = bool - default = false +variable "metadata" { + description = "Metadata for VM-Series firewall. Commented examples below show two examples: 1. partial bootstrap to Panorama 2. Full configuration bootstrap from Google storage bucket." + default = {} + type = map(string) } -variable "nic2_public_ip" { - type = bool - default = false -} variable "pool" { description = "The self_link of google_compute_target_pool where the auto-created instances will be placed for healtchecking of External Load Balancer" @@ -149,7 +103,7 @@ variable "min_replicas_per_zone" { variable "cooldown_period" { description = "How much tame does it take for a spawned PA-VM to become functional on the initialization boot" - default = 720 + default = 480 type = number } @@ -172,15 +126,16 @@ variable "scale_in_control_replicas_fixed" { type = number } -variable "update_policy_min_ready_sec" { - description = <<-EOF - After underlying template changes (e.g. PAN-OS upgrade) and the new instance is being spawned, - how long to wait after it becomes online. - See `update_policy` in the [provider doc](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_instance_group_manager)." - EOF - default = 720 - type = number -} +// Currently in google-beta provider. Will merge once it is GA. +# variable "update_policy_min_ready_sec" { +# description = <<-EOF +# After underlying template changes (e.g. PAN-OS upgrade) and the new instance is being spawned, +# how long to wait after it becomes online. +# See `update_policy` in the [provider doc](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_instance_group_manager)." +# EOF +# default = 720 +# type = number +# } variable "update_policy_type" { description = <<-EOF @@ -217,8 +172,18 @@ variable "named_ports" { default = [] } -variable "service_account" { +variable "service_account_email" { description = "IAM Service Account for running firewall instance (just the email)" default = null type = string } + +variable "network_interfaces" { + description = <<-EOF + List of the network interface specifications. + Available options: + - `subnetwork` - (Required|string) Self-link of a subnetwork to create interface in. + - `create_public_ip` - (Optional|boolean) Whether to reserve public IP for the interface. Ignored if `public_ip` is provided. Defaults to 'false'. + EOF + type = list(any) +} \ No newline at end of file