diff --git a/bgp_ncc_star_tutorial/.gitignore b/bgp_ncc_star_tutorial/.gitignore new file mode 100644 index 0000000..9edb978 --- /dev/null +++ b/bgp_ncc_star_tutorial/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +.terraform.lock.hcl +.terraform +.terraform.tfstate.lock.info +terraform.tfstate +terraform.tfstate.backup +terraform.tfvars +bootstrap.xml \ No newline at end of file diff --git a/bgp_ncc_star_tutorial/LICENSE b/bgp_ncc_star_tutorial/LICENSE new file mode 100644 index 0000000..8f2dfae --- /dev/null +++ b/bgp_ncc_star_tutorial/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Matt McLimans + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/bgp_ncc_star_tutorial/README.md b/bgp_ncc_star_tutorial/README.md new file mode 100644 index 0000000..020249a --- /dev/null +++ b/bgp_ncc_star_tutorial/README.md @@ -0,0 +1,268 @@ +# Google Cloud NCC & VM-Series Tutorial + +This tutorial shows how to use the [Network Connectivity Center](https://cloud.google.com/network-connectivity/docs/network-connectivity-center/concepts/overview) star topology with Palo Alto Networks VM-Series firewalls. This solution supports the following use-cases: + +* Secure internet inbound and outbound traffic to edge VPC networks. +* Secure east-west traffic between edge VPC networks. +* Secure Cloud VPN traffic to edge VPC networks. + +> [!NOTE] +> Please see [comparison to VPC Network Peering](https://cloud.google.com/network-connectivity/docs/network-connectivity-center/concepts/vpc-spokes-overview#vpc-peering-vs-vpc-spokes) for a comparison to VPC Network Peering. + +> [!IMPORTANT] +>To connect the VM-Series as a **Router Appliance** using BGP, see [VM-Series as a Router Appliance (BGP)](bgp_tutorial/README.md). + +This tutorial is intended for network administrators, solution architects, and security professionals who are familiar with [Compute Engine](https://cloud.google.com/compute) and [Virtual Private Cloud (VPC) networking](https://cloud.google.com/vpc). + +## Architecture +The star topology uses two spoke groups with separate route tables to enforce segmentation. All networks belonging to a spoke group are connected to an NCC hub. + +- **Center group:** Allows traffic between center spokes and between center and edge spokes. +- **Edge group:** Allows traffic only to center spokes; direct edge-to-edge communication is blocked. + +The VM-Series `trust-vpc` belongs to the **center group**, while the protected networks, `spoke1-vpc` and `spoke2-vpc` belong to the **edge group**. All ingress and egress traffic from edge networks is routed through the center group, where it is inspected by the VM-Series firewall. + + + +For more information please see [Network Connectivity Center Star Topology](https://cloud.google.com/network-connectivity/docs/network-connectivity-center/concepts/connectivity-topologies#star-topology). + +## Requirements + +The following is required for this tutorial: + +1. A Google Cloud project. +2. A machine with Terraform version:`>= 1.6.0` + +> [!NOTE] +> This tutorial assumes you are using Google Cloud Shell. + + +## Prepare for Deployment + +1. Enable the required APIs and clone the repository. + + ``` + gcloud services enable compute.googleapis.com + git clone https://github.com/PaloAltoNetworks/google-cloud-ncc-tutorial + cd google-cloud-ncc-tutorial + ``` + +2. Generate an SSH key. + + ``` + ssh-keygen -f ~/.ssh/vmseries-tutorial -t rsa + ``` + +3. Create a `terraform.tfvars` file. + + ``` + cp terraform.tfvars.example terraform.tfvars + ``` + +4. Edit the `terraform.tfvars` file and set values for the following variables: + + | Key | Value | + | ---------------------- | ----------------------------------------------------------------------- | + | `project_id` | The Project ID within Google Cloud. | + | `public_key_path` | The local path of the public key you previously created | + | `vmseries_mgmt_ips` | A list of IPv4 addresses which require access to the VM-Series MGT NIC. | + | `vmseries_image` | The VM-Series image to deploy. | + | `configure_ncc` | If `true`, create the NCC hub, center, and edge groups. | + +> [!TIP] +> For `vmseries_image_name`, a full list of public images can be found using `gcloud`: +> ``` +> gcloud compute images list --project paloaltonetworksgcp-public --filter='name ~ .*vmseries-flex.*' +> ``` + +> [!NOTE] +> If you are using BYOL image (i.e. `vmseries-flex-byol-*`), the license can be applied during or after deployment. To license during deployment, add your VM-Series Authcodes to `bootstrap_files/authcodes`.

See [VM-Series Bootstrap Methods](https://docs.paloaltonetworks.com/vm-series/11-1/vm-series-deployment/bootstrap-the-vm-series-firewall) for more information. + + +### Deploy + +When no further changes are necessary in the configuration, deploy the resources. + +1. Initialize and apply the Terraform plan. + + ``` + terraform init + terraform apply + ``` + + Enter `yes` to create the resources. + +2. After all the resources are created, Terraform displays the following message: + +
+    Apply complete!
+
+    Outputs:
+
+    EXTERNAL_LB_IP = "35.192.118.173"
+ +### Verify NCC Star Topology + +1. In Cloud Shell, create an environment variable to represent your Project ID. + +
+    export PROJECT_ID=YOUR_PROJECT_ID
+ +2. Retrieve the name of your NCC hub. + + ``` + export HUB_NAME=$(gcloud network-connectivity hubs list \ + --project=$PROJECT_ID \ + --format="value(name)") + ``` + +2. list the spokes and verify that `spoke1-vpc`, `spoke2-vpc`, and the `trust-vpc` are onboarded to the NCC hub. + + ``` + gcloud network-connectivity hubs list-spokes $HUB_NAME \ + --project=$PROJECT_ID \ + --format='table(name,group,spokeType,state)' + ``` + + (output) +
+    NAME    GROUP   TYPE         STATE
+    spoke1  edge    VPC_NETWORK  ACTIVE
+    spoke2  edge    VPC_NETWORK  ACTIVE
+    trust   center  VPC_NETWORK  ACTIVE
+ +> [!IMPORTANT] +> Ensure that both `spoke1-vpc` and `spoke2-vpc` are assigned to the **EDGE** group, and the `trust-vpc` is assigned to the **CENTER** group. All spokes must show **state**=`ACTIVE`. + + +## Access the Firewall + +To access the VM-Series's user interface, a password must be set for the `admin` user. + +> [!CAUTION] +> It may take an additional 10 minutes for the firewalls to become fully available. + +1. Retrieve the `EXTERNAL_IP` attached to the VM-Series MGT interface (`NIC1`). + + ``` + gcloud compute instances list \ + --filter='tags.items=(vmseries-tutorial)' \ + --format='value(EXTERNAL_IP)' + ``` + +2. SSH to the VM-Series using the `EXTERNAL_IP` with your private SSH key. + +
+    ssh admin@EXTERNAL_IP -i ~/.ssh/vmseries-tutorial
+ +3. On the VM-Series, set a password for the `admin` username. + + ``` + configure + set mgt-config users admin password + ``` + +4. Commit the changes. + ``` + commit + ``` + +5. Enter `exit` twice to terminate the session. + +6. Access the VM-Series web interface using a web browser. Login with the `admin` user and password. + +
+    https://EXTERNAL_IP
+ + +## Internet inbound traffic + +Test inbound internet traffic by accessing the forwarding rule on the external load balancer. Traffic received by the load balancer is forwarded to the VM-Seriess for inspection. If the VM-Series permits the traffic, it performs source NAT to its trust interface (for synchronous response) and destination NAT to the target application in the spoke VPC network. + + + +> [!IMPORTANT] +> The spoke VMs in this tutorial are configured with Jenkins and a generic web service. + +1. Open a HTTP connection to the web service on `spoke1-vm1` by copying the `EXTERNAL_LB_IP` output value into a web browser. + > You can retrieve the `EXTERNAL_LB_IP` by entering `terraform output` in Cloud Shell. + +
+    http://EXTERNAL_LB_IP
+ + + + +1. Open an SSH session with `spoke2-vm1` using the `EXTERNAL_LB_IP` output value. + +
+    ssh paloalto@EXTERNAL_LB_IP -i ~/.ssh/vmseries-tutorial
+ +3. On the VM-Series, go to **Monitor → Traffic**. + +4. Enter the following into the log filter to view your internet inbound traffic. + + ``` + (( port.dst eq '80' ) or ( port.dst eq '22' )) and (( app eq 'ssh' ) or ( app eq 'web-browsing' )) + ``` + + + +> ![NOTE] +> The external load balancer distributes the HTTP & SSH request to the VM-Series. The VM-Series inspects and translates HTTP traffic to `spoke1-vm1` and SSH traffic to `spoke2-vm1`. + + +### Internet outbound & east/west traffic + +In an NCC star topology, protected workloads reside in **edge VPCs**, and the VM-Series is deployed in the **center VPC**, which serves as its `trust-vpc`. The NCC hub shares routes with the edge spokes so they forward all traffic to the center for inspection, using the VM-Series internal load balancer as the next hop. + + + + +2. From `spoke2-vm1`, test **outbound internet** inspection by generating pseudo malicious traffic to the internet. + + ``` + curl -s -o /dev/null -w "%{http_code}\n" http://www.eicar.org/cgi-bin/.%2e/.%2e/.%2e/.%2e/bin/sh --data "echo Content-Type: text/plain; echo; uname -a" --max-time 2 + curl -s -o /dev/null -w "%{http_code}\n" http://www.eicar.org/cgi-bin/user.sh -H "FakeHeader:() { :; }; echo Content-Type: text/html; echo ; /bin/uname -a" --max-time 2 + ``` + + +3. Test **east-west** inspection by generating pseudo malicious traffic between `spoke2-vm1` and `spoke1-vm1`. + + ``` + curl http://10.1.0.10/cgi-bin/../../../..//bin/cat%20/etc/passwd + ``` + +4. On the VM-Series, go to **Monitor → Threat** to view the threat logs. + + + +> [!TIP] +> The security policies enable you to allow or block traffic based on the user, application, and device. When traffic matches an allow rule, the security profiles that are attached to the rule provide further content inspection. See [Cloud-Delivered Security Services](https://www.paloaltonetworks.com/network-security/security-subscriptions) for more information. + + +## Clean up + +Delete all the resources when you no longer need them. + +1. Run the following command + + ``` + terraform destroy + ``` + +2. At the prompt to perform the actions, enter `yes`. + + After all the resources are deleted, Terraform displays the following message: + + ``` + Destroy complete! + ``` + +## Additional information + +* Learn about the[ VM-Series on Google Cloud](https://docs.paloaltonetworks.com/vm-series/10-2/vm-series-deployment/set-up-the-vm-series-firewall-on-google-cloud-platform/about-the-vm-series-firewall-on-google-cloud-platform). +* Getting started with [Palo Alto Networks PAN-OS](https://docs.paloaltonetworks.com/pan-os). +* Read about [securing Google Cloud Networks with the VM-Series](https://cloud.google.com/architecture/partners/palo-alto-networks-vmseries). +* Learn about [VM-Series licensing on all platforms](https://docs.paloaltonetworks.com/vm-series/10-2/vm-series-deployment/license-the-vm-series-firewall/vm-series-firewall-licensing.html#id8fea514c-0d85-457f-b53c-d6d6193df07c). +* Use the [VM-Series Terraform modules for Google Cloud](https://registry.terraform.io/modules/PaloAltoNetworks/vmseries-modules/google/latest). \ No newline at end of file diff --git a/bgp_ncc_star_tutorial/bootstrap_files/authcodes b/bgp_ncc_star_tutorial/bootstrap_files/authcodes new file mode 100644 index 0000000..e69de29 diff --git a/bgp_ncc_star_tutorial/bootstrap_files/bootstrap.template b/bgp_ncc_star_tutorial/bootstrap_files/bootstrap.template new file mode 100644 index 0000000..a794509 --- /dev/null +++ b/bgp_ncc_star_tutorial/bootstrap_files/bootstrap.template @@ -0,0 +1,1671 @@ + + + + + + + + yes + + + + + + yes + 8 + no + + no + + + + + + + + + + + + yes + 5 + + + yes + 5 + + + yes + 5 + + + yes + 10 + + + yes + 5 + + + + yes + + + + 10 + 10 + + 100 + 50 + + + + 10 + 10 + + 100 + 50 + + + + + + 100 + yes + + + + + + + + + + + + + + no + + + + no + + + no + + + yes + + allow-ping + + no + + + + + + + no + + + + no + + + no + + + + + allow-ping + + no + + + + + + + + + no + + + + + allow-health-checks + + + + + + + + 3 + 5 + wait-recover + + + + + yes + + + + + + + + + + yes + yes + no + + + + + + + + + aes-128-cbc + 3des + + + sha1 + + + group2 + + + 8 + + + + + aes-128-cbc + + + sha256 + + + group19 + + + 8 + + + + + aes-256-cbc + + + sha384 + + + group20 + + + 8 + + + + + + + + aes-128-cbc + 3des + + + sha1 + + + group2 + + 1 + + + + + + aes-128-gcm + + + none + + + group19 + + 1 + + + + + + aes-256-gcm + + + none + + + group20 + + 1 + + + + + + + aes-128-cbc + + + sha1 + + + + + + + + + + + + + real-time + + + high + + + high + + + medium + + + medium + + + low + + + low + + + low + + + + + + + + + + + + no + + + 1.25 + 0.5 + 900 + 300 + 900 + yes + + + + + + + + + + + yes + yes + 4 + + + + + + yes + + + yes + ${trust_ip} + 65001 + yes + + + + + yes + original + use-self + + + + + + 10.0.2.10 + + + + 0 + yes + + + 0 + yes + + 0 + 30 + 0 + 90 + 15 + 30 + + + yes + no + + + ethernet1/2 + + + Inherit-vr-global-setting + + 5000 + yes + 65000 + no + ipv4 + yes + non-client + unspecified + + + + 10.0.2.11 + + + + 0 + yes + + + 0 + yes + + 0 + 30 + 0 + 90 + 15 + 30 + + + yes + no + + + ethernet1/2 + + + Inherit-vr-global-setting + + 5000 + yes + 65000 + no + ipv4 + yes + non-client + unspecified + + + yes + no + yes + + + no + yes + + + ipv4 + yes + incomplete + + + + + + + + + + + + + + + + + + + + + + + unicast + + + NCC + + yes + + + + + + + no + + + no + + + no + + + + + + static + + + 0.0.0.0/0 + + + 1 + + + + + + + + ethernet1/1 + ethernet1/2 + loopback.1 + + + + + + + ${gateway_trust} + + + None + + + no + any + 2 + + ethernet1/2 + 10 + 10.0.0.0/8 + + + + + + + ${gateway_trust} + + + None + + + no + any + 2 + + ethernet1/2 + 10 + 172.16.0.0/12 + + + + + + + ${gateway_trust} + + + None + + + no + any + 2 + + ethernet1/2 + 10 + 192.168.0.0/16 + + + + + + + no + any + 2 + + + ${gateway_trust} + + + None + + ethernet1/2 + 10 + 35.191.0.0/16 + + + + + + + no + any + 2 + + + ${gateway_trust} + + + None + + ethernet1/2 + 10 + 130.211.0.0/22 + + + + + + + no + any + 2 + + + ${gateway_untrust} + + + None + + ethernet1/1 + 10 + 35.191.0.0/16 + + + + + + + no + any + 2 + + + ${gateway_untrust} + + + None + + ethernet1/1 + 10 + 130.211.0.0/22 + + + + + + + + + + + + + + + yes + no + yes + yes + + + updates.paloaltonetworks.com + + + + + 5 + no + download-and-install + + + + + + + 0 + download-and-install + + + + + + + 0 + download-and-install + + + + + US/Pacific + + yes + yes + + PA-VM + + + 169.254.169.254 + 8.8.8.8 + + + + no + no + no + + + no + + + + + yes + + + FQDN + + c3NoLXJzYSBBQUFBQjNOemFDMXljMkVBQUFBREFRQUJBQUFCZ1FEcVBpNkJmWUphYVMrVnJyOHVvWFhoK1JxRU9NY1hZTTU5czBuc2c1NlkyRGlUMkJVNktQQlRGb0JzOTdTMytYUC9hbmk2Qmd6QlpyZVNOeUNpSWV6UHhDNG5KQm83U25PbUlvM1BmaWJZK2d1UnJ5SVBHU29WSXJSemdjVmQvbmZQZGtCOUlrNEo2QVcweDVSOC9EUVhBa2UvckM4ZDBiRnNsSGNJc1h3K2ZSaU5LSzRLdEovS05IYlNOL05panY0ZkZZT3I2b051UmdXUVBYNDg4NTJNT0IrWTh1NUZsc2F0QXQrKzQvTkMvS0dtazFrRTVlTS9IcVhWZ1RtbDBPS2Q5UEhBRnFMMnFCcWk5Q21XbTN3WG1Leis4elpCdEY5dUpqWDJqbmN6YUtkTEJlYi9tRFFBbDBKYlJnc0l2NFpLTDhKQThqbGR4aTZUMEhXRU02dWV1VUwzVXNaUXdOVld5ZGcrbzRaTXYvSm1HdzFpMlk2OG8zT0NORjB2SmNQajdkcWF3RW9TVWxrNnd6clZxWW9mY3N6TkhZUFNQL045ZUpYUWdBWnRjWURUcmo3K1dUakdvMDJ5NDArZXN4QXM1enZUTS9HUzdUQVl4OWFmcTRzdk4waWpHK05MUUwwVjUrRDQwcXNVNTB1RVpTQXZMR0dVQkF1QW93Umt2cms9IG1tY2xpbWFuc0BNLUNUN0hMNFhYN0o= + + + yes + no + yes + yes + + + 169.254.169.254 + 8.8.8.8 + mgmt-interface-swap + + + + + + + yes + 1 + + + + + + + + + + + + + ethernet1/1 + + + + + + + ethernet1/2 + + + + + + + loopback.1 + + + + + + + + + 22 + + + + + + + + + + 80 + + + + + + + + + + 8080 + + + + + + + + + + 5001 + + + + + + + + + + + + + + + + + ethernet1/2 + + + + + spoke1-vm1 + + + untrust + + + untrust + + + any + + + any + + tcp-8080 + Inbound DNAT/SNAT policy to spoke1 web VM. + ethernet1/1 + + + + + + ethernet1/2 + + + + + spoke1-vm1 + 80 + + + untrust + + + untrust + + + any + + + any + + tcp-80 + Inbound DNAT/SNAT policy to web VM. + ethernet1/1 + + + + + + ethernet1/2 + + + + + spoke2-vm1 + 22 + + + untrust + + + untrust + + + any + + + any + + tcp-22 + Inbound DNAT/SNAT policy to VM in spoke2. + ethernet1/1 + + + + loopback + + + untrust + + + untrust + + + lb-checks + + + any + + any + ethernet1/1 + + + + loopback + + + trust + + + trust + + + lb-checks + + + any + + any + ethernet1/2 + + + + + + ethernet1/1 + + + + + untrust + + + trust + + + any + + + any + + any + + + + + + + + trust + + + untrust + + + any + + + any + + + any + + + any + + + web-browsing + + + service-http + + + any + + + any + + allow + + + + alert-all + + + alert-all + + + alert-all + + + alert-all + + + default + + + default + + + + + + + trust + + + untrust + + + any + + + any + + + any + + + any + + + ssh + + + any + + + any + + + any + + allow + + + + alert-all + + + alert-all + + + default + + + alert-all + + + alert-all + + + default + + + + + + + trust + + + trust + + + any + + + any + + + any + + + any + + + any + + + tcp-5001 + + + any + + + any + + allow + universal + Security policy to test session resiliency with Google Cloud Memorystore for Redis. + yes + + + + trust + + + trust + + + spoke1-vpc + spoke2-vpc + + + spoke1-vpc + spoke2-vpc + + + any + + + any + + + any + + + application-default + + + any + + + any + + allow + universal + Generic security policy to allow all east-west traffic between the spoke VPC networks. + + + + alert-all + + + alert-all + + + alert-all + + + alert-all + + + default + + + default + + + + + + + untrust + + + trust + + + spoke1-vpc + spoke2-vpc + + + any + + + any + + + any + + + apt-get + dns + google-base + ntp + ssl + web-browsing + + + application-default + + + any + + + any + + allow + universal + Generic security policy to allow outbound internet traffic from the spoke VPC networks. + + + + alert-all + + + alert-all + + + alert-all + + + alert-all + + + default + + + default + + + + + + + lb-checks + + + any + + + lb-checks + + + any + + + any + + + any + + + web-browsing + ssl + + + application-default + + + any + + + any + + allow + universal + Security policy to allow load balancer health checks to verify VM-Series availability. + + + + any + + + any + + + any + + + any + + + any + + + any + + + ping + traceroute + + + application-default + + + any + + + any + + allow + universal + Generic security policy to allow all ping and traceroute traffic. + + + + trust + + + trust + + + any + + + any + + + any + + + any + + + bgp + + + any + + + any + + + any + + allow + no + yes + + + + + + + deny + no + yes + + + deny + no + yes + + + + + + + + ethernet1/1 + ethernet1/2 + loopback.1 + + + +
+ + 100.64.0.1 + Loopback for ILB health-checks + + internal-ip + + + + 35.191.0.0/16 + + google-ip + + Google HTTPS & TCP load balancer health probe range. + + + 130.211.0.0/22 + + google-ip + + Google HTTPS load balancer health probe range. + + + ${spoke1_vm1_ip} + + internal-ip + + Internal load balancer in spoke1 VPC that frontends web VMs. + + + ${spoke1_cidr} + + internal-ip + + + + ${spoke2_cidr} + + internal-ip + + + + ${spoke2_vm1_ip} + + internal-ip + + + + 209.85.152.0/22 + + google-ip + + Google TCP load balancer health probe range. + + + 209.85.204.0/22 + + google-ip + + Google TCP load balancer health probe range. + + + 169.254.169.254 + + google-ip + + Target pool-based network load balancers might proxy health checks through the metadata server (169.254.169.254). + +
+ + + color6 + + + color13 + + + color15 + + + color25 + + + color15 + + + + + + google-metadata-server + lb-check-1 + lb-check-2 + lb-check-3 + lb-check-4 + + + google-ip + + Google TCP load balancer health probe range. + + + + + + + + + + + + any + + + any + + + any + + any + any + any + disable + + + + + + + + + + + + + disable + + + + + default + default + disable + + + default + default + disable + + + default + default + disable + + + default + default + disable + + + default + default + disable + + + default + default + disable + + + default + default + disable + + + default + default + disable + + + + pan-sinkhole-default-ip + ::1 + + + + + + + + + any + + any + any + disable + + + + + + + + + + + medium + + + abortion + abused-drugs + adult + alcohol-and-tobacco + auctions + business-and-economy + command-and-control + computer-and-internet-info + content-delivery-networks + copyright-infringement + cryptocurrency + dating + dynamic-dns + educational-institutions + entertainment-and-arts + extremism + financial-services + gambling + games + government + grayware + hacking + health-and-medicine + high-risk + home-and-garden + hunting-and-fishing + insufficient-content + internet-communications-and-telephony + internet-portals + job-search + legal + low-risk + malware + medium-risk + military + motor-vehicles + music + newly-registered-domain + news + not-resolved + nudity + online-storage-and-backup + parked + peer-to-peer + personal-sites-and-blogs + philosophy-and-political-advocacy + phishing + proxy-avoidance-and-anonymizers + questionable + real-estate + real-time-detection + recreation-and-hobbies + reference-and-research + religion + search-engines + sex-education + shareware-and-freeware + shopping + social-networking + society + sports + stock-advice-and-tools + streaming-media + swimsuits-and-intimate-apparel + training-and-tools + translation + travel + unknown + weapons + web-advertisements + web-based-email + web-hosting + + yes + no + + + + + + + + any + + + any + + both + alert + + + + + +
+
+
+
+
\ No newline at end of file diff --git a/bgp_ncc_star_tutorial/bootstrap_files/init-cfg.txt b/bgp_ncc_star_tutorial/bootstrap_files/init-cfg.txt new file mode 100644 index 0000000..17a7aa8 --- /dev/null +++ b/bgp_ncc_star_tutorial/bootstrap_files/init-cfg.txt @@ -0,0 +1,11 @@ +type=dhcp-client +ip-address= +default-gateway= +netmask= +ipv6-address= +ipv6-default-gateway= +dhcp-accept-server-hostname=yes +dhcp-accept-server-domain=yes +dns-primary=169.254.169.254 +dns-secondary=8.8.8.8 +op-command-modes=mgmt-interface-swap \ No newline at end of file diff --git a/bgp_ncc_star_tutorial/images/diagram_egress.png b/bgp_ncc_star_tutorial/images/diagram_egress.png new file mode 100644 index 0000000..c4b7890 Binary files /dev/null and b/bgp_ncc_star_tutorial/images/diagram_egress.png differ diff --git a/bgp_ncc_star_tutorial/images/diagram_ingress.png b/bgp_ncc_star_tutorial/images/diagram_ingress.png new file mode 100644 index 0000000..7aaaf26 Binary files /dev/null and b/bgp_ncc_star_tutorial/images/diagram_ingress.png differ diff --git a/bgp_ncc_star_tutorial/images/diagram_main.png b/bgp_ncc_star_tutorial/images/diagram_main.png new file mode 100644 index 0000000..578f77b Binary files /dev/null and b/bgp_ncc_star_tutorial/images/diagram_main.png differ diff --git a/bgp_ncc_star_tutorial/images/ss01.png b/bgp_ncc_star_tutorial/images/ss01.png new file mode 100644 index 0000000..fe1d163 Binary files /dev/null and b/bgp_ncc_star_tutorial/images/ss01.png differ diff --git a/bgp_ncc_star_tutorial/images/ss02.png b/bgp_ncc_star_tutorial/images/ss02.png new file mode 100644 index 0000000..75ae683 Binary files /dev/null and b/bgp_ncc_star_tutorial/images/ss02.png differ diff --git a/bgp_ncc_star_tutorial/images/ss03.png b/bgp_ncc_star_tutorial/images/ss03.png new file mode 100644 index 0000000..c3940c2 Binary files /dev/null and b/bgp_ncc_star_tutorial/images/ss03.png differ diff --git a/bgp_ncc_star_tutorial/main.tf b/bgp_ncc_star_tutorial/main.tf new file mode 100644 index 0000000..53e6718 --- /dev/null +++ b/bgp_ncc_star_tutorial/main.tf @@ -0,0 +1,36 @@ +terraform { + required_version = ">= 1.6.0" + + required_providers { + google = { + source = "hashicorp/google" + version = "= 6.48.0" + } + google-beta = { + source = "hashicorp/google-beta" + version = "= 6.48.0" + } + random = { + source = "hashicorp/random" + version = "= 3.7.2" + } + } +} + +provider "google" { + project = var.project_id + region = var.region +} + +provider "google-beta" { + project = var.project_id + region = var.region +} + + +data "google_compute_zones" "main" {} + + +locals { + prefix = var.prefix != null && var.prefix != "" ? "${var.prefix}-" : "" +} \ No newline at end of file diff --git a/bgp_ncc_star_tutorial/ncc.tf b/bgp_ncc_star_tutorial/ncc.tf new file mode 100644 index 0000000..d7e521e --- /dev/null +++ b/bgp_ncc_star_tutorial/ncc.tf @@ -0,0 +1,155 @@ +# ------------------------------------------------------------------------------------- +# Create NCC hub with edge and center groups for the trust VPC network. +# ------------------------------------------------------------------------------------- + +// Create NCC hub in star topology +resource "google_network_connectivity_hub" "main" { + count = (var.configure_ncc ? 1 : 0) + name = "${local.prefix}panw-hub" + preset_topology = "STAR" +} + +// Create NCC center group for VM-Series +resource "google_network_connectivity_group" "center" { + count = (var.configure_ncc ? 1 : 0) + hub = google_network_connectivity_hub.main[0].id + name = "center" + auto_accept { + auto_accept_projects = [ + var.project_id + ] + } +} + +// Create NCC edge group for VM-Series +resource "google_network_connectivity_group" "edge" { + count = (var.configure_ncc ? 1 : 0) + hub = google_network_connectivity_hub.main[0].id + name = "edge" + auto_accept { + auto_accept_projects = [ + var.project_id + ] + } +} + +module "trust_router" { + count = (var.configure_ncc ? 1 : 0) + source = "terraform-google-modules/cloud-router/google" + version = "~> 7.1" + name = "cr-trust" + region = var.region + bgp = { + asn = 65000 + advertised_groups = ["ALL_SUBNETS"] + } + project = var.project_id + network = google_compute_network.trust.name +} + +// Create primary BGP interfaces on the cloud router +resource "google_compute_router_interface" "trust_nic0" { + count = (var.configure_ncc ? var.vmseries_count : 0) + name = "trust-to-ncc-${count.index + 1}-primary" + router = module.trust_router[0].router.name + region = module.trust_router[0].router.region + subnetwork = google_compute_subnetwork.trust.self_link + private_ip_address = cidrhost(var.cidr_trust, 10 + (count.index * 2)) + redundant_interface = google_compute_router_interface.trust_nic1[count.index].name +} + +// Create backup BGP interfaces on the cloud router +resource "google_compute_router_interface" "trust_nic1" { + count = (var.configure_ncc ? var.vmseries_count : 0) + name = "trust-to-ncc-${count.index + 1}-backup" + router = module.trust_router[0].router.name + region = module.trust_router[0].router.region + subnetwork = google_compute_subnetwork.trust.self_link + private_ip_address = cidrhost(var.cidr_trust, 11 + (count.index * 2)) +} + +// Create primary BGP peers using the primary cloud router interface +resource "google_compute_router_peer" "trust_primary" { + count = (var.configure_ncc ? var.vmseries_count : 0) + name = "trust-peer-${count.index + 1}-primary" + router = module.trust_router[0].router.name + region = var.region + peer_asn = 65001 + router_appliance_instance = google_compute_instance_from_template.vmseries[count.index].id + peer_ip_address = google_compute_address.vmseries_trust[count.index].address + interface = google_compute_router_interface.trust_nic0[count.index].name + advertised_route_priority = 100 + + depends_on = [ + google_compute_instance_from_template.vmseries, + google_compute_address.vmseries_trust, + google_compute_router_interface.trust_nic0, + google_network_connectivity_spoke.trust + ] +} + +// Create backup BGP peers using the backup cloud router interface +resource "google_compute_router_peer" "trust_backup" { + count = (var.configure_ncc ? var.vmseries_count : 0) + name = "trust-peer-${count.index + 1}-backup" + router = module.trust_router[0].router.name + region = var.region + peer_asn = 65001 + router_appliance_instance = google_compute_instance_from_template.vmseries[count.index].id + peer_ip_address = google_compute_address.vmseries_trust[count.index].address + interface = google_compute_router_interface.trust_nic1[count.index].name + advertised_route_priority = 110 + + depends_on = [ + google_compute_instance_from_template.vmseries, + google_compute_address.vmseries_trust, + google_compute_router_interface.trust_nic1, + google_network_connectivity_spoke.trust + ] +} // Create NCC spoke for trust VPC with router appliance instances +resource "google_network_connectivity_spoke" "trust" { + count = (var.configure_ncc ? 1 : 0) + name = "${local.prefix}trust" + location = var.region + group = google_network_connectivity_group.center[0].id + hub = google_network_connectivity_hub.main[0].id + + linked_router_appliance_instances { + dynamic "instances" { + for_each = range(var.vmseries_count) + content { + virtual_machine = google_compute_instance_from_template.vmseries[instances.value].id + ip_address = google_compute_address.vmseries_trust[instances.value].address + } + } + site_to_site_data_transfer = true + } +} + +// Create NCC spoke for spoke1-vpc +resource "google_network_connectivity_spoke" "spoke1" { + count = (var.configure_ncc ? 1 : 0) + name = "${local.prefix}spoke1" + location = "global" + group = google_network_connectivity_group.edge[0].id + hub = google_network_connectivity_hub.main[0].id + + linked_vpc_network { + uri = google_compute_network.spoke1.self_link + } +} + +// Create NCC spoke for spoke2-vpc +resource "google_network_connectivity_spoke" "spoke2" { + count = (var.configure_ncc ? 1 : 0) + name = "${local.prefix}spoke2" + location = "global" + group = google_network_connectivity_group.edge[0].id + hub = google_network_connectivity_hub.main[0].id + + linked_vpc_network { + uri = google_compute_network.spoke2.self_link + } +} + + diff --git a/bgp_ncc_star_tutorial/outputs.tf b/bgp_ncc_star_tutorial/outputs.tf new file mode 100644 index 0000000..338970d --- /dev/null +++ b/bgp_ncc_star_tutorial/outputs.tf @@ -0,0 +1,72 @@ +output "EXTERNAL_LB_IP" { + value = google_compute_address.extlb.address +} + +output "VMSERIES_UNTRUST_IPS" { + description = "Static internal IP addresses for VM-Series untrust interfaces" + value = { + for i, addr in google_compute_address.vmseries_untrust : + "vmseries-${i + 1}" => addr.address + } +} + +output "VMSERIES_MGMT_IPS" { + description = "Static internal IP addresses for VM-Series management interfaces" + value = { + for i, addr in google_compute_address.vmseries_mgmt : + "vmseries-${i + 1}" => addr.address + } +} + +output "VMSERIES_TRUST_IPS" { + description = "Static internal IP addresses for VM-Series trust interfaces" + value = { + for i, addr in google_compute_address.vmseries_trust : + "vmseries-${i + 1}" => addr.address + } +} + +output "IP_ALLOCATION_SUMMARY" { + description = "Complete IP allocation summary to avoid conflicts" + value = { + "subnet_gateways" = { + "untrust" = cidrhost(var.cidr_untrust, 1) + "mgmt" = cidrhost(var.cidr_mgmt, 1) + "trust" = cidrhost(var.cidr_trust, 1) + } + "bgp_configuration" = { + "router_interfaces_primary" = { + for i in range(var.vmseries_count) : + "interface-${i + 1}-primary" => cidrhost(var.cidr_trust, 10 + (i * 2)) + } + "router_interfaces_backup" = { + for i in range(var.vmseries_count) : + "interface-${i + 1}-backup" => cidrhost(var.cidr_trust, 11 + (i * 2)) + } + "vmseries_trust_ips" = { + for i in range(var.vmseries_count) : + "vmseries-${i + 1}" => cidrhost(var.cidr_trust, 2 + i) + } + "bgp_peers" = { + for i in range(var.vmseries_count) : + "vmseries-${i + 1}" => { + "primary" = "Router ${cidrhost(var.cidr_trust, 10 + (i * 2))} <-> VM-Series ${cidrhost(var.cidr_trust, 2 + i)} (Priority 100)" + "backup" = "Router ${cidrhost(var.cidr_trust, 11 + (i * 2))} <-> VM-Series ${cidrhost(var.cidr_trust, 2 + i)} (Priority 110)" + } + } + } + "vmseries_ip_ranges" = { + "untrust_start" = cidrhost(var.cidr_untrust, 10) + "untrust_end" = cidrhost(var.cidr_untrust, 9 + var.vmseries_count) + "mgmt_start" = cidrhost(var.cidr_mgmt, 10) + "mgmt_end" = cidrhost(var.cidr_mgmt, 9 + var.vmseries_count) + "trust_start" = cidrhost(var.cidr_trust, 10) + "trust_end" = cidrhost(var.cidr_trust, 9 + var.vmseries_count) + } + "available_ranges" = { + "untrust_available" = "${cidrhost(var.cidr_untrust, 2)} - ${cidrhost(var.cidr_untrust, 9)}" + "mgmt_available" = "${cidrhost(var.cidr_mgmt, 2)} - ${cidrhost(var.cidr_mgmt, 9)}" + "trust_available" = "${cidrhost(var.cidr_trust, 2)} - ${cidrhost(var.cidr_trust, 9)}" + } + } +} diff --git a/bgp_ncc_star_tutorial/spokes.tf b/bgp_ncc_star_tutorial/spokes.tf new file mode 100644 index 0000000..9a0dfb4 --- /dev/null +++ b/bgp_ncc_star_tutorial/spokes.tf @@ -0,0 +1,130 @@ +# ------------------------------------------------------------------------------------- +# Create Spoke1 VPC +# ------------------------------------------------------------------------------------- + +// Create spoke1 VPC +resource "google_compute_network" "spoke1" { + name = "${local.prefix}spoke1-vpc" + auto_create_subnetworks = false + delete_default_routes_on_create = true +} + +// Create spoke1 subnetwork +resource "google_compute_subnetwork" "spoke1_subnet1" { + name = "${local.prefix}${var.region}-spoke1" + ip_cidr_range = var.cidr_spoke1 + region = var.region + network = google_compute_network.spoke1.id + +} + +// Allow all intra-VPC traffic within the spoke1 VPC +resource "google_compute_firewall" "spoke1" { + name = "${local.prefix}spoke1-ingress" + network = google_compute_network.spoke1.name + source_ranges = ["0.0.0.0/0"] + + allow { + protocol = "all" + ports = [] + } +} + +// Create spoke1 VM for testing +resource "google_compute_instance" "spoke1_vm1" { + name = "${local.prefix}spoke1-vm1" + machine_type = "f1-micro" + zone = data.google_compute_zones.main.names[0] + can_ip_forward = false + allow_stopping_for_update = true + + metadata = { + serial-port-enable = true + ssh-keys = "paloalto:${file(var.public_key_path)}" + } + + network_interface { + subnetwork = google_compute_subnetwork.spoke1_subnet1.self_link + network_ip = cidrhost(var.cidr_spoke1, 10) + } + + boot_disk { + initialize_params { + image = "https://www.googleapis.com/compute/v1/projects/panw-gcp-team-testing/global/images/ubuntu-2004-lts-jenkins" + } + } + + service_account { + scopes = [ + "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" + ] + } +} + +# ------------------------------------------------------------------------------------- +# Create Spoke2 VPC +# ------------------------------------------------------------------------------------- + +// Create spoke2 VPC +resource "google_compute_network" "spoke2" { + name = "${local.prefix}spoke2-vpc" + auto_create_subnetworks = false + delete_default_routes_on_create = true +} + +// Create spoke2 subnetwork +resource "google_compute_subnetwork" "spoke2_subnet1" { + name = "${local.prefix}${var.region}-spoke2" + ip_cidr_range = var.cidr_spoke2 + region = var.region + network = google_compute_network.spoke2.id +} + +// Allow all intra-VPC traffic within the spoke2 VPC +resource "google_compute_firewall" "spoke2" { + name = "${local.prefix}spoke2-ingress" + network = google_compute_network.spoke2.name + source_ranges = ["0.0.0.0/0"] + + allow { + protocol = "all" + ports = [] + } +} + +// Create spoke2 VM for testing +resource "google_compute_instance" "spoke2_vm1" { + name = "${local.prefix}spoke2-vm1" + machine_type = "f1-micro" + zone = data.google_compute_zones.main.names[0] + can_ip_forward = false + allow_stopping_for_update = true + + metadata = { + serial-port-enable = true + ssh-keys = "paloalto:${file(var.public_key_path)}" + } + + network_interface { + subnetwork = google_compute_subnetwork.spoke2_subnet1.self_link + network_ip = cidrhost(var.cidr_spoke2, 10) + } + + boot_disk { + initialize_params { + image = "ubuntu-os-cloud/ubuntu-2204-lts" + } + } + + service_account { + scopes = [ + "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" + ] + } +} diff --git a/bgp_ncc_star_tutorial/variables.tf b/bgp_ncc_star_tutorial/variables.tf new file mode 100644 index 0000000..1d1fdee --- /dev/null +++ b/bgp_ncc_star_tutorial/variables.tf @@ -0,0 +1,102 @@ +# ------------------------------------------------------------------------------------- +# Required variables +# ------------------------------------------------------------------------------------- + +variable "project_id" { + type = string + description = "GCP Project ID for the deployment." +} + +variable "region" { + type = string + description = "GCP region for the deployment." +} + +variable "public_key_path" { + type = string + 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`" +} + +variable "vmseries_mgmt_ips" { + type = list(string) + 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." +} + +variable "configure_ncc" { + type = bool + description = "If set to true, ncc.tf will be executed and the NCC hub, groups, and spokes will be created automatically." +} + + +# ------------------------------------------------------------------------------------- +# Optional variables +# ------------------------------------------------------------------------------------ + +variable "prefix" { + type = string + description = "Prefix to add to GCP resource names, an arbitrary string" + default = null +} + +variable "cidr_mgmt" { + type = string + description = "The CIDR range of the management subnetwork." + default = "10.0.0.0/24" +} + +variable "cidr_untrust" { + type = string + description = "The CIDR range of the untrust subnetwork." + default = "10.0.1.0/24" +} + +variable "cidr_trust" { + type = string + description = "The CIDR range of the trust subnetwork." + default = "10.0.2.0/24" +} + +variable "cidr_spoke1" { + type = string + description = "The CIDR range of the spoke1 subnetwork." + default = "10.1.0.0/24" +} + +variable "cidr_spoke2" { + type = string + description = "The CIDR range of the spoke2 subnetwork." + default = "10.2.0.0/24" +} + +variable "vmseries_image" { + type = string + description = "Name of the VM-Series image within the paloaltonetworksgcp-public project. To list available images, run: `gcloud compute images list --project paloaltonetworksgcp-public --no-standard-images`. If you are using a custom image in a different project, please update `local.vmseries_iamge_url` in `main.tf`." + default = "vmseries-flex-bundle2-1022h2" +} + +variable "vmseries_machine_type" { + type = string + description = "The machine shape for the VM-Series instance (N2 and E2 instances are supported)." + default = "n2-standard-4" +} + +variable "vmseries_count" { + type = string + description = "The minimum number of firewalls to scale up to during scaling event." + default = 1 +} + +variable "vmseries_roles" { + type = set(string) + description = "Roles to assign to the firewall's service account." + default = [ + "roles/compute.networkViewer", + "roles/logging.logWriter", + "roles/monitoring.metricWriter", + "roles/monitoring.viewer", + "roles/viewer", + "roles/stackdriver.accounts.viewer", + "roles/stackdriver.resourceMetadata.writer", + ] +} + diff --git a/bgp_ncc_star_tutorial/vmseries.tf b/bgp_ncc_star_tutorial/vmseries.tf new file mode 100644 index 0000000..d429856 --- /dev/null +++ b/bgp_ncc_star_tutorial/vmseries.tf @@ -0,0 +1,403 @@ +# ------------------------------------------------------------------------------------- +# Create management and dataplane VPCs, subnets, and firewall rules. +# ------------------------------------------------------------------------------------- + +// Create management VPC +resource "google_compute_network" "mgmt" { + name = "${local.prefix}mgmt" + auto_create_subnetworks = false +} + +// Create untrust VPC +resource "google_compute_network" "untrust" { + name = "${local.prefix}untrust" + auto_create_subnetworks = false +} + +// Create trust VPC +resource "google_compute_network" "trust" { + name = "${local.prefix}trust" + auto_create_subnetworks = false +} + +// Create management subnet +resource "google_compute_subnetwork" "mgmt" { + name = "${local.prefix}${var.region}-mgmt" + ip_cidr_range = var.cidr_mgmt + region = var.region + network = google_compute_network.mgmt.id +} + +// Create untrust subnet +resource "google_compute_subnetwork" "untrust" { + name = "${local.prefix}${var.region}-untrust" + ip_cidr_range = var.cidr_untrust + region = var.region + network = google_compute_network.untrust.id +} + +// Create trust subnet +resource "google_compute_subnetwork" "trust" { + name = "${local.prefix}${var.region}-trust" + ip_cidr_range = var.cidr_trust + region = var.region + network = google_compute_network.trust.id +} + +// Firewall rule to allow management access +resource "google_compute_firewall" "mgmt" { + name = "${local.prefix}mgmt" + network = google_compute_network.mgmt.name + source_ranges = var.vmseries_mgmt_ips + + allow { + protocol = "tcp" + ports = ["443", "22", "3978"] + } +} + +// Allow all traffic to firewall's untrust VPC +resource "google_compute_firewall" "untrust" { + name = "${local.prefix}untrust" + network = google_compute_network.untrust.name + source_ranges = ["0.0.0.0/0"] + + allow { + protocol = "all" + ports = [] + } +} + +// Allow all traffic to firewall's trust VPC +resource "google_compute_firewall" "trust" { + name = "${local.prefix}trust" + network = google_compute_network.trust.name + source_ranges = ["0.0.0.0/0"] + + allow { + protocol = "all" + ports = [] + } +} + + +# ------------------------------------------------------------------------------------- +# Create Cloud NAT for untrust VPC. +# ------------------------------------------------------------------------------------- + +// Create cloud router for cloud NAT. +resource "google_compute_router" "untrust" { + name = "${local.prefix}${var.region}untrust-router" + network = google_compute_network.untrust.id +} + +// Create cloud NAT for outbound internet access. +resource "google_compute_router_nat" "untrust" { + name = "${local.prefix}untrust-nat" + router = google_compute_router.untrust.name + region = var.region + nat_ip_allocate_option = "AUTO_ONLY" + source_subnetwork_ip_ranges_to_nat = "ALL_SUBNETWORKS_ALL_IP_RANGES" +} + + +# ------------------------------------------------------------------------------------- +# Create bootstrap bucket for VM-Series +# ------------------------------------------------------------------------------------- + +// Retrieve untrust subnet +data "google_compute_subnetwork" "untrust" { + self_link = google_compute_subnetwork.untrust.self_link + region = var.region +} + +// Retrieve trust subnet +data "google_compute_subnetwork" "trust" { + self_link = google_compute_subnetwork.trust.self_link + region = var.region +} + +// Create the bootstrap.xml file from bootstrap.template +resource "local_file" "bootstrap" { + filename = "bootstrap_files/bootstrap.xml" + content = templatefile("bootstrap_files/bootstrap.template", { + gateway_trust = data.google_compute_subnetwork.trust.gateway_address + gateway_untrust = data.google_compute_subnetwork.untrust.gateway_address + spoke1_cidr = var.cidr_spoke1 + spoke2_cidr = var.cidr_spoke2 + spoke1_vm1_ip = cidrhost(var.cidr_spoke1, 10) + spoke2_vm1_ip = cidrhost(var.cidr_spoke2, 10) + trust_ip = google_compute_address.vmseries_trust[0].address // First VM-Series trust interface IP + }) +} + + +// Create service account for firewall +resource "google_service_account" "vmseries" { + account_id = "${local.prefix}panw-sa" +} + +// Add roles to service account +resource "google_project_iam_member" "vmseries" { + for_each = var.vmseries_roles + project = var.project_id + role = each.value + member = "serviceAccount:${google_service_account.vmseries.email}" +} + +// Create the GCS bootstrap bucket with local firewall config (bootstrap.xml). +module "bootstrap" { + source = "PaloAltoNetworks/swfw-modules/google//modules/bootstrap" + version = "~> 2.0" + service_account = google_service_account.vmseries.email + location = "US" + + files = { + "bootstrap_files/init-cfg.txt" = "config/init-cfg.txt" + "${local_file.bootstrap.filename}" = "config/bootstrap.xml" + "bootstrap_files/authcodes" = "license/authcodes" + } +} + + +# ------------------------------------------------------------------------------------- +# Create firewall service account, instance template, MIG, and autoscaler. +# ------------------------------------------------------------------------------------- + +// Create instance template for the firewall +resource "google_compute_instance_template" "vmseries" { + name_prefix = "${local.prefix}panw-template" + machine_type = var.vmseries_machine_type + min_cpu_platform = "Intel Cascade Lake" + tags = ["vmseries-tutorial"] + can_ip_forward = true + + metadata = { + type = "dhcp-client" + dhcp-send-client-id = "yes" + dhcp-accept-server-hostname = "yes" + dhcp-accept-server-domain = "yes" + dns-primary = "169.254.169.254" + vmseries-bootstrap-gce-storagebucket = module.bootstrap.bucket_name + ssh-keys = "admin:${file(var.public_key_path)}" + } + + network_interface { + subnetwork = google_compute_subnetwork.untrust.id + } + + network_interface { + subnetwork = google_compute_subnetwork.mgmt.id + access_config {} + } + + network_interface { + subnetwork = google_compute_subnetwork.trust.id + } + + disk { + source_image = "https://www.googleapis.com/compute/v1/projects/paloaltonetworksgcp-public/global/images/${var.vmseries_image}" + disk_type = "pd-ssd" + auto_delete = true + boot = true + } + + lifecycle { + create_before_destroy = true + } + + service_account { + email = google_service_account.vmseries.email + scopes = [ + "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", + ] + } + + depends_on = [ + module.bootstrap, + google_compute_router_nat.untrust + ] +} + +# ------------------------------------------------------------------------------------- +# Create static IP addresses for VM-Series instances +# IP Allocation Strategy for /28 subnets (16 IPs: .0-.15): +# - Untrust: 10.0.1.10, 10.0.1.11, 10.0.1.12, ... (starting from .10) +# - Mgmt: 10.0.0.10, 10.0.0.11, 10.0.0.12, ... (starting from .10) +# - Trust: 10.0.2.2, 10.0.2.3, 10.0.2.4, ... (starting from .2) +# +# Trust Subnet Reserved IPs (10.0.2.0/28): +# - .0 = Network address (reserved) +# - .1 = Subnet gateway (Google Cloud reserved) +# - .2-.9 = VM-Series trust interfaces (up to 8 instances) +# - .10+ = BGP router interfaces (redundant pairs) +# - VM-Series 1: Primary .10, Backup .11 +# - VM-Series 2: Primary .12, Backup .13 +# - VM-Series 3: Primary .14, Backup .15 +# +# BGP Configuration (High Availability): +# - Each VM-Series gets 2 BGP sessions (primary + backup) +# - Primary interface: Priority 100 (preferred) +# - Backup interface: Priority 110 (fallback) +# - Uses router_appliance_instance to link router peers to VM-Series instances +# ------------------------------------------------------------------------------------- + +// Create static IP addresses for untrust interface +resource "google_compute_address" "vmseries_untrust" { + count = var.vmseries_count + name = "${local.prefix}panw-vmseries-${count.index + 1}-untrust" + subnetwork = google_compute_subnetwork.untrust.id + address_type = "INTERNAL" + address = cidrhost(var.cidr_untrust, 10 + count.index) + region = var.region +} + +// Create static IP addresses for mgmt interface +resource "google_compute_address" "vmseries_mgmt" { + count = var.vmseries_count + name = "${local.prefix}panw-vmseries-${count.index + 1}-mgmt" + subnetwork = google_compute_subnetwork.mgmt.id + address_type = "INTERNAL" + address = cidrhost(var.cidr_mgmt, 10 + count.index) + region = var.region +} + +// Create static IP addresses for trust interface +resource "google_compute_address" "vmseries_trust" { + count = var.vmseries_count + name = "${local.prefix}panw-vmseries-${count.index + 1}-trust" + subnetwork = google_compute_subnetwork.trust.id + address_type = "INTERNAL" + address = cidrhost(var.cidr_trust, 2 + count.index) + region = var.region +} + +# ------------------------------------------------------------------------------------- +# Create VM instances from template and Unmanaged Instance Group (UMIG) +# ------------------------------------------------------------------------------------- + +// Create VM instance from template +resource "google_compute_instance_from_template" "vmseries" { + count = var.vmseries_count + name = "${local.prefix}panw-vmseries-${count.index + 1}" + zone = data.google_compute_zones.available.names[count.index % length(data.google_compute_zones.available.names)] + + source_instance_template = google_compute_instance_template.vmseries.id + + // Override any template settings if needed + can_ip_forward = true + + // Override network interfaces with static IP addresses + network_interface { + subnetwork = google_compute_subnetwork.untrust.id + network_ip = google_compute_address.vmseries_untrust[count.index].address + } + + network_interface { + subnetwork = google_compute_subnetwork.mgmt.id + network_ip = google_compute_address.vmseries_mgmt[count.index].address + access_config { + // Ephemeral public IP + } + } + + network_interface { + subnetwork = google_compute_subnetwork.trust.id + network_ip = google_compute_address.vmseries_trust[count.index].address + } + + // Add labels for identification + labels = { + environment = "tutorial" + role = "firewall" + instance = tostring(count.index + 1) + } +} + +// Get available zones +data "google_compute_zones" "available" { + region = var.region +} + +// Create Unmanaged Instance Groups (one per zone) +resource "google_compute_instance_group" "vmseries" { + for_each = toset(data.google_compute_zones.available.names) + name = "${local.prefix}panw-umig-${each.key}" + description = "Unmanaged instance group for VM-Series firewalls in zone ${each.key}" + zone = each.key + + instances = [ + for instance in google_compute_instance_from_template.vmseries : + instance.self_link if instance.zone == each.key + ] + + named_port { + name = "http" + port = "80" + } + + named_port { + name = "https" + port = "443" + } +} // Note: Autoscaling is not applicable to Unmanaged Instance Groups (UMIG) +// Instances are manually managed through the instance count variable + +# ------------------------------------------------------------------------------------- +# Create internal load balancer. +# ------------------------------------------------------------------------------------- + +// Create health check +resource "google_compute_region_health_check" "vmseries" { + name = "${local.prefix}panw-hc" + https_health_check { + port = 443 + request_path = "/unauth/php/health.php" + } +} + +# ------------------------------------------------------------------------------------- +# Create external load balancer. +# ------------------------------------------------------------------------------------- + +// Create forwarding rule for external load balancer. +resource "google_compute_address" "extlb" { + name = "${local.prefix}panw-extlb" + address_type = "EXTERNAL" +} + +// Create forwarding rule for external load balancer +resource "google_compute_forwarding_rule" "extlb" { + name = "${local.prefix}panw-extlb" + project = var.project_id + region = var.region + load_balancing_scheme = "EXTERNAL" + all_ports = true + ip_protocol = "TCP" + ip_address = google_compute_address.extlb.address + backend_service = google_compute_region_backend_service.extlb.self_link +} + +// Create backend service. +resource "google_compute_region_backend_service" "extlb" { + provider = google-beta + project = var.project_id + region = var.region + name = "${local.prefix}panw-extlb" + load_balancing_scheme = "EXTERNAL" + health_checks = [google_compute_region_health_check.vmseries.self_link] + protocol = "UNSPECIFIED" + session_affinity = "CLIENT_IP" + + dynamic "backend" { + for_each = google_compute_instance_group.vmseries + content { + group = backend.value.self_link + balancing_mode = "CONNECTION" + } + } +}