From e8852cbeaeda232bfa89ba9ad8b218aef3e4a0e5 Mon Sep 17 00:00:00 2001 From: Luca Prete Date: Tue, 4 Apr 2023 18:14:42 +0200 Subject: [PATCH] Move everything to a new stage --- fast/stages/2-networking-c-nva/README.md | 81 +-- .../stages/2-networking-c-nva/data/cidrs.yaml | 12 - .../firewall-rules/landing-trusted/rules.yaml | 12 - .../landing-untrusted/rules.yaml | 20 - fast/stages/2-networking-c-nva/diagram.png | Bin 81573 -> 67125 bytes fast/stages/2-networking-c-nva/diagram.svg | 2 +- fast/stages/2-networking-c-nva/landing.tf | 1 - fast/stages/2-networking-c-nva/nva.tf | 244 ++++---- fast/stages/2-networking-c-nva/regions.tf | 4 - fast/stages/2-networking-c-nva/spoke-dev.tf | 38 +- fast/stages/2-networking-c-nva/spoke-prod.tf | 38 +- .../2-networking-c-nva/test-resources.tf | 60 +- fast/stages/2-networking-c-nva/variables.tf | 19 +- fast/stages/2-networking-e-nva-bgp/README.md | 540 ++++++++++++++++++ .../data/bgp-config.tftpl | 76 +++ .../2-networking-e-nva-bgp/data/cidrs.yaml | 27 + .../data/dashboards/firewall_insights.json | 68 +++ .../data/dashboards/vpn.json | 248 ++++++++ .../data/firewall-rules/dev/rules.yaml | 21 + .../firewall-rules/landing-trusted/rules.yaml | 31 + .../landing-untrusted/rules.yaml | 31 + .../data/hierarchical-policy-rules.yaml | 49 ++ .../subnets/dev/dev-dataplatform-ew1.yaml | 8 + .../data/subnets/dev/dev-default-ew1.yaml | 5 + .../data/subnets/dev/dev-default-ew4.yaml | 5 + .../landing-trusted-default-ew1.yaml | 5 + .../landing-trusted-default-ew4.yaml | 5 + .../landing-untrusted-default-ew1.yaml | 5 + .../landing-untrusted-default-ew4.yaml | 5 + .../data/subnets/prod/prod-default-ew1.yaml | 5 + .../data/subnets/prod/prod-default-ew4.yaml | 5 + .../stages/2-networking-e-nva-bgp/diagram.png | Bin 0 -> 81573 bytes .../stages/2-networking-e-nva-bgp/diagram.svg | 1 + fast/stages/2-networking-e-nva-bgp/dns-dev.tf | 53 ++ .../2-networking-e-nva-bgp/dns-landing.tf | 155 +++++ .../stages/2-networking-e-nva-bgp/dns-prod.tf | 53 ++ fast/stages/2-networking-e-nva-bgp/landing.tf | 145 +++++ fast/stages/2-networking-e-nva-bgp/main.tf | 57 ++ .../2-networking-e-nva-bgp/monitoring.tf | 32 ++ .../ncc.tf | 0 fast/stages/2-networking-e-nva-bgp/nva.tf | 187 ++++++ fast/stages/2-networking-e-nva-bgp/outputs.tf | 91 +++ fast/stages/2-networking-e-nva-bgp/regions.tf | 46 ++ .../2-networking-e-nva-bgp/spoke-dev.tf | 112 ++++ .../2-networking-e-nva-bgp/spoke-prod.tf | 110 ++++ .../2-networking-e-nva-bgp/test-resources.tf | 245 ++++++++ .../2-networking-e-nva-bgp/variables.tf | 283 +++++++++ .../2-networking-e-nva-bgp/vpn-onprem.tf | 56 ++ .../stages/s2_networking_c_nva/stage.yaml | 4 +- .../s2_networking_e_nva_bgp/common.tfvars | 106 ++++ .../s2_networking_e_nva_bgp/stage.tfvars | 0 .../stages/s2_networking_e_nva_bgp/stage.yaml | 17 + .../s2_networking_e_nva_bgp/test_plan.py | 21 + .../s2_networking_e_nva_bgp/tftest.yaml | 22 + 54 files changed, 3179 insertions(+), 287 deletions(-) create mode 100644 fast/stages/2-networking-e-nva-bgp/README.md create mode 100644 fast/stages/2-networking-e-nva-bgp/data/bgp-config.tftpl create mode 100644 fast/stages/2-networking-e-nva-bgp/data/cidrs.yaml create mode 100644 fast/stages/2-networking-e-nva-bgp/data/dashboards/firewall_insights.json create mode 100644 fast/stages/2-networking-e-nva-bgp/data/dashboards/vpn.json create mode 100644 fast/stages/2-networking-e-nva-bgp/data/firewall-rules/dev/rules.yaml create mode 100644 fast/stages/2-networking-e-nva-bgp/data/firewall-rules/landing-trusted/rules.yaml create mode 100644 fast/stages/2-networking-e-nva-bgp/data/firewall-rules/landing-untrusted/rules.yaml create mode 100644 fast/stages/2-networking-e-nva-bgp/data/hierarchical-policy-rules.yaml create mode 100644 fast/stages/2-networking-e-nva-bgp/data/subnets/dev/dev-dataplatform-ew1.yaml create mode 100644 fast/stages/2-networking-e-nva-bgp/data/subnets/dev/dev-default-ew1.yaml create mode 100644 fast/stages/2-networking-e-nva-bgp/data/subnets/dev/dev-default-ew4.yaml create mode 100644 fast/stages/2-networking-e-nva-bgp/data/subnets/landing-trusted/landing-trusted-default-ew1.yaml create mode 100644 fast/stages/2-networking-e-nva-bgp/data/subnets/landing-trusted/landing-trusted-default-ew4.yaml create mode 100644 fast/stages/2-networking-e-nva-bgp/data/subnets/landing-untrusted/landing-untrusted-default-ew1.yaml create mode 100644 fast/stages/2-networking-e-nva-bgp/data/subnets/landing-untrusted/landing-untrusted-default-ew4.yaml create mode 100644 fast/stages/2-networking-e-nva-bgp/data/subnets/prod/prod-default-ew1.yaml create mode 100644 fast/stages/2-networking-e-nva-bgp/data/subnets/prod/prod-default-ew4.yaml create mode 100644 fast/stages/2-networking-e-nva-bgp/diagram.png create mode 100644 fast/stages/2-networking-e-nva-bgp/diagram.svg create mode 100644 fast/stages/2-networking-e-nva-bgp/dns-dev.tf create mode 100644 fast/stages/2-networking-e-nva-bgp/dns-landing.tf create mode 100644 fast/stages/2-networking-e-nva-bgp/dns-prod.tf create mode 100644 fast/stages/2-networking-e-nva-bgp/landing.tf create mode 100644 fast/stages/2-networking-e-nva-bgp/main.tf create mode 100644 fast/stages/2-networking-e-nva-bgp/monitoring.tf rename fast/stages/{2-networking-c-nva => 2-networking-e-nva-bgp}/ncc.tf (100%) create mode 100644 fast/stages/2-networking-e-nva-bgp/nva.tf create mode 100644 fast/stages/2-networking-e-nva-bgp/outputs.tf create mode 100644 fast/stages/2-networking-e-nva-bgp/regions.tf create mode 100644 fast/stages/2-networking-e-nva-bgp/spoke-dev.tf create mode 100644 fast/stages/2-networking-e-nva-bgp/spoke-prod.tf create mode 100644 fast/stages/2-networking-e-nva-bgp/test-resources.tf create mode 100644 fast/stages/2-networking-e-nva-bgp/variables.tf create mode 100644 fast/stages/2-networking-e-nva-bgp/vpn-onprem.tf create mode 100644 tests/fast/stages/s2_networking_e_nva_bgp/common.tfvars create mode 100644 tests/fast/stages/s2_networking_e_nva_bgp/stage.tfvars create mode 100644 tests/fast/stages/s2_networking_e_nva_bgp/stage.yaml create mode 100644 tests/fast/stages/s2_networking_e_nva_bgp/test_plan.py create mode 100644 tests/fast/stages/s2_networking_e_nva_bgp/tftest.yaml diff --git a/fast/stages/2-networking-c-nva/README.md b/fast/stages/2-networking-c-nva/README.md index adb68d8977..8874dc4689 100644 --- a/fast/stages/2-networking-c-nva/README.md +++ b/fast/stages/2-networking-c-nva/README.md @@ -4,15 +4,6 @@ This stage sets up the shared network infrastructure for the whole organization. It is designed for those who would like to leverage Network Virtual Appliances (NVAs) between trusted and untrusted areas of the network, for example for Intrusion Prevention System (IPS) purposes. -We use Network Connectivity Center Router Appliance (NCC-RA) and BGP appliances (for the sake of the demo [FRRouting](https://frrouting.org/)) to avoid different limitations that static routes bring (more [here](https://medium.com/google-cloud/gcp-routing-adventures-vol-2-enterprise-multi-regional-deployments-in-google-cloud-3968e9591d59)). The goals of this design include: - -- Avoid using network tags to route traffic. -- Route traffic symmetrically to prevent breaking stateful NVAs. -- Avoid unnecessary NAT traffic at the NVAs. -- Avoid cross-regional traffic unless absolutely necessary for disaster recovery. -- Automatically send all traffic through the cross-regional NVAs if the ones in-region fail. -- Keep the trusted hub VPC unique, rather than having one per region. - It adopts the common “hub and spoke” reference design, which is well suited for multiple scenarios, and it offers several advantages versus other designs: - the "trusted hub" VPC centralizes the external connectivity towards trusted network resources (e.g. on-prem, other cloud environments and the spokes), and it is ready to host cross-environment services like CI/CD, code repositories, and monitoring probes @@ -72,8 +63,8 @@ The final number of subnets, and their IP addressing will depend on the user-spe ### Multi-regional deployment -The stage deploys the the infrastructure in two regions. By default, europe-west1 and europe-west4. Regional resources include NVAs and test VMs. This provides enough redundancy to be resilient to regional failures. -In case of a regional failure, the corresponding dynamic routes are withdrawn and traffic will failover in the secondary region. +The stage deploys the the infrastructure in two regions. By default, europe-west1 and europe-west4. Regional resources include NVAs (templates, MIGs, ILBs) and test VMs. +This provides enough redundancy to be resilient to regional failures. ### VPC design @@ -82,28 +73,15 @@ The "landing zone" is divided into two VPC networks: - the trusted VPC: the connectivity hub towards other trusted networks - the untrusted VPC: the connectivity hub towards any other untrusted network -### NCC, NVAs and BGP sessions - -The VPCs connect through two sets of sample NVA machines: one per region, each containing two instances. The appliances run [Contrainer-Optimized OS](https://cloud.google.com/container-optimized-os/docs) and a container with [FRRouting](https://frrouting.org/). - -We levarage NCC-RA to allow the NVAs to establish BGP sessions with Cloud Routers in the untrusted and in the trusted VPCs. This allows Cloud Routers to advertise routes to the NVAs, and the NVAs to announce routes to the Cloud Router, so it can program them in the VPC. - -Specifically, each NVA establishes two BGP sessions (for redundancy) with the the Cloud Router deployed in the VPC and in the subnet where the interface of that VM is attached to. - -**Cloud Routers in the untrusted VPC advertise the default route (0.0.0.0/0) to the NVAs**. The NVAs advertise the route to the Cloud Routers in the trusted VPC. These dynamic routes are then imported through VPC peerings in the spokes. +The VPCs are connected with two sets of sample NVA machines, grouped in regional (multi-zone) [Managed Instance Groups (MIGs)](https://cloud.google.com/compute/docs/instance-groups). The appliances are plain Linux machines, performing simple routing/natting, leveraging some standard Linux features, such as *ip route* or *iptables*. The appliances are suited for demo purposes only and they should be replaced with enterprise-grade solutions before moving to production. +The traffic destined to the VMs in each MIG is mediated through regional internal load balancers, both in the trusted and in the untrusted networks. -**Cloud Routers in the trusted hub advertis to the NVAs** all the subnets of the trusted VPCs. This includes the regional subnets and the cross-regional subnets. The NVAs manipulate the route costs (MED) before advertising them to the Cloud Routers in the untrusted VPC. This is done to guarantee symmetric traffic paths (more [here](https://medium.com/google-cloud/gcp-routing-adventures-vol-2-enterprise-multi-regional-deployments-in-google-cloud-3968e9591d59)). - -NVAs establish **extra BGP sessions with both cross-regional NVAs**. In this case, the NVAs advertise the regional trusted routes only. This allows cross-spoke (environment) traffic to remain also symmetric (more [here](https://medium.com/google-cloud/gcp-routing-adventures-vol-2-enterprise-multi-regional-deployments-in-google-cloud-3968e9591d59)). We set these routes to be exchanged at a lower cost than the one set for the other routes. - -Following the majority of real-life deployments, **we assume appliances to be stateful and not able to synchronize sessions between multiple NVAs within the same regional cluster**. For this reason, within each regional cluster, NVAs announce the same routes with different MED costs (1 point of difference between the primary and the secondary). This will cause traffic to go deterministically through one applaiance at the time within each region. You can change this default behavior modifying the cost settings in the [NVAs BGP configuration file](./data/bgp-config.tftpl). - -By default, the design assumes that: +By default, the design assumes the following: - on-premise networks (and related resources) are considered trusted. As such, the VPNs connecting with on-premises are terminated in GCP, in the trusted VPC -- the public Internet is considered untrusted. As such [Cloud NAT](https://cloud.google.com/nat/docs/overview) has been deployed in the untrusted landing VPC only. Also, the default route is set to carry traffic from the trusted VPCs, through the NVAs, to the untrusted VPC. -- cross-spoke (environment) traffic and traffic from any untrusted network to any trusted network (and vice versa) pass through the NVAs. -- any traffic from a trusted network to an untrusted network (e.g. Internet) is natted by the NVAs. Users can configure further exclusions. +- the public Internet is considered untrusted. As such [Cloud NAT](https://cloud.google.com/nat/docs/overview) has been deployed in the untrusted landing VPC only +- cross-environment traffic and traffic from any untrusted network to any trusted network (and vice versa) pass through the NVAs. For demo purposes, the current NVA performs simple routing/natting only +- any traffic from a trusted network to an untrusted network (e.g. Internet) is natted by the NVAs. Users can configure further exclusions The trusted landing VPC acts as a hub: it bridges internal resources with the outside world and it hosts the shared services consumed by the spoke VPCs, connected to the hub thorugh VPC network peerings. Spokes are used to partition the environments. By default: @@ -111,7 +89,7 @@ The trusted landing VPC acts as a hub: it bridges internal resources with the ou - one spoke VPC hosts the production environment resources Each virtual network is a [shared VPC](https://cloud.google.com/vpc/docs/shared-vpc): shared VPCs are managed in dedicated *host projects* and shared with other *service projects* that consume the network resources. -Shared VPCs let organization administrators delegate administrative responsibilities, such as creating and managing instances, to Service Project Admins while maintaining centralized control over network resources like subnets, routes, and firewalls. +Shared VPC lets organization administrators delegate administrative responsibilities, such as creating and managing instances, to Service Project Admins while maintaining centralized control over network resources like subnets, routes, and firewalls. Users can easily extend the design to host additional environments, or adopt different logical mappings for the spokes (for example, in order to create a new spoke for each company entity). Adding spokes is trivial and it does not increase the design complexity. The steps to add more spokes are provided in the following sections. @@ -193,9 +171,11 @@ Routes in GCP are either automatically created (for example, when a subnet is ad In this setup: - routes between multiple subnets within the same VPC are automatically exchanged by GCP -- the spokes and the trusted landing VPC exchange dynamic routes through VPC peerings +- the spokes and the trusted landing VPC exchange routes through VPC peerings - on-premises is connected to the trusted landing VPC and it dynamically exchanges BGP routes with GCP (with the trusted VPC) using HA VPN -- the NVAs exchange dynamic routes using BGP with Cloud Routers in the untrusted VPC, Cloud Routers in the trusted VPC and cross-regional NVAs. This allows VMs in different environments and different regions to communicate. +- for cross-environment (spokes) communications, and for connections to on-premises and to the Internet, the spokes leverage some default tagged routes that send the traffic of each region (whose machines are identified by a dedicated network tag, e.g. *ew1*) to a corresponding regional NVA in the trusted VPC, through an ILB (whose VIP is set as the route next-hop) +- the spokes are configured with backup default routes, so if the NVAs in the same region become unavailable, more routes to the NVAs in the other region are already available. Current routes are not able to understand if the next-hop ILBs become unhealthy. As such, in case of a regional failure, users will need to manually withdraw the primary default routes, so the secondaries will take over +- the NVAs are configured with static routes that allow the communication with on-premises and between the GCP resources (including the cross-environment communication) The Cloud Routers (connected to the VPN gateways in the trusted VPC) are configured to exclude the default advertisement of VPC ranges and they only advertise their respective aggregate ranges, via custom advertisements. This greatly simplifies the routing configuration and avoids quota or limit issues, by keeping the number of routes small, instead of making it proportional to the subnets and to the secondary ranges in the VPCs. @@ -269,11 +249,9 @@ The connectivity between on-premises and GCP (the trusted landing VPC) is implem ### Routing and BGP -Each VPC network ([`net-vpc`](../../../modules/net-vpc)) manages a separate routing table, where you can define static routes (e.g. to private.googleapis.com) and receives dynamic routes through VPC peering and BGP sessions established with the neighbor networks (e.g. NCC routers, routers on-premises). +Each VPC network ([`net-vpc`](../../../modules/net-vpc)) manages a separate routing table, which can define static routes (e.g. to private.googleapis.com) and receives dynamic routes through VPC peering and BGP sessions established with the neighbor networks (e.g. the trusted landing VPC receives routes from on-premises, and the spokes receive RFC1918 from the trusted landing VPC). -NCC/Cloud Router BGP settings are defined in `ncc.tf`. -NVA BGP settings are defined in the [bpg-config.tftpl template file](./data/bgp-config.tftpl). -The variable `ncc_asn` allows to change the Autonomous System Number (ASN) assigned to the untrusted VPC Cloud Routers, to the trusted VPC Cloud Routers and to the NVAs. +Static routes are defined in `vpc-*.tf` files in the `routes` section of each `net-vpc` module. BGP sessions for trusted landing to on-premises are configured through the variable `vpn_onprem_configs`. @@ -473,12 +451,10 @@ Adapt the new file by replacing the value "prod" with the value "staging". Running `diff spoke-dev.tf spoke-prod.tf` can help to see how environment files differ. The new VPC requires a set of dedicated CIDRs, one per region, added to variable `gcp_ranges` (for example as `spoke_staging_ew1` and `spoke_staging_ew4`). -`gcp_ranges` is a map that "resolves" CIDR names to the actual addresses, and will be used later to configure routing. - +>`gcp_ranges` is a map that "resolves" CIDR names to the actual addresses, and will be used later to configure routing. +> Variables managing L7 Internal Load Balancers (`l7ilb_subnets`) and Private Service Access (`psa_ranges`) should also be adapted, and subnets and firewall rules for the new spoke should be added, as described above. -Configure the NVAs deployed updating the sample BGP [config file](./data/bgp-config.tftpl). - DNS configurations are centralised in the `dns-*.tf` files. Spokes delegate DNS resolution to Landing through DNS peering, and optionally define a private zone (e.g. `dev.gcp.example.com`) which the landing peers to. To configure DNS for a new environment, copy one of the other environments DNS files [e.g. (dns-dev.tf)](dns-dev.tf) into a new `dns-*.tf` file suffixed with the environment name (e.g. `dns-staging.tf`), and update its content accordingly. Don't forget to add a peering zone from the landing to the newly created environment private zone. @@ -494,8 +470,7 @@ DNS configurations are centralised in the `dns-*.tf` files. Spokes delegate DNS | [landing.tf](./landing.tf) | Landing VPC and related resources. | net-cloudnat · net-vpc · net-vpc-firewall · project | | | [main.tf](./main.tf) | Networking folder and hierarchical policy. | folder | | | [monitoring.tf](./monitoring.tf) | Network monitoring dashboards. | | google_monitoring_dashboard | -| [ncc.tf](./ncc.tf) | None | ncc-spoke-ra | | -| [nva.tf](./nva.tf) | None | compute-vm · simple-nva | google_compute_address | +| [nva.tf](./nva.tf) | None | compute-mig · compute-vm · simple-nva | | | [outputs.tf](./outputs.tf) | Module outputs. | | google_storage_bucket_object · local_file | | [regions.tf](./regions.tf) | Compute short names for regions. | | | | [spoke-dev.tf](./spoke-dev.tf) | Dev spoke VPC and related resources. | net-vpc · net-vpc-firewall · net-vpc-peering · project | google_project_iam_binding | @@ -511,21 +486,19 @@ DNS configurations are centralised in the `dns-*.tf` files. Spokes delegate DNS | [automation](variables.tf#L17) | Automation resources created by the bootstrap stage. | object({…}) | ✓ | | 0-bootstrap | | [billing_account](variables.tf#L25) | Billing account id. If billing account is not part of the same org set `is_org_level` to false. | object({…}) | ✓ | | 0-bootstrap | | [folder_ids](variables.tf#L75) | Folders to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created. | object({…}) | ✓ | | 1-resman | -| [organization](variables.tf#L119) | Organization details. | object({…}) | ✓ | | 0-bootstrap | -| [prefix](variables.tf#L135) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | 0-bootstrap | +| [organization](variables.tf#L108) | Organization details. | object({…}) | ✓ | | 0-bootstrap | +| [prefix](variables.tf#L124) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | 0-bootstrap | | [custom_roles](variables.tf#L38) | Custom roles defined at the org level, in key => id format. | object({…}) | | null | 0-bootstrap | | [dns](variables.tf#L47) | Onprem DNS resolvers. | map(list(string)) | | {…} | | | [factories_config](variables.tf#L55) | Configuration for network resource factories. | object({…}) | | {…} | | | [gcp_ranges](variables.tf#L85) | GCP address ranges in name => range format. | map(string) | | {…} | | -| [ncc_asn](variables.tf#L100) | The NCC Cloud Routers ASN configuration. | map(number) | | {…} | | -| [onprem_cidr](variables.tf#L111) | Onprem addresses in name => range format. | map(string) | | {…} | | -| [outputs_location](variables.tf#L129) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | -| [psa_ranges](variables.tf#L146) | IP ranges used for Private Service Access (e.g. CloudSQL). Ranges is in name => range format. | object({…}) | | null | | -| [regions](variables.tf#L167) | Region definitions. | object({…}) | | {…} | | -| [service_accounts](variables.tf#L179) | Automation service accounts in name => email format. | object({…}) | | null | 1-resman | -| [vpn_onprem_primary_config](variables.tf#L193) | VPN gateway configuration for onprem interconnection in the primary region. | object({…}) | | null | | -| [vpn_onprem_secondary_config](variables.tf#L236) | VPN gateway configuration for onprem interconnection in the secondary region. | object({…}) | | null | | -| [zones](variables.tf#L279) | Zones in which NVAs are deployed. | list(string) | | ["b", "c"] | | +| [onprem_cidr](variables.tf#L100) | Onprem addresses in name => range format. | map(string) | | {…} | | +| [outputs_location](variables.tf#L118) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | +| [psa_ranges](variables.tf#L135) | IP ranges used for Private Service Access (e.g. CloudSQL). Ranges is in name => range format. | object({…}) | | null | | +| [regions](variables.tf#L156) | Region definitions. | object({…}) | | {…} | | +| [service_accounts](variables.tf#L168) | Automation service accounts in name => email format. | object({…}) | | null | 1-resman | +| [vpn_onprem_primary_config](variables.tf#L182) | VPN gateway configuration for onprem interconnection in the primary region. | object({…}) | | null | | +| [vpn_onprem_secondary_config](variables.tf#L225) | VPN gateway configuration for onprem interconnection in the secondary region. | object({…}) | | null | | ## Outputs diff --git a/fast/stages/2-networking-c-nva/data/cidrs.yaml b/fast/stages/2-networking-c-nva/data/cidrs.yaml index 93d7bb0b12..b6c25e21ab 100644 --- a/fast/stages/2-networking-c-nva/data/cidrs.yaml +++ b/fast/stages/2-networking-c-nva/data/cidrs.yaml @@ -6,18 +6,6 @@ healthchecks: - 209.85.152.0/22 - 209.85.204.0/22 -ncc_cloud_routers_trusted: - - 10.128.64.201/32 - - 10.128.64.202/32 - - 10.128.96.201/32 - - 10.128.96.202/32 - -ncc_cloud_routers_untrusted: - - 10.128.0.201/32 - - 10.128.0.202/32 - - 10.128.32.201/32 - - 10.128.32.202/32 - rfc1918: - 10.0.0.0/8 - 172.16.0.0/12 diff --git a/fast/stages/2-networking-c-nva/data/firewall-rules/landing-trusted/rules.yaml b/fast/stages/2-networking-c-nva/data/firewall-rules/landing-trusted/rules.yaml index 6e00603bd5..1405170fb5 100644 --- a/fast/stages/2-networking-c-nva/data/firewall-rules/landing-trusted/rules.yaml +++ b/fast/stages/2-networking-c-nva/data/firewall-rules/landing-trusted/rules.yaml @@ -17,15 +17,3 @@ ingress: - protocol: tcp ports: - 12345 - # This is not really needed, but it's good to have it - # in place if the more generic hierarchical firewall policies - # get deleted - allow-ncc-nva-bgp-trusted: - description: "Allow BGP traffic from NCC Cloud Routers to NVAs" - source_ranges: - - ncc_cloud_routers_trusted - targets: ["nva"] - rules: - - protocol: tcp - ports: - - 179 diff --git a/fast/stages/2-networking-c-nva/data/firewall-rules/landing-untrusted/rules.yaml b/fast/stages/2-networking-c-nva/data/firewall-rules/landing-untrusted/rules.yaml index c6077013cf..aa51c0fe80 100644 --- a/fast/stages/2-networking-c-nva/data/firewall-rules/landing-untrusted/rules.yaml +++ b/fast/stages/2-networking-c-nva/data/firewall-rules/landing-untrusted/rules.yaml @@ -9,23 +9,3 @@ ingress: - protocol: tcp ports: - 22 - # these are not really needed, but it's good to have them - # in place if the more generic hierarchical firewall policies - # get deleted - allow-ncc-nva-bgp-untrusted: - description: "Allow BGP traffic from NCC Cloud Routers to NVAs" - source_ranges: - - ncc_cloud_routers_untrusted - targets: ["nva"] - rules: - - protocol: tcp - ports: - - 179 - allow-nva-nva-bgp-untrusted: - description: "Allow BGP traffic from cross-regional NVAs" - sources: ["nva"] - targets: ["nva"] - rules: - - protocol: tcp - ports: - - 179 diff --git a/fast/stages/2-networking-c-nva/diagram.png b/fast/stages/2-networking-c-nva/diagram.png index 53c4daca2e5ea7452b18bccd6811d93c01362f82..5e789ed792feb469bacd49df52d85b5f6039627b 100644 GIT binary patch literal 67125 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 literal 81573 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 z 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 RQXvFfs)`R4-rq9~{4W#Y=1Kqn diff --git a/fast/stages/2-networking-c-nva/diagram.svg b/fast/stages/2-networking-c-nva/diagram.svg index 7fad1244b2..957b7d102b 100644 --- a/fast/stages/2-networking-c-nva/diagram.svg +++ b/fast/stages/2-networking-c-nva/diagram.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/fast/stages/2-networking-c-nva/landing.tf b/fast/stages/2-networking-c-nva/landing.tf index 8f0b195927..e66b03db94 100644 --- a/fast/stages/2-networking-c-nva/landing.tf +++ b/fast/stages/2-networking-c-nva/landing.tf @@ -26,7 +26,6 @@ module "landing-project" { "compute.googleapis.com", "dns.googleapis.com", "iap.googleapis.com", - "networkconnectivity.googleapis.com", "networkmanagement.googleapis.com", "stackdriver.googleapis.com" ] diff --git a/fast/stages/2-networking-c-nva/nva.tf b/fast/stages/2-networking-c-nva/nva.tf index baf6018a53..62c45dbd22 100644 --- a/fast/stages/2-networking-c-nva/nva.tf +++ b/fast/stages/2-networking-c-nva/nva.tf @@ -1,5 +1,5 @@ /** - * Copyright 2023 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,173 +15,165 @@ */ locals { - _nva_zones = ["b", "c"] - - # The configurations used to create the NVA VMs. - # - # Rendered as following: - # nva_configs = { - # primary-b = {...} - # primary-c = {...} - # secondary-b = {...} - # secondary-c = {...} - # } - nva_configs = { - for v in setproduct(keys(var.regions), local._nva_zones) : - join("-", v) => { - # Each NVA announces its trusted regional subnets - announce-to-nva = upper(v.0) - # NVAs in each region have their own ASN - # and peer with cross-regional NVAs. - asn_nva = ( - v.0 == "primary" - ? var.ncc_asn.nva_primary - : var.ncc_asn.nva_secondary - ) - asn_nva_cross_region = ( - v.0 == "primary" - ? var.ncc_asn.nva_secondary - : var.ncc_asn.nva_primary - ) - asn_trusted = var.ncc_asn.trusted - asn_untrusted = var.ncc_asn.untrusted - # To guarantee traffic to remain symmetric, - # NVAs need to advertise cross-region routes with a higher cost (10100) - cost_primary = v.0 == "primary" ? "100" : "10100" - cost_secondary = v.0 == "primary" ? "10100" : "100" - gcp_dev_primary = var.gcp_ranges.gcp_dev_primary - gcp_dev_secondary = var.gcp_ranges.gcp_dev_secondary - gcp_landing_trusted_primary = var.gcp_ranges.gcp_landing_trusted_primary - gcp_landing_trusted_secondary = var.gcp_ranges.gcp_landing_trusted_secondary - gcp_landing_untrusted_primary = var.gcp_ranges.gcp_landing_untrusted_primary - gcp_landing_untrusted_secondary = var.gcp_ranges.gcp_landing_untrusted_secondary - gcp_prod_primary = var.gcp_ranges.gcp_prod_primary - gcp_prod_secondary = var.gcp_ranges.gcp_prod_secondary - # The IPs of cross-region NVA VMs in the untrusted VPC (x.y.w.z) - ip_neighbor_cross_region_nva_0 = cidrhost(module.landing-untrusted-vpc.subnet_ips["${local._regions_cross[v.0]}/landing-untrusted-default-${local.region_shortnames[local._regions_cross[v.0]]}"], 101) - ip_neighbor_cross_region_nva_1 = cidrhost(module.landing-untrusted-vpc.subnet_ips["${local._regions_cross[v.0]}/landing-untrusted-default-${local.region_shortnames[local._regions_cross[v.0]]}"], 102) - # The Cloud router IPs (x.y.w.z) in the untrusted - # and in the trusted VPCs, where the NVA connects to - ip_neighbor_trusted_0 = cidrhost(module.landing-trusted-vpc.subnet_ips["${var.regions[v.0]}/landing-trusted-default-${local.region_shortnames[var.regions[v.0]]}"], 201) - ip_neighbor_trusted_1 = cidrhost(module.landing-trusted-vpc.subnet_ips["${var.regions[v.0]}/landing-trusted-default-${local.region_shortnames[var.regions[v.0]]}"], 202) - ip_neighbor_untrusted_0 = cidrhost(module.landing-untrusted-vpc.subnet_ips["${var.regions[v.0]}/landing-untrusted-default-${local.region_shortnames[var.regions[v.0]]}"], 201) - ip_neighbor_untrusted_1 = cidrhost(module.landing-untrusted-vpc.subnet_ips["${var.regions[v.0]}/landing-untrusted-default-${local.region_shortnames[var.regions[v.0]]}"], 202) - # The IPs to assign to the NVA NICs - # in the trusted and in the untrusted VPCs. - ip_trusted = cidrhost(module.landing-trusted-vpc.subnet_ips["${var.regions[v.0]}/landing-trusted-default-${local.region_shortnames[var.regions[v.0]]}"], 101 + index(var.zones, v.1)) - ip_untrusted = cidrhost(module.landing-untrusted-vpc.subnet_ips["${var.regions[v.0]}/landing-untrusted-default-${local.region_shortnames[var.regions[v.0]]}"], 101 + index(var.zones, v.1)) - # Either primary or secondary - name = v.0 - # The name of the region where the NVA lives. - # For example, europe-west1 or europe-west4 - region = var.regions[v.0] - # the short name for the region. For example, ew1 or ew4 - shortname = local.region_shortnames[var.regions[v.0]] - # The zone where the NVA lives. For example, b or c - zone = v.1 - } - } - - # The routing_config should be aligned to the NVA NICs. - # For example: - # local.routing_config[0] configures eth0; - # local.routing_config[0] configures eth1. + # routing_config should be aligned to the NVA network interfaces - i.e. + # local.routing_config[0] sets up the first interface, and so on. routing_config = [ { - enable_masquerading = true name = "untrusted" + enable_masquerading = true routes = [ var.gcp_ranges.gcp_landing_untrusted_primary, - var.gcp_ranges.gcp_landing_untrusted_secondary + var.gcp_ranges.gcp_landing_untrusted_secondary, ] }, { name = "trusted" routes = [ + var.gcp_ranges.gcp_dev_primary, + var.gcp_ranges.gcp_dev_secondary, var.gcp_ranges.gcp_landing_trusted_primary, - var.gcp_ranges.gcp_landing_trusted_secondary + var.gcp_ranges.gcp_landing_trusted_secondary, + var.gcp_ranges.gcp_prod_primary, + var.gcp_ranges.gcp_prod_secondary, ] - } + }, ] + nva_locality = { + for v in setproduct(keys(var.regions), local.nva_zones) : + join("-", v) => { + name = v.0 + region = var.regions[v.0] + shortname = local.region_shortnames[var.regions[v.0]] + zone = v.1 + } + } + nva_zones = ["b", "c"] } -module "nva-bgp-cloud-config" { - for_each = local.nva_configs +# NVA config +module "nva-cloud-config" { source = "../../../modules/cloud-config-container/simple-nva" enable_health_checks = true network_interfaces = local.routing_config - frr_config = { - config_file = templatefile("data/bgp-config.tftpl", each.value) - daemons_enabled = ["bgpd"] - } -} - -resource "google_compute_address" "nva_static_ip_trusted" { - for_each = local.nva_configs - name = "nva-ip-trusted-${each.value.shortname}-${each.value.zone}" - project = module.landing-project.project_id - subnetwork = module.landing-trusted-vpc.subnet_self_links["${each.value.region}/landing-trusted-default-${each.value.shortname}"] - address_type = "INTERNAL" - address = each.value.ip_trusted - region = each.value.region } -resource "google_compute_address" "nva_static_ip_untrusted" { - for_each = local.nva_configs - name = "nva-ip-untrusted-${each.value.shortname}-${each.value.zone}" - project = module.landing-project.project_id - subnetwork = module.landing-untrusted-vpc.subnet_self_links["${each.value.region}/landing-untrusted-default-${each.value.shortname}"] - address_type = "INTERNAL" - address = each.value.ip_untrusted - region = each.value.region -} - -module "nva" { - for_each = local.nva_configs - source = "../../../modules/compute-vm" - project_id = module.landing-project.project_id - name = "nva-${each.value.shortname}-${each.value.zone}" - instance_type = "e2-standard-2" - can_ip_forward = true - zone = "${each.value.region}-${each.value.zone}" - tags = ["nva"] - +module "nva-template" { + for_each = local.nva_locality + source = "../../../modules/compute-vm" + project_id = module.landing-project.project_id + name = "nva-template-${each.key}" + zone = "${each.value.region}-${each.value.zone}" + instance_type = "e2-standard-2" + tags = ["nva"] + create_template = true + can_ip_forward = true network_interfaces = [ { network = module.landing-untrusted-vpc.self_link subnetwork = module.landing-untrusted-vpc.subnet_self_links["${each.value.region}/landing-untrusted-default-${each.value.shortname}"] nat = false - addresses = { - external = null - internal = google_compute_address.nva_static_ip_untrusted[each.key].address - } + addresses = null }, { network = module.landing-trusted-vpc.self_link subnetwork = module.landing-trusted-vpc.subnet_self_links["${each.value.region}/landing-trusted-default-${each.value.shortname}"] nat = false - addresses = { - external = null - internal = google_compute_address.nva_static_ip_trusted[each.key].address - } + addresses = null } ] - boot_disk = { initialize_params = { image = "projects/cos-cloud/global/images/family/cos-stable" - size = 10 - type = "pd-balanced" } } - options = { allow_stopping_for_update = true deletion_protection = false + spot = true termination_action = "STOP" } - metadata = { - user-data = module.nva-bgp-cloud-config[each.key].cloud_config + user-data = module.nva-cloud-config.cloud_config + } +} + +module "nva-mig" { + for_each = local.nva_locality + source = "../../../modules/compute-mig" + project_id = module.landing-project.project_id + location = each.value.region + name = "nva-cos-${each.key}" + instance_template = module.nva-template[each.key].template.self_link + target_size = 1 + auto_healing_policies = { + initial_delay_sec = 30 + } + health_check_config = { + enable_logging = true + tcp = { + port = 22 + } + } +} + +module "ilb-nva-untrusted" { + for_each = { + for k, v in var.regions : k => { + region = v + shortname = local.region_shortnames[v] + subnet = "${v}/landing-untrusted-default-${local.region_shortnames[v]}" + } + } + source = "../../../modules/net-ilb" + project_id = module.landing-project.project_id + region = each.value.region + name = "nva-untrusted-${each.key}" + service_label = var.prefix + global_access = true + vpc_config = { + network = module.landing-untrusted-vpc.self_link + subnetwork = module.landing-untrusted-vpc.subnet_self_links[each.value.subnet] + } + backends = [ + for k, v in module.nva-mig : + { group = v.group_manager.instance_group } + if startswith(k, each.key) + ] + health_check_config = { + enable_logging = true + tcp = { + port = 22 + } + } +} + +module "ilb-nva-trusted" { + for_each = { + for k, v in var.regions : k => { + region = v + shortname = local.region_shortnames[v] + subnet = "${v}/landing-trusted-default-${local.region_shortnames[v]}" + } + } + source = "../../../modules/net-ilb" + project_id = module.landing-project.project_id + region = each.value.region + name = "nva-trusted-${each.key}" + service_label = var.prefix + global_access = true + vpc_config = { + network = module.landing-trusted-vpc.self_link + subnetwork = module.landing-trusted-vpc.subnet_self_links[each.value.subnet] + } + backends = [ + for k, v in module.nva-mig : + { group = v.group_manager.instance_group } + if startswith(k, each.key) + ] + health_check_config = { + enable_logging = true + tcp = { + port = 22 + } } } diff --git a/fast/stages/2-networking-c-nva/regions.tf b/fast/stages/2-networking-c-nva/regions.tf index ee6b0d6928..53514afa9b 100644 --- a/fast/stages/2-networking-c-nva/regions.tf +++ b/fast/stages/2-networking-c-nva/regions.tf @@ -21,10 +21,6 @@ locals { _region_cardinal = { southeast = "se" } - _regions_cross = { - primary = var.regions["secondary"] - secondary = var.regions["primary"] - } # only map when the first character would not work _region_geo = { australia = "o" diff --git a/fast/stages/2-networking-c-nva/spoke-dev.tf b/fast/stages/2-networking-c-nva/spoke-dev.tf index 967a2746ff..8e26a7325e 100644 --- a/fast/stages/2-networking-c-nva/spoke-dev.tf +++ b/fast/stages/2-networking-c-nva/spoke-dev.tf @@ -66,6 +66,34 @@ module "dev-spoke-vpc" { next_hop_type = "gateway" next_hop = "default-internet-gateway" } + nva-primary-to-primary = { + dest_range = "0.0.0.0/0" + priority = 1000 + tags = ["primary"] + next_hop_type = "ilb" + next_hop = module.ilb-nva-trusted["primary"].forwarding_rule_address + } + nva-secondary-to-secondary = { + dest_range = "0.0.0.0/0" + priority = 1000 + tags = ["secondary"] + next_hop_type = "ilb" + next_hop = module.ilb-nva-trusted["secondary"].forwarding_rule_address + } + nva-primary-to-secondary = { + dest_range = "0.0.0.0/0" + priority = 1001 + tags = ["primary"] + next_hop_type = "ilb" + next_hop = module.ilb-nva-trusted["primary"].forwarding_rule_address + } + nva-secondary-to-primary = { + dest_range = "0.0.0.0/0" + priority = 1001 + tags = ["secondary"] + next_hop_type = "ilb" + next_hop = module.ilb-nva-trusted["secondary"].forwarding_rule_address + } } } @@ -83,12 +111,10 @@ module "dev-spoke-firewall" { } module "peering-dev" { - source = "../../../modules/net-vpc-peering" - prefix = "dev-peering-0" - local_network = module.dev-spoke-vpc.self_link - peer_network = module.landing-trusted-vpc.self_link - export_local_custom_routes = true - export_peer_custom_routes = true + source = "../../../modules/net-vpc-peering" + prefix = "dev-peering-0" + local_network = module.dev-spoke-vpc.self_link + peer_network = module.landing-trusted-vpc.self_link } # Create delegated grants for stage3 service accounts diff --git a/fast/stages/2-networking-c-nva/spoke-prod.tf b/fast/stages/2-networking-c-nva/spoke-prod.tf index 9978608145..1b2c4e2b6d 100644 --- a/fast/stages/2-networking-c-nva/spoke-prod.tf +++ b/fast/stages/2-networking-c-nva/spoke-prod.tf @@ -65,6 +65,34 @@ module "prod-spoke-vpc" { next_hop_type = "gateway" next_hop = "default-internet-gateway" } + nva-primary-to-primary = { + dest_range = "0.0.0.0/0" + priority = 1000 + tags = ["primary"] + next_hop_type = "ilb" + next_hop = module.ilb-nva-trusted["primary"].forwarding_rule_address + } + nva-secondary-to-secondary = { + dest_range = "0.0.0.0/0" + priority = 1000 + tags = ["secondary"] + next_hop_type = "ilb" + next_hop = module.ilb-nva-trusted["secondary"].forwarding_rule_address + } + nva-primary-to-secondary = { + dest_range = "0.0.0.0/0" + priority = 1001 + tags = ["primary"] + next_hop_type = "ilb" + next_hop = module.ilb-nva-trusted["secondary"].forwarding_rule_address + } + nva-secondary-to-primary = { + dest_range = "0.0.0.0/0" + priority = 1001 + tags = ["secondary"] + next_hop_type = "ilb" + next_hop = module.ilb-nva-trusted["primary"].forwarding_rule_address + } } } @@ -82,12 +110,10 @@ module "prod-spoke-firewall" { } module "peering-prod" { - source = "../../../modules/net-vpc-peering" - prefix = "prod-peering-0" - local_network = module.prod-spoke-vpc.self_link - peer_network = module.landing-trusted-vpc.self_link - export_local_custom_routes = true - export_peer_custom_routes = true + source = "../../../modules/net-vpc-peering" + prefix = "prod-peering-0" + local_network = module.prod-spoke-vpc.self_link + peer_network = module.landing-trusted-vpc.self_link } # Create delegated grants for stage3 service accounts diff --git a/fast/stages/2-networking-c-nva/test-resources.tf b/fast/stages/2-networking-c-nva/test-resources.tf index b2816e4fdb..97bb7208c5 100644 --- a/fast/stages/2-networking-c-nva/test-resources.tf +++ b/fast/stages/2-networking-c-nva/test-resources.tf @@ -1,5 +1,5 @@ /** - * Copyright 2023 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,15 +22,16 @@ # source = "../../../modules/compute-vm" # project_id = module.landing-project.project_id # zone = "${var.regions.primary}-b" -# name = "test-vm-lnd-unt-primary-0" +# name = "test-vm-lnd-unt-pri-0" # network_interfaces = [{ # network = module.landing-untrusted-vpc.self_link # subnetwork = module.landing-untrusted-vpc.subnet_self_links["${var.regions.primary}/landing-untrusted-default-${local.region_shortnames[var.regions.primary]}"] # }] -# tags = ["primary", "ssh"] +# tags = ["primary", "ssh"] +# service_account_create = true # boot_disk = { # initialize_params = { -# image = "projects/debian-cloud/global/images/family/debian-11" +# image = "projects/debian-cloud/global/images/family/debian-10" # } # } # options = { @@ -49,15 +50,16 @@ # source = "../../../modules/compute-vm" # project_id = module.landing-project.project_id # zone = "${var.regions.secondary}-a" -# name = "test-vm-lnd-unt-secondary-0" +# name = "test-vm-lnd-unt-sec-0" # network_interfaces = [{ # network = module.landing-untrusted-vpc.self_link # subnetwork = module.landing-untrusted-vpc.subnet_self_links["${var.regions.secondary}/landing-untrusted-default-${local.region_shortnames[var.regions.secondary]}"] # }] -# tags = ["secondary", "ssh"] +# tags = ["secondary", "ssh"] +# service_account_create = true # boot_disk = { # initialize_params = { -# image = "projects/debian-cloud/global/images/family/debian-11" +# image = "projects/debian-cloud/global/images/family/debian-10" # } # } # options = { @@ -78,15 +80,16 @@ # source = "../../../modules/compute-vm" # project_id = module.landing-project.project_id # zone = "${var.regions.primary}-b" -# name = "test-vm-lnd-tru-primary-0" +# name = "test-vm-lnd-tru-pri-0" # network_interfaces = [{ # network = module.landing-trusted-vpc.self_link # subnetwork = module.landing-trusted-vpc.subnet_self_links["${var.regions.primary}/landing-trusted-default-${local.region_shortnames[var.regions.primary]}"] # }] -# tags = ["primary", "ssh"] +# tags = ["primary", "ssh"] +# service_account_create = true # boot_disk = { # initialize_params = { -# image = "projects/debian-cloud/global/images/family/debian-11" +# image = "projects/debian-cloud/global/images/family/debian-10" # } # } # options = { @@ -105,15 +108,16 @@ # source = "../../../modules/compute-vm" # project_id = module.landing-project.project_id # zone = "${var.regions.secondary}-a" -# name = "test-vm-lnd-tru-secondary-0" +# name = "test-vm-lnd-tru-sec-0" # network_interfaces = [{ # network = module.landing-trusted-vpc.self_link # subnetwork = module.landing-trusted-vpc.subnet_self_links["${var.regions.secondary}/landing-trusted-default-${local.region_shortnames[var.regions.secondary]}"] # }] -# tags = ["secondary", "ssh"] +# tags = ["secondary", "ssh"] +# service_account_create = true # boot_disk = { # initialize_params = { -# image = "projects/debian-cloud/global/images/family/debian-11" +# image = "projects/debian-cloud/global/images/family/debian-10" # } # } # options = { @@ -134,16 +138,17 @@ # source = "../../../modules/compute-vm" # project_id = module.dev-spoke-project.project_id # zone = "${var.regions.primary}-b" -# name = "test-vm-dev-primary-0" +# name = "test-vm-dev-pri-0" # network_interfaces = [{ # network = module.dev-spoke-vpc.self_link # # change the subnet name to match the values you are actually using # subnetwork = module.dev-spoke-vpc.subnet_self_links["${var.regions.primary}/dev-default-${local.region_shortnames[var.regions.primary]}"] # }] -# tags = ["primary", "ssh"] +# tags = ["primary", "ssh"] +# service_account_create = true # boot_disk = { # initialize_params = { -# image = "projects/debian-cloud/global/images/family/debian-11" +# image = "projects/debian-cloud/global/images/family/debian-10" # } # } # options = { @@ -162,16 +167,17 @@ # source = "../../../modules/compute-vm" # project_id = module.dev-spoke-project.project_id # zone = "${var.regions.secondary}-a" -# name = "test-vm-dev-secondary-0" +# name = "test-vm-dev-sec-0" # network_interfaces = [{ # network = module.dev-spoke-vpc.self_link # # change the subnet name to match the values you are actually using # subnetwork = module.dev-spoke-vpc.subnet_self_links["${var.regions.secondary}/dev-default-${local.region_shortnames[var.regions.secondary]}"] # }] -# tags = ["secondary", "ssh"] +# tags = ["secondary", "ssh"] +# service_account_create = true # boot_disk = { # initialize_params = { -# image = "projects/debian-cloud/global/images/family/debian-11" +# image = "projects/debian-cloud/global/images/family/debian-10" # } # } # options = { @@ -192,16 +198,19 @@ # source = "../../../modules/compute-vm" # project_id = module.prod-spoke-project.project_id # zone = "${var.regions.primary}-b" -# name = "test-vm-prod-primary-0" +# name = "test-vm-prod-pri-0" # network_interfaces = [{ # network = module.prod-spoke-vpc.self_link # # change the subnet name to match the values you are actually using # subnetwork = module.prod-spoke-vpc.subnet_self_links["${var.regions.primary}/prod-default-${local.region_shortnames[var.regions.primary]}"] # }] -# tags = ["primary", "ssh"] +# tags = ["primary", "ssh"] +# service_account_create = true # boot_disk = { # initialize_params = { -# image = "projects/debian-cloud/global/images/family/debian-11" +# image = "projects/debian-cloud/global/images/family/debian-10" +# type = "pd-balanced" +# size = 10 # } # } # options = { @@ -220,16 +229,17 @@ # source = "../../../modules/compute-vm" # project_id = module.prod-spoke-project.project_id # zone = "${var.regions.secondary}-a" -# name = "test-vm-prod-secondary-0" +# name = "test-vm-prod-sec-0" # network_interfaces = [{ # network = module.prod-spoke-vpc.self_link # # change the subnet name to match the values you are actually using # subnetwork = module.prod-spoke-vpc.subnet_self_links["${var.regions.secondary}/prod-default-${local.region_shortnames[var.regions.secondary]}"] # }] -# tags = ["secondary", "ssh"] +# tags = ["secondary", "ssh"] +# service_account_create = true # boot_disk = { # initialize_params = { -# image = "projects/debian-cloud/global/images/family/debian-11" +# image = "projects/debian-cloud/global/images/family/debian-10" # } # } # options = { diff --git a/fast/stages/2-networking-c-nva/variables.tf b/fast/stages/2-networking-c-nva/variables.tf index 85d3c7c07e..e4fe6896a3 100644 --- a/fast/stages/2-networking-c-nva/variables.tf +++ b/fast/stages/2-networking-c-nva/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2023 Google LLC + * Copyright 2022 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -97,17 +97,6 @@ variable "gcp_ranges" { } } -variable "ncc_asn" { - description = "The NCC Cloud Routers ASN configuration." - type = map(number) - default = { - nva_primary = 64513 - nva_secondary = 64514 - trusted = 64515 - untrusted = 64512 - } -} - variable "onprem_cidr" { description = "Onprem addresses in name => range format." type = map(string) @@ -275,9 +264,3 @@ variable "vpn_onprem_secondary_config" { }) default = null } - -variable "zones" { - description = "Zones in which NVAs are deployed." - type = list(string) - default = ["b", "c"] -} diff --git a/fast/stages/2-networking-e-nva-bgp/README.md b/fast/stages/2-networking-e-nva-bgp/README.md new file mode 100644 index 0000000000..adb68d8977 --- /dev/null +++ b/fast/stages/2-networking-e-nva-bgp/README.md @@ -0,0 +1,540 @@ +# Networking with Network Virtual Appliance + +This stage sets up the shared network infrastructure for the whole organization. + +It is designed for those who would like to leverage Network Virtual Appliances (NVAs) between trusted and untrusted areas of the network, for example for Intrusion Prevention System (IPS) purposes. + +We use Network Connectivity Center Router Appliance (NCC-RA) and BGP appliances (for the sake of the demo [FRRouting](https://frrouting.org/)) to avoid different limitations that static routes bring (more [here](https://medium.com/google-cloud/gcp-routing-adventures-vol-2-enterprise-multi-regional-deployments-in-google-cloud-3968e9591d59)). The goals of this design include: + +- Avoid using network tags to route traffic. +- Route traffic symmetrically to prevent breaking stateful NVAs. +- Avoid unnecessary NAT traffic at the NVAs. +- Avoid cross-regional traffic unless absolutely necessary for disaster recovery. +- Automatically send all traffic through the cross-regional NVAs if the ones in-region fail. +- Keep the trusted hub VPC unique, rather than having one per region. + +It adopts the common “hub and spoke” reference design, which is well suited for multiple scenarios, and it offers several advantages versus other designs: + +- the "trusted hub" VPC centralizes the external connectivity towards trusted network resources (e.g. on-prem, other cloud environments and the spokes), and it is ready to host cross-environment services like CI/CD, code repositories, and monitoring probes +- the "spoke" VPCs allow partitioning workloads (e.g. by environment like in this setup), while still retaining controlled access to central connectivity and services +- Shared VPCs -both in hub and spokes- split the management of the network resources into specific (host) projects, while still allowing them to be consumed from the workload (service) projects +- the design facilitates DNS centralization + +Connectivity between the hub and the spokes is established via [VPC network peerings](https://cloud.google.com/vpc/docs/vpc-peering), which offer uncapped bandwidth, lower latencies, at no additional costs and with a very low management overhead. Different ways of implementing connectivity, and related some pros and cons, are discussed below. + +The diagram shows the high-level design and it should be used as a reference throughout the following sections. + +The final number of subnets, and their IP addressing will depend on the user-specific requirements. It can be easily changed via variables or external data files, without any need to edit the code. + +

+ Networking diagram +

+ +## Table of contents + +- [Networking with Network Virtual Appliance](#networking-with-network-virtual-appliance) + - [Table of contents](#table-of-contents) + - [Design overview and choices](#design-overview-and-choices) + - [Multi-regional deployment](#multi-regional-deployment) + - [VPC design](#vpc-design) + - [External connectivity](#external-connectivity) + - [Internal connectivity](#internal-connectivity) + - [IP ranges, subnetting, routing](#ip-ranges-subnetting-routing) + - [Internet egress](#internet-egress) + - [VPC and Hierarchical Firewall](#vpc-and-hierarchical-firewall) + - [DNS](#dns) + - [Stage structure and files layout](#stage-structure-and-files-layout) + - [VPCs](#vpcs) + - [VPNs](#vpns) + - [Routing and BGP](#routing-and-bgp) + - [Firewall](#firewall) + - [DNS architecture](#dns-architecture) + - [Cloud environment](#cloud-environment) + - [Cloud to on-prem](#cloud-to-on-prem) + - [On-prem to cloud](#on-prem-to-cloud) + - [How to run this stage](#how-to-run-this-stage) + - [Provider and Terraform variables](#provider-and-terraform-variables) + - [Impersonating the automation service account](#impersonating-the-automation-service-account) + - [Variable configuration](#variable-configuration) + - [Using delayed billing association for projects](#using-delayed-billing-association-for-projects) + - [Running the stage](#running-the-stage) + - [Post-deployment activities](#post-deployment-activities) + - [Private Google Access](#private-google-access) + - [Customizations](#customizations) + - [Changing default regions](#changing-default-regions) + - [Configuring the VPNs to on prem](#configuring-the-vpns-to-on-prem) + - [Adding an environment](#adding-an-environment) + - [Files](#files) + - [Variables](#variables) + - [Outputs](#outputs) + +## Design overview and choices + +### Multi-regional deployment + +The stage deploys the the infrastructure in two regions. By default, europe-west1 and europe-west4. Regional resources include NVAs and test VMs. This provides enough redundancy to be resilient to regional failures. +In case of a regional failure, the corresponding dynamic routes are withdrawn and traffic will failover in the secondary region. + +### VPC design + +The "landing zone" is divided into two VPC networks: + +- the trusted VPC: the connectivity hub towards other trusted networks +- the untrusted VPC: the connectivity hub towards any other untrusted network + +### NCC, NVAs and BGP sessions + +The VPCs connect through two sets of sample NVA machines: one per region, each containing two instances. The appliances run [Contrainer-Optimized OS](https://cloud.google.com/container-optimized-os/docs) and a container with [FRRouting](https://frrouting.org/). + +We levarage NCC-RA to allow the NVAs to establish BGP sessions with Cloud Routers in the untrusted and in the trusted VPCs. This allows Cloud Routers to advertise routes to the NVAs, and the NVAs to announce routes to the Cloud Router, so it can program them in the VPC. + +Specifically, each NVA establishes two BGP sessions (for redundancy) with the the Cloud Router deployed in the VPC and in the subnet where the interface of that VM is attached to. + +**Cloud Routers in the untrusted VPC advertise the default route (0.0.0.0/0) to the NVAs**. The NVAs advertise the route to the Cloud Routers in the trusted VPC. These dynamic routes are then imported through VPC peerings in the spokes. + +**Cloud Routers in the trusted hub advertis to the NVAs** all the subnets of the trusted VPCs. This includes the regional subnets and the cross-regional subnets. The NVAs manipulate the route costs (MED) before advertising them to the Cloud Routers in the untrusted VPC. This is done to guarantee symmetric traffic paths (more [here](https://medium.com/google-cloud/gcp-routing-adventures-vol-2-enterprise-multi-regional-deployments-in-google-cloud-3968e9591d59)). + +NVAs establish **extra BGP sessions with both cross-regional NVAs**. In this case, the NVAs advertise the regional trusted routes only. This allows cross-spoke (environment) traffic to remain also symmetric (more [here](https://medium.com/google-cloud/gcp-routing-adventures-vol-2-enterprise-multi-regional-deployments-in-google-cloud-3968e9591d59)). We set these routes to be exchanged at a lower cost than the one set for the other routes. + +Following the majority of real-life deployments, **we assume appliances to be stateful and not able to synchronize sessions between multiple NVAs within the same regional cluster**. For this reason, within each regional cluster, NVAs announce the same routes with different MED costs (1 point of difference between the primary and the secondary). This will cause traffic to go deterministically through one applaiance at the time within each region. You can change this default behavior modifying the cost settings in the [NVAs BGP configuration file](./data/bgp-config.tftpl). + +By default, the design assumes that: + +- on-premise networks (and related resources) are considered trusted. As such, the VPNs connecting with on-premises are terminated in GCP, in the trusted VPC +- the public Internet is considered untrusted. As such [Cloud NAT](https://cloud.google.com/nat/docs/overview) has been deployed in the untrusted landing VPC only. Also, the default route is set to carry traffic from the trusted VPCs, through the NVAs, to the untrusted VPC. +- cross-spoke (environment) traffic and traffic from any untrusted network to any trusted network (and vice versa) pass through the NVAs. +- any traffic from a trusted network to an untrusted network (e.g. Internet) is natted by the NVAs. Users can configure further exclusions. + +The trusted landing VPC acts as a hub: it bridges internal resources with the outside world and it hosts the shared services consumed by the spoke VPCs, connected to the hub thorugh VPC network peerings. Spokes are used to partition the environments. By default: + +- one spoke VPC hosts the development environment resources +- one spoke VPC hosts the production environment resources + +Each virtual network is a [shared VPC](https://cloud.google.com/vpc/docs/shared-vpc): shared VPCs are managed in dedicated *host projects* and shared with other *service projects* that consume the network resources. +Shared VPCs let organization administrators delegate administrative responsibilities, such as creating and managing instances, to Service Project Admins while maintaining centralized control over network resources like subnets, routes, and firewalls. + +Users can easily extend the design to host additional environments, or adopt different logical mappings for the spokes (for example, in order to create a new spoke for each company entity). Adding spokes is trivial and it does not increase the design complexity. The steps to add more spokes are provided in the following sections. + +In multi-organization scenarios, where production and non-production resources use different Cloud Identity and GCP organizations, the hub/landing VPC is usually part of the production organization. It establishes connections with the production spokes within the same organization, and with non-production spokes in a different organization. + +### External connectivity + +External connectivity to on-prem is implemented leveraging [Cloud HA VPN](https://cloud.google.com/network-connectivity/docs/vpn/concepts/topologies) (two tunnels per region). This is what users normally deploy as a final solution, or to validate routing and to transfer data, while waiting for [interconnects](https://cloud.google.com/network-connectivity/docs/interconnect) to be provisioned. + +Connectivity to additional on-prem sites or to other cloud providers should be implemented in a similar fashion, via VPN tunnels or interconnects, in the landing VPC (either trusted or untrusted, depending by the nature of the peers), sharing the same regional routers. + +### Internal connectivity + +Internal connectivity (e.g. between the trusted landing VPC and the spokes) is realized with VPC network peerings. As mentioned, there are other ways to implement connectivity. These can be easily retrofitted with minimal code changes, although they introduce additional considerations on service interoperability, quotas and management. + +This is an options summary: + +- [VPC Peering](https://cloud.google.com/vpc/docs/vpc-peering) (used here to connect the trusted landing VPC with the spokes, also used by [02-networking-vpn](../2-networking-b-vpn/)) + - Pros: no additional costs, full bandwidth with no configurations, no extra latency + - Cons: no transitivity (e.g. to GKE masters, Cloud SQL, etc.), no selective exchange of routes, several quotas and limits shared between VPCs in a peering group +- [Multi-NIC appliances](https://cloud.google.com/architecture/best-practices-vpc-design#multi-nic) (used here to connect the trusted landing and untrusted VPCs) + - Pros: provides additional security features (e.g. IPS), potentially better integration with on-prem systems by using the same vendor + - Cons: complex HA/failover setup, limited by VM bandwidth and scale, additional costs for VMs and licenses, out of band management of a critical cloud component +- [HA VPN](https://cloud.google.com/network-connectivity/docs/vpn/concepts/topologies) + - Pros: simple compatibility with GCP services that leverage peering internally, better control on routes, avoids peering groups shared quotas and limits + - Cons: additional costs, marginal increase in latency, requires multiple tunnels for full bandwidth + +### IP ranges, subnetting, routing + +Minimizing the number of routes (and subnets) in the cloud environment is important, as it simplifies management and it avoids hitting [Cloud Router](https://cloud.google.com/network-connectivity/docs/router/quotas) and [VPC](https://cloud.google.com/vpc/docs/quota) quotas and limits. For this reason, we recommend to carefully plan the IP space used in your cloud environment. This allows the use of larger IP CIDR blocks in routes, whenever possible. + +This stage uses a dedicated /16 block (10.128.0.0/16), which should be sized to the own needs. The subnets created in each VPC derive from this range. + +The /16 block is evenly split in eight, smaller /19 blocks, assigned to different areas of the GCP network: *landing untrusted europe-west1*, *landing untrusted europe-west4*, *landing trusted europe-west1*, *landing untrusted europe-west4*, *development europe-west1*, *development europe-west4*, *production europe-west1*, *production europe-west4*. + +The first /24 range in every area is allocated for a default subnet, which can be removed or modified as needed. + +Spoke VPCs also define and reserve three "special" CIDR ranges, derived from the respective /19, dedicated to + +- [PSA (Private Service Access)](https://cloud.google.com/vpc/docs/private-services-access): + + - The second-last /24 range is used for PSA (CloudSQL, Postrgres) + + - The third-last /24 range is used for PSA (CloudSQL, MySQL) + +- [Internal HTTPs Load Balancers (L7ILB)](https://cloud.google.com/load-balancing/docs/l7-internal): + + - The last /24 range + +This is a summary of the subnets allocated by default in this setup: + +| name | description | CIDR | +|---|---|---| +| landing-trusted-default-ew1 | Trusted landing subnet - europe-west1 | 10.128.64.0/24 | +| landing-trusted-default-ew4 | Trusted landing subnet - europe-west4 | 10.128.96.0/24 | +| landing-untrusted-default-ew1 | Untrusted landing subnet - europe-west1 | 10.128.0.0/24 | +| landing-untrusted-default-ew4 | Untrusted landing subnet - europe-west4 | 10.128.32.0/24 | +| dev-default-ew1 | Dev spoke subnet - europe-west1 | 10.128.128.0/24 | +| dev-default-ew1 (PSA MySQL) | PSA subnet for MySQL in dev spoke - europe-west1 | 10.128.157.0/24 | +| dev-default-ew1 (PSA SQL Server) | PSA subnet for Postgres in dev spoke - europe-west1 | 10.128.158.0/24 | +| dev-default-ew1 (L7 ILB) | L7 ILB subnet for dev spoke - europe-west1 | 10.128.92.0/24 | +| dev-default-ew4 | Dev spoke subnet - europe-west4 | 10.128.160.0/24 | +| dev-default-ew4 (PSA MySQL) | PSA subnet for MySQL in dev spoke - europe-west4 | 10.128.189.0/24 | +| dev-default-ew4 (PSA SQL Server) | PSA subnet for Postgres in dev spoke - europe-west4 | 10.128.190.0/24 | +| dev-default-ew4 (L7 ILB) | L7 ILB subnet for dev spoke - europe-west4 | 10.128.93.0/24 | +| prod-default-ew1 | Prod spoke subnet - europe-west1 | 10.128.192.0/24 | +| prod-default-ew1 (PSA MySQL) | PSA subnet for MySQL in prod spoke - europe-west1 | 10.128.221.0/24 | +| prod-default-ew1 (PSA SQL Server) | PSA subnet for Postgres in prod spoke - europe-west1 | 10.128.253.0/24 | +| prod-default-ew1 (L7 ILB) | L7 ILB subnet for prod spoke - europe-west1 | 10.128.60.0/24 | +| prod-default-ew4 | Prod spoke subnet - europe-west4 | 10.128.224.0/24 | +| prod-default-ew4 (PSA MySQL) | PSA subnet for MySQL in prod spoke - europe-west4 | 10.128.222.0/24 | +| prod-default-ew4 (PSA SQL Server) | PSA subnet for Postgres in prod spoke - europe-west4 | 10.128.254.0/24 | +| prod-default-ew4 (L7 ILB) | L7 ILB subnet for prod spoke - europe-west4 | 10.128.61.0/24 | + +These subnets are advertised to on-premises as a whole /16 range (10.128.0.0/16). + +Routes in GCP are either automatically created (for example, when a subnet is added to a VPC), manually created via static routes, dynamically exchanged through VPC peerings, or dynamically programmed by [Cloud Routers](https://cloud.google.com/network-connectivity/docs/router#docs) when a BGP session is established. BGP sessions can be configured to advertise VPC ranges, and/or custom ranges via custom advertisements. + +In this setup: + +- routes between multiple subnets within the same VPC are automatically exchanged by GCP +- the spokes and the trusted landing VPC exchange dynamic routes through VPC peerings +- on-premises is connected to the trusted landing VPC and it dynamically exchanges BGP routes with GCP (with the trusted VPC) using HA VPN +- the NVAs exchange dynamic routes using BGP with Cloud Routers in the untrusted VPC, Cloud Routers in the trusted VPC and cross-regional NVAs. This allows VMs in different environments and different regions to communicate. + +The Cloud Routers (connected to the VPN gateways in the trusted VPC) are configured to exclude the default advertisement of VPC ranges and they only advertise their respective aggregate ranges, via custom advertisements. This greatly simplifies the routing configuration and avoids quota or limit issues, by keeping the number of routes small, instead of making it proportional to the subnets and to the secondary ranges in the VPCs. + +### Internet egress + +In this setup, Internet egress is realized through [Cloud NAT](https://cloud.google.com/nat/docs/overview), deployed in the untrusted landing VPC. This allows instances in all other VPCs to reach the Internet, passing through the NVAs (being the public Internet considered untrusted). + +Several other scenarios are possible, with various degrees of complexity: + +- deploy Cloud NAT in every VPC +- add forwarding proxies, with optional URL filters +- send Internet traffic to on-premises, so the existing egress infrastructure can be leveraged + +Future pluggable modules will allow users to easily experiment with the above scenarios. + +### VPC and Hierarchical Firewall + +The GCP Firewall is a stateful, distributed feature that allows the creation of L4 policies, either via VPC-level rules or -more recently- via hierarchical policies, applied on the resource hierarchy (organization, folders). + +The current setup adopts both firewall types. Hierarchical firewall rules are applied in the networking folder for common ingress rules (egress is open by default): for example, it allows the health checks and the IAP forwarders traffic to reach the VMs. + +Rules and policies are defined in simple YAML files, described below. + +### DNS + +DNS goes hand in hand with networking, especially on GCP where Cloud DNS zones and policies are associated at the VPC level. This setup implements both DNS flows: + +- on-prem to cloud via private zones for cloud-managed domains, and an [inbound policy](https://cloud.google.com/dns/docs/server-policies-overview#dns-server-policy-in) used as forwarding target or via delegation (requires some extra configuration) from on-prem DNS resolvers +- cloud to on-prem via forwarding zones for the on-prem managed domains + +DNS configuration is further centralized by leveraging peering zones, so that + +- the hub/landing Cloud DNS hosts configurations for on-prem forwarding, Google API domains, and the top-level private zone/s (e.g. gcp.example.com) +- the spokes Cloud DNS host configurations for the environment-specific domains (e.g. prod.gcp.example.com), which are bound to the hub/landing leveraging [cross-project binding](https://cloud.google.com/dns/docs/zones/zones-overview#cross-project_binding); a peering zone for the `.` (root) zone is then created on each spoke, delegating all DNS resolution to hub/landing. +- Private Google Access is enabled for a selection of the [supported domains](https://cloud.google.com/vpc/docs/configure-private-google-access#domain-options), namely + - `private.googleapis.com` + - `restricted.googleapis.com` + - `gcr.io` + - `packages.cloud.google.com` + - `pkg.dev` + - `pki.goog` + +To complete the configuration, the 35.199.192.0/19 range should be routed to the VPN tunnels from on-premises, and the following names should be configured for DNS forwarding to cloud: + +- `private.googleapis.com` +- `restricted.googleapis.com` +- `gcp.example.com` (used as a placeholder) + +In GCP, a forwarding zone in the landing project is configured to forward queries to the placeholder domain `onprem.example.com` to on-premises. + +This configuration is battle-tested, and flexible enough to lend itself to simple modifications without subverting its design. + +## Stage structure and files layout + +### VPCs + +VPCs are defined in separate files, one for `landing` (trusted and untrusted), one for `prod` and one for `dev`. + +These files contain different resources: + +- **project** ([`projects`](../../../modules/project)): the "[host projects](https://cloud.google.com/vpc/docs/shared-vpc)" containing the VPCs and enabling the required APIs. +- **VPCs** ([`net-vpc`](../../../modules/net-vpc)): manage the subnets, the explicit routes for `{private,restricted}.googleapis.com` and the DNS inbound policy (for the trusted landing VPC). Subnets are created leveraging "resource factories": the configuration is separated from the module that implements it, and stored in a well-structured file. To add a new subnet, simply create a new file in the `data_folder` directory defined in the module, following the examples found in the [Fabric `net-vpc` documentation](../../../modules/net-vpc#subnet-factory). Sample subnets are shipped in [data/subnets](./data/subnets) and can be easily customized to fit users' needs. + +Subnets for [L7 ILBs](https://cloud.google.com/load-balancing/docs/l7-internal/proxy-only-subnets) are handled differently, and defined in variable `l7ilb_subnets`, while ranges for [PSA](https://cloud.google.com/vpc/docs/configure-private-services-access#allocating-range) are configured by variable `psa_ranges` - such variables are consumed by spoke VPCs. + +- **Cloud NAT** ([`net-cloudnat`](../../../modules/net-cloudnat)) (in the untrusted landing VPC only): it manages the networking infrastructure required to enable the Internet egress. + +### VPNs + +The connectivity between on-premises and GCP (the trusted landing VPC) is implemented with Cloud HA VPN ([`net-vpn`](../../../modules/net-vpn-ha)) and defined in [`vpn-onprem.tf`](./vpn-onprem.tf). The file implements a single logical connection between on-premises and the trusted landing VPC, both in `europe-west1` and `europe-west4`. The relevant parameters for its configuration are found in the variables `vpn_onprem_primary_config` and `vpn_onprem_secondary_config`. + +### Routing and BGP + +Each VPC network ([`net-vpc`](../../../modules/net-vpc)) manages a separate routing table, where you can define static routes (e.g. to private.googleapis.com) and receives dynamic routes through VPC peering and BGP sessions established with the neighbor networks (e.g. NCC routers, routers on-premises). + +NCC/Cloud Router BGP settings are defined in `ncc.tf`. +NVA BGP settings are defined in the [bpg-config.tftpl template file](./data/bgp-config.tftpl). +The variable `ncc_asn` allows to change the Autonomous System Number (ASN) assigned to the untrusted VPC Cloud Routers, to the trusted VPC Cloud Routers and to the NVAs. + +BGP sessions for trusted landing to on-premises are configured through the variable `vpn_onprem_configs`. + +### Firewall + +**VPC firewall rules** ([`net-vpc-firewall`](../../../modules/net-vpc-firewall)) are defined per-vpc on each `vpc-*.tf` file and leverage a resource factory to massively create rules. +To add a new firewall rule, create a new file or edit an existing one in the `data_folder` directory defined in the module `net-vpc-firewall`, following the examples of the "[Rules factory](../../../modules/net-vpc-firewall#rules-factory)" section of the module documentation. Sample firewall rules are shipped in [data/firewall-rules/landing-untrusted](./data/firewall-rules/landing-untrusted) and in [data/firewall-rules/landing-trusted](./data/firewall-rules/landing-trusted), and can be easily customized. + +**Hierarchical firewall policies** ([`folder`](../../../modules/folder)) are defined in `main.tf`, and managed through a policy factory implemented by the `folder` module, which applies the defined hierarchical to the `Networking` folder, which contains all the core networking infrastructure. Policies are defined in the `rules_file` file - to define a new one simply use the instructions found on "[Firewall policy factory](../../../modules/organization#firewall-policy-factory)". Sample hierarchical firewall policies are shipped in [data/hierarchical-policy-rules.yaml](./data/hierarchical-policy-rules.yaml) and can be easily customized. + +### DNS architecture + +The DNS ([`dns`](../../../modules/dns)) infrastructure is defined in [`dns-*.tf`] files. + +Cloud DNS manages onprem forwarding, the main GCP zone (in this example `gcp.example.com`) and environment-specific zones (i.e. `dev.gcp.example.com` and `prod.gcp.example.com`). + +#### Cloud environment + +The root DNS zone defined in the landing project acts as the source of truth for DNS within the Cloud environment. The resources defined in the spoke VPCs consume the landing DNS infrastructure through DNS peering (e.g. `prod-landing-root-dns-peering`). +The spokes can optionally define private zones (e.g. `prod-dns-private-zone`). Granting visibility both to the trusted and untrusted landing VPCs ensures that the whole cloud environment can query such zones. + +#### Cloud to on-prem + +Leveraging the forwarding zone defined in the landing project (e.g. `onprem-example-dns-forwarding` and `reverse-10-dns-forwarding`), the cloud environment can resolve `in-addr.arpa.` and `onprem.example.com.` using the on-premise DNS infrastructure. On-premise resolver IPs are set in the variable `dns.onprem`. + +DNS queries sent to the on-premise infrastructure come from the `35.199.192.0/19` source range. + +#### On-prem to cloud + +The [Inbound DNS Policy](https://cloud.google.com/dns/docs/server-policies-overview#dns-server-policy-in) defined in the *trusted landing VPC module* ([`landing.tf`](./landing.tf)) automatically reserves the first available IP address on each subnet (typically the third one in a CIDR) to expose the Cloud DNS service, so that it can be consumed from outside of GCP. + +## How to run this stage + +This stage is meant to be executed after the [resource management](../1-resman) stage has run, as it leverages the automation service account and bucket created there, and additional resources configured in the [bootstrap](../0-bootstrap) stage. + +It's of course possible to run this stage in isolation, but that's outside the scope of this document, and you would need to refer to the code for the previous stages for the environmental requirements. + +Before running this stage, you need to make sure you have the correct credentials and permissions, and localize variables by assigning values that match your configuration. + +### Provider and Terraform variables + +As all other FAST stages, the [mechanism used to pass variable values and pre-built provider files from one stage to the next](../0-bootstrap/README.md#output-files-and-cross-stage-variables) is also leveraged here. + +The commands to link or copy the provider and terraform variable files can be easily derived from the `stage-links.sh` script in the FAST root folder, passing it a single argument with the local output files folder (if configured) or the GCS output bucket in the automation project (derived from stage 0 outputs). The following examples demonstrate both cases, and the resulting commands that then need to be copy/pasted and run. + +```bash +../../stage-links.sh ~/fast-config + +# copy and paste the following commands for '2-networking-a-peering' + +ln -s ~/fast-config/providers/2-networking-providers.tf ./ +ln -s ~/fast-config/tfvars/globals.auto.tfvars.json ./ +ln -s ~/fast-config/tfvars/0-bootstrap.auto.tfvars.json ./ +ln -s ~/fast-config/tfvars/1-resman.auto.tfvars.json ./ +``` + +```bash +../../stage-links.sh gs://xxx-prod-iac-core-outputs-0 + +# copy and paste the following commands for '2-networking-a-peering' + +gcloud alpha storage cp gs://xxx-prod-iac-core-outputs-0/providers/2-networking-providers.tf ./ +gcloud alpha storage cp gs://xxx-prod-iac-core-outputs-0/tfvars/globals.auto.tfvars.json ./ +gcloud alpha storage cp gs://xxx-prod-iac-core-outputs-0/tfvars/0-bootstrap.auto.tfvars.json ./ +gcloud alpha storage cp gs://xxx-prod-iac-core-outputs-0/tfvars/1-resman.auto.tfvars.json ./ +``` + +### Impersonating the automation service account + +The preconfigured provider file uses impersonation to run with this stage's automation service account's credentials. The `gcp-devops` and `organization-admins` groups have the necessary IAM bindings in place to do that, so make sure the current user is a member of one of those groups. + +### Variable configuration + +Variables in this stage -- like most other FAST stages -- are broadly divided into three separate sets: + +- variables which refer to global values for the whole organization (org id, billing account id, prefix, etc.), which are pre-populated via the `globals.auto.tfvars.json` file linked or copied above +- variables which refer to resources managed by previous stage, which are prepopulated here via the `0-bootstrap.auto.tfvars.json` and `1-resman.auto.tfvars.json` files linked or copied above +- and finally variables that optionally control this stage's behaviour and customizations, and can to be set in a custom `terraform.tfvars` file + +The latter set is explained in the [Customization](#customizations) sections below, and the full list can be found in the [Variables](#variables) table at the bottom of this document. + +Note that the `outputs_location` variable is disabled by default, you need to explicitly set it in your `terraform.tfvars` file if you want output files to be generated by this stage. This is a sample `terraform.tfvars` that configures it, refer to the [bootstrap stage documentation](../0-bootstrap/README.md#output-files-and-cross-stage-variables) for more details: + +```hcl +outputs_location = "~/fast-config" +``` + +### Using delayed billing association for projects + +This configuration is possible but unsupported and only exists for development purposes, use at your own risk: + +- temporarily switch `billing_account.id` to `null` in `globals.auto.tfvars.json` +- for each project resources in the project modules used in this stage (`dev-spoke-project`, `landing-project`, `prod-spoke-project`) + - apply using `-target`, for example + `terraform apply -target 'module.landing-project.google_project.project[0]'` + - untaint the project resource after applying, for example + `terraform untaint 'module.landing-project.google_project.project[0]'` +- go through the process to associate the billing account with the two projects +- switch `billing_account.id` back to the real billing account id +- resume applying normally + +### Running the stage + +Once provider and variable values are in place and the correct user is configured, the stage can be run: + +```bash +terraform init +terraform apply +``` + +### Post-deployment activities + +- On-prem routers should be configured to advertise all relevant CIDRs to the GCP environments. To avoid hitting GCP quotas, we recomment aggregating routes as much as possible. +- On-prem routers should accept BGP sessions from their cloud peers. +- On-prem DNS servers should have forward zones for GCP-managed ones. + +#### Private Google Access + +[Private Google Access](https://cloud.google.com/vpc/docs/private-google-access) (or PGA) enables VMs and on-prem systems to consume Google APIs from within the Google network, and is already fully configured on this environment. + +For PGA to work: + +- Private Google Access should be enabled on the subnet. \ +Subnets created by the `net-vpc` module are PGA-enabled by default. + +- 199.36.153.4/30 (`restricted.googleapis.com`) and 199.36.153.8/30 (`private.googleapis.com`) should be routed from on-prem to VPC, and from there to the `default-internet-gateway`. \ +Per variable `vpn_onprem_configs` such ranges are advertised to onprem - furthermore every VPC (e.g. see `landing-vpc` in [`landing.tf`](./landing.tf)) has explicit routes set in case the `0.0.0.0/0` route is changed. + +- A private DNS zone for `googleapis.com` should be created and configured per [this article](https://cloud.google.com/vpc/docs/configure-private-google-access-hybrid#config-domain), as implemented in module `googleapis-private-zone` in [`dns-landing.tf`](./dns-landing.tf) + +## Customizations + +### Changing default regions + +Regions are defined via the `regions` variable which sets up a mapping between the `regions.primary` and `regions.secondary` logical names and actual GCP region names. If you need to change regions from the defaults: + +- change the values of the mappings in the `regions` variable to the regions you are going to use +- change the regions in the factory subnet files in the `data` folder + +### Configuring the VPNs to on prem + +This stage includes basic support for an HA VPN connecting the landing zone in the primary region to on prem. Configuration is via the `vpn_onprem_primary_config` and `vpn_onprem_secondary_config` variables, that closely mirrors the variables defined in the [`net-vpn-ha`](../../../modules/net-vpn-ha/). + +Support for the onprem VPNs is disabled by default so that no resources are created, this is an example of how to configure one variable to enable the VPN in the primary region: + +```hcl +vpn_onprem_primary_config = { + peer_external_gateways = { + default = { + redundancy_type = "SINGLE_IP_INTERNALLY_REDUNDANT" + interfaces = ["8.8.8.8"] + } + } + router_config = { + asn = 65501 + custom_advertise = { + all_subnets = false + ip_ranges = { + "10.1.0.0/16" = "gcp" + "35.199.192.0/19" = "gcp-dns" + "199.36.153.4/30" = "gcp-restricted" + } + } + } + tunnels = { + "0" = { + bgp_peer = { + address = "169.254.1.1" + asn = 65500 + } + bgp_session_range = "169.254.1.2/30" + peer_external_gateway_interface = 0 + shared_secret = "foo" + vpn_gateway_interface = 0 + } + "1" = { + bgp_peer = { + address = "169.254.2.1" + asn = 64513 + } + bgp_session_range = "169.254.2.2/30" + peer_external_gateway_interface = 1 + shared_secret = "foo" + vpn_gateway_interface = 1 + } + } +} +# tftest skip +``` + +### Adding an environment + +To create a new environment (e.g. `staging`), a few changes are required: + +Create a `spoke-staging.tf` file by copying `spoke-prod.tf` file. +Adapt the new file by replacing the value "prod" with the value "staging". +Running `diff spoke-dev.tf spoke-prod.tf` can help to see how environment files differ. + +The new VPC requires a set of dedicated CIDRs, one per region, added to variable `gcp_ranges` (for example as `spoke_staging_ew1` and `spoke_staging_ew4`). +`gcp_ranges` is a map that "resolves" CIDR names to the actual addresses, and will be used later to configure routing. + +Variables managing L7 Internal Load Balancers (`l7ilb_subnets`) and Private Service Access (`psa_ranges`) should also be adapted, and subnets and firewall rules for the new spoke should be added, as described above. + +Configure the NVAs deployed updating the sample BGP [config file](./data/bgp-config.tftpl). + +DNS configurations are centralised in the `dns-*.tf` files. Spokes delegate DNS resolution to Landing through DNS peering, and optionally define a private zone (e.g. `dev.gcp.example.com`) which the landing peers to. To configure DNS for a new environment, copy one of the other environments DNS files [e.g. (dns-dev.tf)](dns-dev.tf) into a new `dns-*.tf` file suffixed with the environment name (e.g. `dns-staging.tf`), and update its content accordingly. Don't forget to add a peering zone from the landing to the newly created environment private zone. + + + + +## Files + +| name | description | modules | resources | +|---|---|---|---| +| [dns-dev.tf](./dns-dev.tf) | Development spoke DNS zones and peerings setup. | dns | | +| [dns-landing.tf](./dns-landing.tf) | Landing DNS zones and peerings setup. | dns | | +| [dns-prod.tf](./dns-prod.tf) | Production spoke DNS zones and peerings setup. | dns | | +| [landing.tf](./landing.tf) | Landing VPC and related resources. | net-cloudnat · net-vpc · net-vpc-firewall · project | | +| [main.tf](./main.tf) | Networking folder and hierarchical policy. | folder | | +| [monitoring.tf](./monitoring.tf) | Network monitoring dashboards. | | google_monitoring_dashboard | +| [ncc.tf](./ncc.tf) | None | ncc-spoke-ra | | +| [nva.tf](./nva.tf) | None | compute-vm · simple-nva | google_compute_address | +| [outputs.tf](./outputs.tf) | Module outputs. | | google_storage_bucket_object · local_file | +| [regions.tf](./regions.tf) | Compute short names for regions. | | | +| [spoke-dev.tf](./spoke-dev.tf) | Dev spoke VPC and related resources. | net-vpc · net-vpc-firewall · net-vpc-peering · project | google_project_iam_binding | +| [spoke-prod.tf](./spoke-prod.tf) | Production spoke VPC and related resources. | net-vpc · net-vpc-firewall · net-vpc-peering · project | google_project_iam_binding | +| [test-resources.tf](./test-resources.tf) | temporary instances for testing | compute-vm | | +| [variables.tf](./variables.tf) | Module variables. | | | +| [vpn-onprem.tf](./vpn-onprem.tf) | VPN between landing and onprem. | net-vpn-ha | | + +## Variables + +| name | description | type | required | default | producer | +|---|---|:---:|:---:|:---:|:---:| +| [automation](variables.tf#L17) | Automation resources created by the bootstrap stage. | object({…}) | ✓ | | 0-bootstrap | +| [billing_account](variables.tf#L25) | Billing account id. If billing account is not part of the same org set `is_org_level` to false. | object({…}) | ✓ | | 0-bootstrap | +| [folder_ids](variables.tf#L75) | Folders to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created. | object({…}) | ✓ | | 1-resman | +| [organization](variables.tf#L119) | Organization details. | object({…}) | ✓ | | 0-bootstrap | +| [prefix](variables.tf#L135) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | 0-bootstrap | +| [custom_roles](variables.tf#L38) | Custom roles defined at the org level, in key => id format. | object({…}) | | null | 0-bootstrap | +| [dns](variables.tf#L47) | Onprem DNS resolvers. | map(list(string)) | | {…} | | +| [factories_config](variables.tf#L55) | Configuration for network resource factories. | object({…}) | | {…} | | +| [gcp_ranges](variables.tf#L85) | GCP address ranges in name => range format. | map(string) | | {…} | | +| [ncc_asn](variables.tf#L100) | The NCC Cloud Routers ASN configuration. | map(number) | | {…} | | +| [onprem_cidr](variables.tf#L111) | Onprem addresses in name => range format. | map(string) | | {…} | | +| [outputs_location](variables.tf#L129) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | +| [psa_ranges](variables.tf#L146) | IP ranges used for Private Service Access (e.g. CloudSQL). Ranges is in name => range format. | object({…}) | | null | | +| [regions](variables.tf#L167) | Region definitions. | object({…}) | | {…} | | +| [service_accounts](variables.tf#L179) | Automation service accounts in name => email format. | object({…}) | | null | 1-resman | +| [vpn_onprem_primary_config](variables.tf#L193) | VPN gateway configuration for onprem interconnection in the primary region. | object({…}) | | null | | +| [vpn_onprem_secondary_config](variables.tf#L236) | VPN gateway configuration for onprem interconnection in the secondary region. | object({…}) | | null | | +| [zones](variables.tf#L279) | Zones in which NVAs are deployed. | list(string) | | ["b", "c"] | | + +## Outputs + +| name | description | sensitive | consumers | +|---|---|:---:|---| +| [host_project_ids](outputs.tf#L58) | Network project ids. | | | +| [host_project_numbers](outputs.tf#L63) | Network project numbers. | | | +| [shared_vpc_self_links](outputs.tf#L68) | Shared VPC host projects. | | | +| [tfvars](outputs.tf#L73) | Terraform variables file for the following stages. | ✓ | | +| [vpn_gateway_endpoints](outputs.tf#L79) | External IP Addresses for the GCP VPN gateways. | | | + + diff --git a/fast/stages/2-networking-e-nva-bgp/data/bgp-config.tftpl b/fast/stages/2-networking-e-nva-bgp/data/bgp-config.tftpl new file mode 100644 index 0000000000..53009c17a1 --- /dev/null +++ b/fast/stages/2-networking-e-nva-bgp/data/bgp-config.tftpl @@ -0,0 +1,76 @@ +# NVAs configuration template + +log syslog informational +no ipv6 forwarding +service integrated-vtysh-config + +interface lo + ip address ${ip_untrusted}/32 + +ip prefix-list DEFAULT seq 10 permit 0.0.0.0/0 +! +ip prefix-list PRIMARY seq 10 permit ${gcp_landing_trusted_primary} +ip prefix-list PRIMARY seq 20 permit ${gcp_dev_primary} +ip prefix-list PRIMARY seq 30 permit ${gcp_prod_primary} +! +ip prefix-list SECONDARY seq 10 permit ${gcp_landing_trusted_secondary} +ip prefix-list SECONDARY seq 20 permit ${gcp_dev_secondary} +ip prefix-list SECONDARY seq 30 permit ${gcp_prod_secondary} + +route-map TO-UNTRUSTED permit 10 + match ip address prefix-list PRIMARY + set metric ${cost_primary} +! +route-map TO-UNTRUSTED permit 20 + match ip address prefix-list SECONDARY + set metric ${cost_secondary} +! +route-map TO-TRUSTED permit 10 + match ip address prefix-list DEFAULT + set metric 100 +! +route-map TO-NVA permit 10 + match ip address prefix-list ${announce-to-nva} + set metric 50 + +router bgp ${asn_nva} + bgp router-id ${ip_untrusted} + bgp bestpath as-path ignore + bgp disable-ebgp-connected-route-check + bgp timers 20 60 +! + no bgp ebgp-requires-policy + no bgp network import-check +! + neighbor ${ip_neighbor_untrusted_0} remote-as ${asn_untrusted} + neighbor ${ip_neighbor_untrusted_1} remote-as ${asn_untrusted} +! + neighbor ${ip_neighbor_trusted_0} remote-as ${asn_trusted} + neighbor ${ip_neighbor_trusted_0} update-source ${ip_trusted} + neighbor ${ip_neighbor_trusted_1} remote-as ${asn_trusted} + neighbor ${ip_neighbor_trusted_1} update-source ${ip_trusted} +! + neighbor ${ip_neighbor_cross_region_nva_0} remote-as ${asn_nva_cross_region} + neighbor ${ip_neighbor_cross_region_nva_0} ebgp-multihop 2 + neighbor ${ip_neighbor_cross_region_nva_1} remote-as ${asn_nva_cross_region} + neighbor ${ip_neighbor_cross_region_nva_1} ebgp-multihop 2 +! + address-family ipv4 unicast + neighbor ${ip_neighbor_untrusted_0} route-map TO-UNTRUSTED out + neighbor ${ip_neighbor_untrusted_0} soft-reconfiguration inbound +! + neighbor ${ip_neighbor_untrusted_1} route-map TO-UNTRUSTED out + neighbor ${ip_neighbor_untrusted_1} soft-reconfiguration inbound +! + neighbor ${ip_neighbor_trusted_0} route-map TO-TRUSTED out + neighbor ${ip_neighbor_trusted_0} soft-reconfiguration inbound +! + neighbor ${ip_neighbor_trusted_1} route-map TO-TRUSTED out + neighbor ${ip_neighbor_trusted_1} soft-reconfiguration inbound +! + neighbor ${ip_neighbor_cross_region_nva_0} route-map TO-NVA out + neighbor ${ip_neighbor_cross_region_nva_0} soft-reconfiguration inbound +! + neighbor ${ip_neighbor_cross_region_nva_1} route-map TO-NVA out + neighbor ${ip_neighbor_cross_region_nva_1} soft-reconfiguration inbound + exit-address-family diff --git a/fast/stages/2-networking-e-nva-bgp/data/cidrs.yaml b/fast/stages/2-networking-e-nva-bgp/data/cidrs.yaml new file mode 100644 index 0000000000..93d7bb0b12 --- /dev/null +++ b/fast/stages/2-networking-e-nva-bgp/data/cidrs.yaml @@ -0,0 +1,27 @@ +# skip boilerplate check + +healthchecks: + - 35.191.0.0/16 + - 130.211.0.0/22 + - 209.85.152.0/22 + - 209.85.204.0/22 + +ncc_cloud_routers_trusted: + - 10.128.64.201/32 + - 10.128.64.202/32 + - 10.128.96.201/32 + - 10.128.96.202/32 + +ncc_cloud_routers_untrusted: + - 10.128.0.201/32 + - 10.128.0.202/32 + - 10.128.32.201/32 + - 10.128.32.202/32 + +rfc1918: + - 10.0.0.0/8 + - 172.16.0.0/12 + - 192.168.0.0/16 + +onprem_probes: + - 10.255.255.254/32 diff --git a/fast/stages/2-networking-e-nva-bgp/data/dashboards/firewall_insights.json b/fast/stages/2-networking-e-nva-bgp/data/dashboards/firewall_insights.json new file mode 100644 index 0000000000..4c0ebe2878 --- /dev/null +++ b/fast/stages/2-networking-e-nva-bgp/data/dashboards/firewall_insights.json @@ -0,0 +1,68 @@ +{ + "displayName": "Firewall Insights Monitoring", + "gridLayout": { + "columns": "2", + "widgets": [ + { + "title": "Subnet Firewall Hit Counts", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_RATE" + }, + "filter": "metric.type=\"firewallinsights.googleapis.com/subnet/firewall_hit_count\" resource.type=\"gce_subnetwork\"", + "secondaryAggregation": {} + }, + "unitOverride": "1" + } + } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" + } + } + }, + { + "title": "VM Firewall Hit Counts", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_RATE" + }, + "filter": "metric.type=\"firewallinsights.googleapis.com/vm/firewall_hit_count\" resource.type=\"gce_instance\"", + "secondaryAggregation": {} + }, + "unitOverride": "1" + } + } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" + } + } + } + ] + } +} diff --git a/fast/stages/2-networking-e-nva-bgp/data/dashboards/vpn.json b/fast/stages/2-networking-e-nva-bgp/data/dashboards/vpn.json new file mode 100644 index 0000000000..1aec3e45b2 --- /dev/null +++ b/fast/stages/2-networking-e-nva-bgp/data/dashboards/vpn.json @@ -0,0 +1,248 @@ +{ + "displayName": "VPN Monitoring", + "gridLayout": { + "columns": "2", + "widgets": [ + { + "title": "Number of connections", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_MEAN" + }, + "filter": "metric.type=\"vpn.googleapis.com/gateway/connections\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} + }, + "unitOverride": "1" + } + } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" + } + } + }, + { + "title": "Tunnel established", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_MEAN" + }, + "filter": "metric.type=\"vpn.googleapis.com/tunnel_established\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} + }, + "unitOverride": "1" + } + } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" + } + } + }, + { + "title": "Cloud VPN Gateway - Received bytes", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_RATE" + }, + "filter": "metric.type=\"vpn.googleapis.com/network/received_bytes_count\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} + }, + "unitOverride": "By" + } + } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" + } + } + }, + { + "title": "Cloud VPN Gateway - Sent bytes", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_RATE" + }, + "filter": "metric.type=\"vpn.googleapis.com/network/sent_bytes_count\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} + }, + "unitOverride": "By" + } + } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" + } + } + }, + { + "title": "Cloud VPN Gateway - Received packets", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_RATE" + }, + "filter": "metric.type=\"vpn.googleapis.com/network/received_packets_count\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} + }, + "unitOverride": "{packets}" + } + } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" + } + } + }, + { + "title": "Cloud VPN Gateway - Sent packets", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_RATE" + }, + "filter": "metric.type=\"vpn.googleapis.com/network/sent_packets_count\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} + }, + "unitOverride": "{packets}" + } + } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" + } + } + }, + { + "title": "Incoming packets dropped", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_RATE" + }, + "filter": "metric.type=\"vpn.googleapis.com/network/dropped_received_packets_count\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} + }, + "unitOverride": "1" + } + } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" + } + } + }, + { + "title": "Outgoing packets dropped", + "xyChart": { + "chartOptions": { + "mode": "COLOR" + }, + "dataSets": [ + { + "minAlignmentPeriod": "60s", + "plotType": "LINE", + "targetAxis": "Y1", + "timeSeriesQuery": { + "timeSeriesFilter": { + "aggregation": { + "perSeriesAligner": "ALIGN_RATE" + }, + "filter": "metric.type=\"vpn.googleapis.com/network/dropped_sent_packets_count\" resource.type=\"vpn_gateway\"", + "secondaryAggregation": {} + }, + "unitOverride": "1" + } + } + ], + "timeshiftDuration": "0s", + "yAxis": { + "label": "y1Axis", + "scale": "LINEAR" + } + } + } + ] + } +} diff --git a/fast/stages/2-networking-e-nva-bgp/data/firewall-rules/dev/rules.yaml b/fast/stages/2-networking-e-nva-bgp/data/firewall-rules/dev/rules.yaml new file mode 100644 index 0000000000..cab42edc94 --- /dev/null +++ b/fast/stages/2-networking-e-nva-bgp/data/firewall-rules/dev/rules.yaml @@ -0,0 +1,21 @@ +# skip boilerplate check + +ingress: + ingress-allow-composer-nodes: + description: "Allow traffic to Composer nodes." + sources: + - composer-worker + targets: + - composer-worker + rules: + - protocol: tcp + ports: [80, 443, 3306, 3307] + ingress-allow-dataflow-load: + description: "Allow traffic to Dataflow nodes." + sources: + - dataflow + targets: + - dataflow + rules: + - protocol: tcp + ports: [12345, 12346] diff --git a/fast/stages/2-networking-e-nva-bgp/data/firewall-rules/landing-trusted/rules.yaml b/fast/stages/2-networking-e-nva-bgp/data/firewall-rules/landing-trusted/rules.yaml new file mode 100644 index 0000000000..6e00603bd5 --- /dev/null +++ b/fast/stages/2-networking-e-nva-bgp/data/firewall-rules/landing-trusted/rules.yaml @@ -0,0 +1,31 @@ +# skip boilerplate check + +ingress: + allow-hc-nva-ssh-trusted: + description: "Allow traffic from Google healthchecks to NVA appliances" + source_ranges: + - healthchecks + rules: + - protocol: tcp + ports: + - 22 + allow-onprem-probes-trusted-example: + description: "Allow traffic from onprem probes" + source_ranges: + - onprem_probes + rules: + - protocol: tcp + ports: + - 12345 + # This is not really needed, but it's good to have it + # in place if the more generic hierarchical firewall policies + # get deleted + allow-ncc-nva-bgp-trusted: + description: "Allow BGP traffic from NCC Cloud Routers to NVAs" + source_ranges: + - ncc_cloud_routers_trusted + targets: ["nva"] + rules: + - protocol: tcp + ports: + - 179 diff --git a/fast/stages/2-networking-e-nva-bgp/data/firewall-rules/landing-untrusted/rules.yaml b/fast/stages/2-networking-e-nva-bgp/data/firewall-rules/landing-untrusted/rules.yaml new file mode 100644 index 0000000000..c6077013cf --- /dev/null +++ b/fast/stages/2-networking-e-nva-bgp/data/firewall-rules/landing-untrusted/rules.yaml @@ -0,0 +1,31 @@ +# skip boilerplate check + +ingress: + allow-hc-nva-ssh-untrusted: + description: "Allow traffic from Google healthchecks to NVA appliances" + source_ranges: + - healthchecks + rules: + - protocol: tcp + ports: + - 22 + # these are not really needed, but it's good to have them + # in place if the more generic hierarchical firewall policies + # get deleted + allow-ncc-nva-bgp-untrusted: + description: "Allow BGP traffic from NCC Cloud Routers to NVAs" + source_ranges: + - ncc_cloud_routers_untrusted + targets: ["nva"] + rules: + - protocol: tcp + ports: + - 179 + allow-nva-nva-bgp-untrusted: + description: "Allow BGP traffic from cross-regional NVAs" + sources: ["nva"] + targets: ["nva"] + rules: + - protocol: tcp + ports: + - 179 diff --git a/fast/stages/2-networking-e-nva-bgp/data/hierarchical-policy-rules.yaml b/fast/stages/2-networking-e-nva-bgp/data/hierarchical-policy-rules.yaml new file mode 100644 index 0000000000..0172a3091e --- /dev/null +++ b/fast/stages/2-networking-e-nva-bgp/data/hierarchical-policy-rules.yaml @@ -0,0 +1,49 @@ +# skip boilerplate check + +allow-admins: + description: Access from the admin subnet to all subnets + direction: INGRESS + action: allow + priority: 1000 + ranges: + - $rfc1918 + ports: + all: [] + target_resources: null + enable_logging: false + +allow-healthchecks: + description: Enable HTTP and HTTPS healthchecks + direction: INGRESS + action: allow + priority: 1001 + ranges: + - $healthchecks + ports: + tcp: ["80", "443"] + target_resources: null + enable_logging: false + +allow-ssh-from-iap: + description: Enable SSH from IAP + direction: INGRESS + action: allow + priority: 1002 + ranges: + - 35.235.240.0/20 + ports: + tcp: ["22"] + target_resources: null + enable_logging: false + +allow-icmp: + description: Enable ICMP + direction: INGRESS + action: allow + priority: 1003 + ranges: + - 0.0.0.0/0 + ports: + icmp: [] + target_resources: null + enable_logging: false diff --git a/fast/stages/2-networking-e-nva-bgp/data/subnets/dev/dev-dataplatform-ew1.yaml b/fast/stages/2-networking-e-nva-bgp/data/subnets/dev/dev-dataplatform-ew1.yaml new file mode 100644 index 0000000000..2c682405c5 --- /dev/null +++ b/fast/stages/2-networking-e-nva-bgp/data/subnets/dev/dev-dataplatform-ew1.yaml @@ -0,0 +1,8 @@ +# skip boilerplate check + +region: europe-west1 +description: Default subnet for dev Data Platform +ip_cidr_range: 10.128.48.0/24 +secondary_ip_ranges: + pods: 100.128.48.0/20 + services: 100.255.48.0/24 diff --git a/fast/stages/2-networking-e-nva-bgp/data/subnets/dev/dev-default-ew1.yaml b/fast/stages/2-networking-e-nva-bgp/data/subnets/dev/dev-default-ew1.yaml new file mode 100644 index 0000000000..d6e4fcdb5a --- /dev/null +++ b/fast/stages/2-networking-e-nva-bgp/data/subnets/dev/dev-default-ew1.yaml @@ -0,0 +1,5 @@ +# skip boilerplate check + +region: europe-west1 +ip_cidr_range: 10.128.128.0/24 +description: Default europe-west1 subnet for dev diff --git a/fast/stages/2-networking-e-nva-bgp/data/subnets/dev/dev-default-ew4.yaml b/fast/stages/2-networking-e-nva-bgp/data/subnets/dev/dev-default-ew4.yaml new file mode 100644 index 0000000000..2a070fb5b6 --- /dev/null +++ b/fast/stages/2-networking-e-nva-bgp/data/subnets/dev/dev-default-ew4.yaml @@ -0,0 +1,5 @@ +# skip boilerplate check + +region: europe-west4 +ip_cidr_range: 10.128.160.0/24 +description: Default europe-west4 subnet for dev diff --git a/fast/stages/2-networking-e-nva-bgp/data/subnets/landing-trusted/landing-trusted-default-ew1.yaml b/fast/stages/2-networking-e-nva-bgp/data/subnets/landing-trusted/landing-trusted-default-ew1.yaml new file mode 100644 index 0000000000..1a83620cf8 --- /dev/null +++ b/fast/stages/2-networking-e-nva-bgp/data/subnets/landing-trusted/landing-trusted-default-ew1.yaml @@ -0,0 +1,5 @@ +# skip boilerplate check + +region: europe-west1 +ip_cidr_range: 10.128.64.0/24 +description: Default europe-west1 subnet for landing trusted diff --git a/fast/stages/2-networking-e-nva-bgp/data/subnets/landing-trusted/landing-trusted-default-ew4.yaml b/fast/stages/2-networking-e-nva-bgp/data/subnets/landing-trusted/landing-trusted-default-ew4.yaml new file mode 100644 index 0000000000..b302fef6a7 --- /dev/null +++ b/fast/stages/2-networking-e-nva-bgp/data/subnets/landing-trusted/landing-trusted-default-ew4.yaml @@ -0,0 +1,5 @@ +# skip boilerplate check + +region: europe-west4 +ip_cidr_range: 10.128.96.0/24 +description: Default europe-west4 subnet for landing trusted diff --git a/fast/stages/2-networking-e-nva-bgp/data/subnets/landing-untrusted/landing-untrusted-default-ew1.yaml b/fast/stages/2-networking-e-nva-bgp/data/subnets/landing-untrusted/landing-untrusted-default-ew1.yaml new file mode 100644 index 0000000000..fae0f2eb4b --- /dev/null +++ b/fast/stages/2-networking-e-nva-bgp/data/subnets/landing-untrusted/landing-untrusted-default-ew1.yaml @@ -0,0 +1,5 @@ +# skip boilerplate check + +region: europe-west1 +ip_cidr_range: 10.128.0.0/24 +description: Default europe-west1 subnet for landing untrusted diff --git a/fast/stages/2-networking-e-nva-bgp/data/subnets/landing-untrusted/landing-untrusted-default-ew4.yaml b/fast/stages/2-networking-e-nva-bgp/data/subnets/landing-untrusted/landing-untrusted-default-ew4.yaml new file mode 100644 index 0000000000..5588d55e00 --- /dev/null +++ b/fast/stages/2-networking-e-nva-bgp/data/subnets/landing-untrusted/landing-untrusted-default-ew4.yaml @@ -0,0 +1,5 @@ +# skip boilerplate check + +region: europe-west4 +ip_cidr_range: 10.128.32.0/24 +description: Default europe-west4 subnet for landing untrusted diff --git a/fast/stages/2-networking-e-nva-bgp/data/subnets/prod/prod-default-ew1.yaml b/fast/stages/2-networking-e-nva-bgp/data/subnets/prod/prod-default-ew1.yaml new file mode 100644 index 0000000000..241558403d --- /dev/null +++ b/fast/stages/2-networking-e-nva-bgp/data/subnets/prod/prod-default-ew1.yaml @@ -0,0 +1,5 @@ +# skip boilerplate check + +region: europe-west1 +ip_cidr_range: 10.128.192.0/24 +description: Default europe-west1 subnet for prod diff --git a/fast/stages/2-networking-e-nva-bgp/data/subnets/prod/prod-default-ew4.yaml b/fast/stages/2-networking-e-nva-bgp/data/subnets/prod/prod-default-ew4.yaml new file mode 100644 index 0000000000..26260e42c4 --- /dev/null +++ b/fast/stages/2-networking-e-nva-bgp/data/subnets/prod/prod-default-ew4.yaml @@ -0,0 +1,5 @@ +# skip boilerplate check + +region: europe-west4 +ip_cidr_range: 10.128.224.0/24 +description: Default europe-west4 subnet for prod diff --git a/fast/stages/2-networking-e-nva-bgp/diagram.png b/fast/stages/2-networking-e-nva-bgp/diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..53c4daca2e5ea7452b18bccd6811d93c01362f82 GIT binary patch literal 81573 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 z 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 RQXvFfs)`R4-rq9~{4W#Y=1Kqn literal 0 HcmV?d00001 diff --git a/fast/stages/2-networking-e-nva-bgp/diagram.svg b/fast/stages/2-networking-e-nva-bgp/diagram.svg new file mode 100644 index 0000000000..7fad1244b2 --- /dev/null +++ b/fast/stages/2-networking-e-nva-bgp/diagram.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/fast/stages/2-networking-e-nva-bgp/dns-dev.tf b/fast/stages/2-networking-e-nva-bgp/dns-dev.tf new file mode 100644 index 0000000000..4eb472a159 --- /dev/null +++ b/fast/stages/2-networking-e-nva-bgp/dns-dev.tf @@ -0,0 +1,53 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Development spoke DNS zones and peerings setup. + +# GCP-specific environment zone + +module "dev-dns-private-zone" { + source = "../../../modules/dns" + project_id = module.dev-spoke-project.project_id + type = "private" + name = "dev-gcp-example-com" + domain = "dev.gcp.example.com." + client_networks = [module.landing-trusted-vpc.self_link, module.landing-untrusted-vpc.self_link] + recordsets = { + "A localhost" = { records = ["127.0.0.1"] } + } +} + +# root zone peering to landing to centralize configuration; remove if unneeded + +module "dev-landing-root-dns-peering" { + source = "../../../modules/dns" + project_id = module.dev-spoke-project.project_id + type = "peering" + name = "dev-root-dns-peering" + domain = "." + client_networks = [module.dev-spoke-vpc.self_link] + peer_network = module.landing-trusted-vpc.self_link +} + +module "dev-reverse-10-dns-peering" { + source = "../../../modules/dns" + project_id = module.dev-spoke-project.project_id + type = "peering" + name = "dev-reverse-10-dns-peering" + domain = "10.in-addr.arpa." + client_networks = [module.dev-spoke-vpc.self_link] + peer_network = module.landing-trusted-vpc.self_link +} diff --git a/fast/stages/2-networking-e-nva-bgp/dns-landing.tf b/fast/stages/2-networking-e-nva-bgp/dns-landing.tf new file mode 100644 index 0000000000..40090279f9 --- /dev/null +++ b/fast/stages/2-networking-e-nva-bgp/dns-landing.tf @@ -0,0 +1,155 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Landing DNS zones and peerings setup. + +# forwarding to on-prem DNS resolvers + +module "onprem-example-dns-forwarding" { + source = "../../../modules/dns" + project_id = module.landing-project.project_id + type = "forwarding" + name = "example-com" + domain = "onprem.example.com." + client_networks = [ + module.landing-untrusted-vpc.self_link, + module.landing-trusted-vpc.self_link + ] + forwarders = { for ip in var.dns.onprem : ip => null } +} + +module "reverse-10-dns-forwarding" { + source = "../../../modules/dns" + project_id = module.landing-project.project_id + type = "forwarding" + name = "root-reverse-10" + domain = "10.in-addr.arpa." + client_networks = [ + module.landing-untrusted-vpc.self_link, + module.landing-trusted-vpc.self_link + ] + forwarders = { for ip in var.dns.onprem : ip => null } +} + +module "gcp-example-dns-private-zone" { + source = "../../../modules/dns" + project_id = module.landing-project.project_id + type = "private" + name = "gcp-example-com" + domain = "gcp.example.com." + client_networks = [ + module.landing-untrusted-vpc.self_link, + module.landing-trusted-vpc.self_link + ] + recordsets = { + "A localhost" = { records = ["127.0.0.1"] } + } +} + +# Google APIs + +module "googleapis-private-zone" { + source = "../../../modules/dns" + project_id = module.landing-project.project_id + type = "private" + name = "googleapis-com" + domain = "googleapis.com." + client_networks = [ + module.landing-untrusted-vpc.self_link, + module.landing-trusted-vpc.self_link + ] + recordsets = { + "A private" = { records = [ + "199.36.153.8", "199.36.153.9", "199.36.153.10", "199.36.153.11" + ] } + "A restricted" = { records = [ + "199.36.153.4", "199.36.153.5", "199.36.153.6", "199.36.153.7" + ] } + "CNAME *" = { records = ["private.googleapis.com."] } + } +} + +module "gcrio-private-zone" { + source = "../../../modules/dns" + project_id = module.landing-project.project_id + type = "private" + name = "gcr-io" + domain = "gcr.io." + client_networks = [ + module.landing-untrusted-vpc.self_link, + module.landing-trusted-vpc.self_link + ] + recordsets = { + "A gcr.io." = { ttl = 300, records = [ + "199.36.153.8", "199.36.153.9", "199.36.153.10", "199.36.153.11" + ] } + "CNAME *" = { ttl = 300, records = ["private.googleapis.com."] } + } +} + +module "packages-private-zone" { + source = "../../../modules/dns" + project_id = module.landing-project.project_id + type = "private" + name = "packages-cloud" + domain = "packages.cloud.google.com." + client_networks = [ + module.landing-untrusted-vpc.self_link, + module.landing-trusted-vpc.self_link + ] + recordsets = { + "A packages.cloud.google.com." = { ttl = 300, records = [ + "199.36.153.8", "199.36.153.9", "199.36.153.10", "199.36.153.11" + ] } + "CNAME *" = { ttl = 300, records = ["private.googleapis.com."] } + } +} + +module "pkgdev-private-zone" { + source = "../../../modules/dns" + project_id = module.landing-project.project_id + type = "private" + name = "pkg-dev" + domain = "pkg.dev." + client_networks = [ + module.landing-untrusted-vpc.self_link, + module.landing-trusted-vpc.self_link + ] + recordsets = { + "A pkg.dev." = { ttl = 300, records = [ + "199.36.153.8", "199.36.153.9", "199.36.153.10", "199.36.153.11" + ] } + "CNAME *" = { ttl = 300, records = ["private.googleapis.com."] } + } +} + +module "pkigoog-private-zone" { + source = "../../../modules/dns" + project_id = module.landing-project.project_id + type = "private" + name = "pki-goog" + domain = "pki.goog." + client_networks = [ + module.landing-untrusted-vpc.self_link, + module.landing-trusted-vpc.self_link + ] + recordsets = { + "A pki.goog." = { ttl = 300, records = [ + "199.36.153.8", "199.36.153.9", "199.36.153.10", "199.36.153.11" + ] } + "CNAME *" = { ttl = 300, records = ["private.googleapis.com."] } + } +} diff --git a/fast/stages/2-networking-e-nva-bgp/dns-prod.tf b/fast/stages/2-networking-e-nva-bgp/dns-prod.tf new file mode 100644 index 0000000000..b54609dfdb --- /dev/null +++ b/fast/stages/2-networking-e-nva-bgp/dns-prod.tf @@ -0,0 +1,53 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Production spoke DNS zones and peerings setup. + +# GCP-specific environment zone + +module "prod-dns-private-zone" { + source = "../../../modules/dns" + project_id = module.prod-spoke-project.project_id + type = "private" + name = "prod-gcp-example-com" + domain = "prod.gcp.example.com." + client_networks = [module.landing-trusted-vpc.self_link, module.landing-untrusted-vpc.self_link] + recordsets = { + "A localhost" = { records = ["127.0.0.1"] } + } +} + +# root zone peering to landing to centralize configuration; remove if unneeded + +module "prod-landing-root-dns-peering" { + source = "../../../modules/dns" + project_id = module.prod-spoke-project.project_id + type = "peering" + name = "prod-root-dns-peering" + domain = "." + client_networks = [module.prod-spoke-vpc.self_link] + peer_network = module.landing-trusted-vpc.self_link +} + +module "prod-reverse-10-dns-peering" { + source = "../../../modules/dns" + project_id = module.prod-spoke-project.project_id + type = "peering" + name = "prod-reverse-10-dns-peering" + domain = "10.in-addr.arpa." + client_networks = [module.prod-spoke-vpc.self_link] + peer_network = module.landing-trusted-vpc.self_link +} diff --git a/fast/stages/2-networking-e-nva-bgp/landing.tf b/fast/stages/2-networking-e-nva-bgp/landing.tf new file mode 100644 index 0000000000..8f0b195927 --- /dev/null +++ b/fast/stages/2-networking-e-nva-bgp/landing.tf @@ -0,0 +1,145 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Landing VPC and related resources. + +module "landing-project" { + source = "../../../modules/project" + billing_account = var.billing_account.id + name = "prod-net-landing-0" + parent = var.folder_ids.networking-prod + prefix = var.prefix + services = [ + "compute.googleapis.com", + "dns.googleapis.com", + "iap.googleapis.com", + "networkconnectivity.googleapis.com", + "networkmanagement.googleapis.com", + "stackdriver.googleapis.com" + ] + shared_vpc_host_config = { + enabled = true + } + iam = { + "roles/dns.admin" = compact([ + try(local.service_accounts.project-factory-prod, null) + ]) + (local.custom_roles.service_project_network_admin) = compact([ + try(local.service_accounts.project-factory-prod, null) + ]) + } +} + +# Untrusted VPC + +module "landing-untrusted-vpc" { + source = "../../../modules/net-vpc" + project_id = module.landing-project.project_id + name = "prod-untrusted-landing-0" + mtu = 1500 + dns_policy = { + inbound = false + logging = false + } + data_folder = "${var.factories_config.data_dir}/subnets/landing-untrusted" +} + +module "landing-untrusted-firewall" { + source = "../../../modules/net-vpc-firewall" + project_id = module.landing-project.project_id + network = module.landing-untrusted-vpc.name + default_rules_config = { + disabled = true + } + factories_config = { + cidr_tpl_file = "${var.factories_config.data_dir}/cidrs.yaml" + rules_folder = "${var.factories_config.data_dir}/firewall-rules/landing-untrusted" + } +} + +# NAT + +moved { + from = module.landing-nat-ew1 + to = module.landing-nat-primary +} + +module "landing-nat-primary" { + source = "../../../modules/net-cloudnat" + project_id = module.landing-project.project_id + region = var.regions.primary + name = local.region_shortnames[var.regions.primary] + router_create = true + router_name = "prod-nat-${local.region_shortnames[var.regions.primary]}" + router_network = module.landing-untrusted-vpc.name + router_asn = 4200001024 +} + +moved { + from = module.landing-nat-ew4 + to = module.landing-nat-secondary +} + +module "landing-nat-secondary" { + source = "../../../modules/net-cloudnat" + project_id = module.landing-project.project_id + region = var.regions.secondary + name = local.region_shortnames[var.regions.secondary] + router_create = true + router_name = "prod-nat-${local.region_shortnames[var.regions.secondary]}" + router_network = module.landing-untrusted-vpc.name + router_asn = 4200001024 +} + +# Trusted VPC + +module "landing-trusted-vpc" { + source = "../../../modules/net-vpc" + project_id = module.landing-project.project_id + name = "prod-trusted-landing-0" + delete_default_routes_on_create = true + mtu = 1500 + data_folder = "${var.factories_config.data_dir}/subnets/landing-trusted" + dns_policy = { + inbound = true + } + # Set explicit routes for googleapis in case the default route is deleted + routes = { + private-googleapis = { + dest_range = "199.36.153.8/30" + next_hop_type = "gateway" + next_hop = "default-internet-gateway" + } + restricted-googleapis = { + dest_range = "199.36.153.4/30" + next_hop_type = "gateway" + next_hop = "default-internet-gateway" + } + } +} + +module "landing-trusted-firewall" { + source = "../../../modules/net-vpc-firewall" + project_id = module.landing-project.project_id + network = module.landing-trusted-vpc.name + default_rules_config = { + disabled = true + } + factories_config = { + cidr_tpl_file = "${var.factories_config.data_dir}/cidrs.yaml" + rules_folder = "${var.factories_config.data_dir}/firewall-rules/landing-trusted" + } +} diff --git a/fast/stages/2-networking-e-nva-bgp/main.tf b/fast/stages/2-networking-e-nva-bgp/main.tf new file mode 100644 index 0000000000..0a56396ecd --- /dev/null +++ b/fast/stages/2-networking-e-nva-bgp/main.tf @@ -0,0 +1,57 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Networking folder and hierarchical policy. + +locals { + custom_roles = coalesce(var.custom_roles, {}) + # combine all regions from variables and subnets + regions = distinct(concat( + values(var.regions), + values(module.dev-spoke-vpc.subnet_regions), + values(module.landing-trusted-vpc.subnet_regions), + values(module.landing-untrusted-vpc.subnet_regions), + values(module.prod-spoke-vpc.subnet_regions), + )) + service_accounts = { + for k, v in coalesce(var.service_accounts, {}) : + k => "serviceAccount:${v}" if v != null + } + stage3_sas_delegated_grants = [ + "roles/composer.sharedVpcAgent", + "roles/compute.networkUser", + "roles/compute.networkViewer", + "roles/container.hostServiceAgentUser", + "roles/multiclusterservicediscovery.serviceAgent", + "roles/vpcaccess.user", + ] +} + +module "folder" { + source = "../../../modules/folder" + parent = "organizations/${var.organization.id}" + name = "Networking" + folder_create = var.folder_ids.networking == null + id = var.folder_ids.networking + firewall_policy_factory = { + cidr_file = "${var.factories_config.data_dir}/cidrs.yaml" + policy_name = var.factories_config.firewall_policy_name + rules_file = "${var.factories_config.data_dir}/hierarchical-policy-rules.yaml" + } + firewall_policy_association = { + factory-policy = var.factories_config.firewall_policy_name + } +} diff --git a/fast/stages/2-networking-e-nva-bgp/monitoring.tf b/fast/stages/2-networking-e-nva-bgp/monitoring.tf new file mode 100644 index 0000000000..be3a47faac --- /dev/null +++ b/fast/stages/2-networking-e-nva-bgp/monitoring.tf @@ -0,0 +1,32 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Network monitoring dashboards. + +locals { + dashboard_path = "${var.factories_config.data_dir}/dashboards" + dashboard_files = fileset(local.dashboard_path, "*.json") + dashboards = { + for filename in local.dashboard_files : + filename => "${local.dashboard_path}/${filename}" + } +} + +resource "google_monitoring_dashboard" "dashboard" { + for_each = local.dashboards + project = module.landing-project.project_id + dashboard_json = file(each.value) +} diff --git a/fast/stages/2-networking-c-nva/ncc.tf b/fast/stages/2-networking-e-nva-bgp/ncc.tf similarity index 100% rename from fast/stages/2-networking-c-nva/ncc.tf rename to fast/stages/2-networking-e-nva-bgp/ncc.tf diff --git a/fast/stages/2-networking-e-nva-bgp/nva.tf b/fast/stages/2-networking-e-nva-bgp/nva.tf new file mode 100644 index 0000000000..baf6018a53 --- /dev/null +++ b/fast/stages/2-networking-e-nva-bgp/nva.tf @@ -0,0 +1,187 @@ +/** + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +locals { + _nva_zones = ["b", "c"] + + # The configurations used to create the NVA VMs. + # + # Rendered as following: + # nva_configs = { + # primary-b = {...} + # primary-c = {...} + # secondary-b = {...} + # secondary-c = {...} + # } + nva_configs = { + for v in setproduct(keys(var.regions), local._nva_zones) : + join("-", v) => { + # Each NVA announces its trusted regional subnets + announce-to-nva = upper(v.0) + # NVAs in each region have their own ASN + # and peer with cross-regional NVAs. + asn_nva = ( + v.0 == "primary" + ? var.ncc_asn.nva_primary + : var.ncc_asn.nva_secondary + ) + asn_nva_cross_region = ( + v.0 == "primary" + ? var.ncc_asn.nva_secondary + : var.ncc_asn.nva_primary + ) + asn_trusted = var.ncc_asn.trusted + asn_untrusted = var.ncc_asn.untrusted + # To guarantee traffic to remain symmetric, + # NVAs need to advertise cross-region routes with a higher cost (10100) + cost_primary = v.0 == "primary" ? "100" : "10100" + cost_secondary = v.0 == "primary" ? "10100" : "100" + gcp_dev_primary = var.gcp_ranges.gcp_dev_primary + gcp_dev_secondary = var.gcp_ranges.gcp_dev_secondary + gcp_landing_trusted_primary = var.gcp_ranges.gcp_landing_trusted_primary + gcp_landing_trusted_secondary = var.gcp_ranges.gcp_landing_trusted_secondary + gcp_landing_untrusted_primary = var.gcp_ranges.gcp_landing_untrusted_primary + gcp_landing_untrusted_secondary = var.gcp_ranges.gcp_landing_untrusted_secondary + gcp_prod_primary = var.gcp_ranges.gcp_prod_primary + gcp_prod_secondary = var.gcp_ranges.gcp_prod_secondary + # The IPs of cross-region NVA VMs in the untrusted VPC (x.y.w.z) + ip_neighbor_cross_region_nva_0 = cidrhost(module.landing-untrusted-vpc.subnet_ips["${local._regions_cross[v.0]}/landing-untrusted-default-${local.region_shortnames[local._regions_cross[v.0]]}"], 101) + ip_neighbor_cross_region_nva_1 = cidrhost(module.landing-untrusted-vpc.subnet_ips["${local._regions_cross[v.0]}/landing-untrusted-default-${local.region_shortnames[local._regions_cross[v.0]]}"], 102) + # The Cloud router IPs (x.y.w.z) in the untrusted + # and in the trusted VPCs, where the NVA connects to + ip_neighbor_trusted_0 = cidrhost(module.landing-trusted-vpc.subnet_ips["${var.regions[v.0]}/landing-trusted-default-${local.region_shortnames[var.regions[v.0]]}"], 201) + ip_neighbor_trusted_1 = cidrhost(module.landing-trusted-vpc.subnet_ips["${var.regions[v.0]}/landing-trusted-default-${local.region_shortnames[var.regions[v.0]]}"], 202) + ip_neighbor_untrusted_0 = cidrhost(module.landing-untrusted-vpc.subnet_ips["${var.regions[v.0]}/landing-untrusted-default-${local.region_shortnames[var.regions[v.0]]}"], 201) + ip_neighbor_untrusted_1 = cidrhost(module.landing-untrusted-vpc.subnet_ips["${var.regions[v.0]}/landing-untrusted-default-${local.region_shortnames[var.regions[v.0]]}"], 202) + # The IPs to assign to the NVA NICs + # in the trusted and in the untrusted VPCs. + ip_trusted = cidrhost(module.landing-trusted-vpc.subnet_ips["${var.regions[v.0]}/landing-trusted-default-${local.region_shortnames[var.regions[v.0]]}"], 101 + index(var.zones, v.1)) + ip_untrusted = cidrhost(module.landing-untrusted-vpc.subnet_ips["${var.regions[v.0]}/landing-untrusted-default-${local.region_shortnames[var.regions[v.0]]}"], 101 + index(var.zones, v.1)) + # Either primary or secondary + name = v.0 + # The name of the region where the NVA lives. + # For example, europe-west1 or europe-west4 + region = var.regions[v.0] + # the short name for the region. For example, ew1 or ew4 + shortname = local.region_shortnames[var.regions[v.0]] + # The zone where the NVA lives. For example, b or c + zone = v.1 + } + } + + # The routing_config should be aligned to the NVA NICs. + # For example: + # local.routing_config[0] configures eth0; + # local.routing_config[0] configures eth1. + routing_config = [ + { + enable_masquerading = true + name = "untrusted" + routes = [ + var.gcp_ranges.gcp_landing_untrusted_primary, + var.gcp_ranges.gcp_landing_untrusted_secondary + ] + }, + { + name = "trusted" + routes = [ + var.gcp_ranges.gcp_landing_trusted_primary, + var.gcp_ranges.gcp_landing_trusted_secondary + ] + } + ] +} + +module "nva-bgp-cloud-config" { + for_each = local.nva_configs + source = "../../../modules/cloud-config-container/simple-nva" + enable_health_checks = true + network_interfaces = local.routing_config + frr_config = { + config_file = templatefile("data/bgp-config.tftpl", each.value) + daemons_enabled = ["bgpd"] + } +} + +resource "google_compute_address" "nva_static_ip_trusted" { + for_each = local.nva_configs + name = "nva-ip-trusted-${each.value.shortname}-${each.value.zone}" + project = module.landing-project.project_id + subnetwork = module.landing-trusted-vpc.subnet_self_links["${each.value.region}/landing-trusted-default-${each.value.shortname}"] + address_type = "INTERNAL" + address = each.value.ip_trusted + region = each.value.region +} + +resource "google_compute_address" "nva_static_ip_untrusted" { + for_each = local.nva_configs + name = "nva-ip-untrusted-${each.value.shortname}-${each.value.zone}" + project = module.landing-project.project_id + subnetwork = module.landing-untrusted-vpc.subnet_self_links["${each.value.region}/landing-untrusted-default-${each.value.shortname}"] + address_type = "INTERNAL" + address = each.value.ip_untrusted + region = each.value.region +} + +module "nva" { + for_each = local.nva_configs + source = "../../../modules/compute-vm" + project_id = module.landing-project.project_id + name = "nva-${each.value.shortname}-${each.value.zone}" + instance_type = "e2-standard-2" + can_ip_forward = true + zone = "${each.value.region}-${each.value.zone}" + tags = ["nva"] + + network_interfaces = [ + { + network = module.landing-untrusted-vpc.self_link + subnetwork = module.landing-untrusted-vpc.subnet_self_links["${each.value.region}/landing-untrusted-default-${each.value.shortname}"] + nat = false + addresses = { + external = null + internal = google_compute_address.nva_static_ip_untrusted[each.key].address + } + }, + { + network = module.landing-trusted-vpc.self_link + subnetwork = module.landing-trusted-vpc.subnet_self_links["${each.value.region}/landing-trusted-default-${each.value.shortname}"] + nat = false + addresses = { + external = null + internal = google_compute_address.nva_static_ip_trusted[each.key].address + } + } + ] + + boot_disk = { + initialize_params = { + image = "projects/cos-cloud/global/images/family/cos-stable" + size = 10 + type = "pd-balanced" + } + } + + options = { + allow_stopping_for_update = true + deletion_protection = false + termination_action = "STOP" + } + + metadata = { + user-data = module.nva-bgp-cloud-config[each.key].cloud_config + } +} diff --git a/fast/stages/2-networking-e-nva-bgp/outputs.tf b/fast/stages/2-networking-e-nva-bgp/outputs.tf new file mode 100644 index 0000000000..eb53a63f4f --- /dev/null +++ b/fast/stages/2-networking-e-nva-bgp/outputs.tf @@ -0,0 +1,91 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +locals { + host_project_ids = { + dev-spoke-0 = module.dev-spoke-project.project_id + prod-landing = module.landing-project.project_id + prod-spoke-0 = module.prod-spoke-project.project_id + } + host_project_numbers = { + dev-spoke-0 = module.dev-spoke-project.number + prod-landing = module.landing-project.number + prod-spoke-0 = module.prod-spoke-project.number + } + tfvars = { + host_project_ids = local.host_project_ids + host_project_numbers = local.host_project_numbers + vpc_self_links = local.vpc_self_links + } + vpc_self_links = { + prod-landing-trusted = module.landing-trusted-vpc.self_link + prod-landing-untrusted = module.landing-untrusted-vpc.self_link + dev-spoke-0 = module.dev-spoke-vpc.self_link + prod-spoke-0 = module.prod-spoke-vpc.self_link + } +} + +# generate tfvars file for subsequent stages + +resource "local_file" "tfvars" { + for_each = var.outputs_location == null ? {} : { 1 = 1 } + file_permission = "0644" + filename = "${try(pathexpand(var.outputs_location), "")}/tfvars/2-networking.auto.tfvars.json" + content = jsonencode(local.tfvars) +} + +resource "google_storage_bucket_object" "tfvars" { + bucket = var.automation.outputs_bucket + name = "tfvars/2-networking.auto.tfvars.json" + content = jsonencode(local.tfvars) +} + +# outputs + +output "host_project_ids" { + description = "Network project ids." + value = local.host_project_ids +} + +output "host_project_numbers" { + description = "Network project numbers." + value = local.host_project_numbers +} + +output "shared_vpc_self_links" { + description = "Shared VPC host projects." + value = local.vpc_self_links +} + +output "tfvars" { + description = "Terraform variables file for the following stages." + sensitive = true + value = local.tfvars +} + +output "vpn_gateway_endpoints" { + description = "External IP Addresses for the GCP VPN gateways." + value = { + onprem-primary = var.vpn_onprem_primary_config == null ? {} : { + for v in module.landing-to-onprem-primary-vpn.0.gateway.vpn_interfaces : + v.id => v.ip_address + } + onprem-secondary = var.vpn_onprem_secondary_config == null ? {} : { + for v in module.landing-to-onprem-secondary-vpn.0.gateway.vpn_interfaces : + v.id => v.ip_address + } + } +} diff --git a/fast/stages/2-networking-e-nva-bgp/regions.tf b/fast/stages/2-networking-e-nva-bgp/regions.tf new file mode 100644 index 0000000000..ee6b0d6928 --- /dev/null +++ b/fast/stages/2-networking-e-nva-bgp/regions.tf @@ -0,0 +1,46 @@ +/** + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Compute short names for regions. + +locals { + # only map when the first character would not work + _region_cardinal = { + southeast = "se" + } + _regions_cross = { + primary = var.regions["secondary"] + secondary = var.regions["primary"] + } + # only map when the first character would not work + _region_geo = { + australia = "o" + } + # split in [geo, cardinal, number] tokens + _region_tokens = { + for v in local.regions : v => regexall("(?:[a-z]+)|(?:[0-9]+)", v) + } + region_shortnames = { + for k, v in local._region_tokens : k => join("", [ + # first token via geo alias map or first character + lookup(local._region_geo, v.0, substr(v.0, 0, 1)), + # first token via cardinal alias map or first character + lookup(local._region_cardinal, v.1, substr(v.1, 0, 1)), + # region number as is + v.2 + ]) + } +} diff --git a/fast/stages/2-networking-e-nva-bgp/spoke-dev.tf b/fast/stages/2-networking-e-nva-bgp/spoke-dev.tf new file mode 100644 index 0000000000..967a2746ff --- /dev/null +++ b/fast/stages/2-networking-e-nva-bgp/spoke-dev.tf @@ -0,0 +1,112 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Dev spoke VPC and related resources. + +module "dev-spoke-project" { + source = "../../../modules/project" + billing_account = var.billing_account.id + name = "dev-net-spoke-0" + parent = var.folder_ids.networking-dev + prefix = var.prefix + services = [ + "compute.googleapis.com", + "dns.googleapis.com", + "iap.googleapis.com", + "networkmanagement.googleapis.com", + "servicenetworking.googleapis.com", + "stackdriver.googleapis.com", + "vpcaccess.googleapis.com" + ] + shared_vpc_host_config = { + enabled = true + } + metric_scopes = [module.landing-project.project_id] + iam = { + "roles/dns.admin" = compact([ + try(local.service_accounts.gke-dev, null), + try(local.service_accounts.project-factory-dev, null), + try(local.service_accounts.project-factory-prod, null), + ]) + } +} + +module "dev-spoke-vpc" { + source = "../../../modules/net-vpc" + project_id = module.dev-spoke-project.project_id + name = "dev-spoke-0" + mtu = 1500 + data_folder = "${var.factories_config.data_dir}/subnets/dev" + delete_default_routes_on_create = true + psa_config = try(var.psa_ranges.dev, null) + # Set explicit routes for googleapis; send everything else to NVAs + routes = { + private-googleapis = { + dest_range = "199.36.153.8/30" + priority = 999 + next_hop_type = "gateway" + next_hop = "default-internet-gateway" + } + restricted-googleapis = { + dest_range = "199.36.153.4/30" + priority = 999 + next_hop_type = "gateway" + next_hop = "default-internet-gateway" + } + } +} + +module "dev-spoke-firewall" { + source = "../../../modules/net-vpc-firewall" + project_id = module.dev-spoke-project.project_id + network = module.dev-spoke-vpc.name + default_rules_config = { + disabled = true + } + factories_config = { + cidr_tpl_file = "${var.factories_config.data_dir}/cidrs.yaml" + rules_folder = "${var.factories_config.data_dir}/firewall-rules/dev" + } +} + +module "peering-dev" { + source = "../../../modules/net-vpc-peering" + prefix = "dev-peering-0" + local_network = module.dev-spoke-vpc.self_link + peer_network = module.landing-trusted-vpc.self_link + export_local_custom_routes = true + export_peer_custom_routes = true +} + +# Create delegated grants for stage3 service accounts +resource "google_project_iam_binding" "dev_spoke_project_iam_delegated" { + project = module.dev-spoke-project.project_id + role = "roles/resourcemanager.projectIamAdmin" + members = compact([ + try(local.service_accounts.data-platform-dev, null), + try(local.service_accounts.project-factory-dev, null), + try(local.service_accounts.project-factory-prod, null), + try(local.service_accounts.gke-dev, null), + ]) + condition { + title = "dev_stage3_sa_delegated_grants" + description = "Development host project delegated grants." + expression = format( + "api.getAttribute('iam.googleapis.com/modifiedGrantsByRole', []).hasOnly([%s])", + join(",", formatlist("'%s'", local.stage3_sas_delegated_grants)) + ) + } +} diff --git a/fast/stages/2-networking-e-nva-bgp/spoke-prod.tf b/fast/stages/2-networking-e-nva-bgp/spoke-prod.tf new file mode 100644 index 0000000000..9978608145 --- /dev/null +++ b/fast/stages/2-networking-e-nva-bgp/spoke-prod.tf @@ -0,0 +1,110 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description Production spoke VPC and related resources. + +module "prod-spoke-project" { + source = "../../../modules/project" + billing_account = var.billing_account.id + name = "prod-net-spoke-0" + parent = var.folder_ids.networking-prod + prefix = var.prefix + services = [ + "compute.googleapis.com", + "dns.googleapis.com", + "iap.googleapis.com", + "networkmanagement.googleapis.com", + "servicenetworking.googleapis.com", + "stackdriver.googleapis.com", + "vpcaccess.googleapis.com" + ] + shared_vpc_host_config = { + enabled = true + } + metric_scopes = [module.landing-project.project_id] + iam = { + "roles/dns.admin" = compact([ + try(local.service_accounts.gke-prod, null), + try(local.service_accounts.project-factory-prod, null), + ]) + } +} + +module "prod-spoke-vpc" { + source = "../../../modules/net-vpc" + project_id = module.prod-spoke-project.project_id + name = "prod-spoke-0" + mtu = 1500 + data_folder = "${var.factories_config.data_dir}/subnets/prod" + delete_default_routes_on_create = true + psa_config = try(var.psa_ranges.prod, null) + # Set explicit routes for googleapis; send everything else to NVAs + routes = { + private-googleapis = { + dest_range = "199.36.153.8/30" + priority = 999 + next_hop_type = "gateway" + next_hop = "default-internet-gateway" + } + restricted-googleapis = { + dest_range = "199.36.153.4/30" + priority = 999 + next_hop_type = "gateway" + next_hop = "default-internet-gateway" + } + } +} + +module "prod-spoke-firewall" { + source = "../../../modules/net-vpc-firewall" + project_id = module.prod-spoke-project.project_id + network = module.prod-spoke-vpc.name + default_rules_config = { + disabled = true + } + factories_config = { + cidr_tpl_file = "${var.factories_config.data_dir}/cidrs.yaml" + rules_folder = "${var.factories_config.data_dir}/firewall-rules/prod" + } +} + +module "peering-prod" { + source = "../../../modules/net-vpc-peering" + prefix = "prod-peering-0" + local_network = module.prod-spoke-vpc.self_link + peer_network = module.landing-trusted-vpc.self_link + export_local_custom_routes = true + export_peer_custom_routes = true +} + +# Create delegated grants for stage3 service accounts +resource "google_project_iam_binding" "prod_spoke_project_iam_delegated" { + project = module.prod-spoke-project.project_id + role = "roles/resourcemanager.projectIamAdmin" + members = compact([ + try(local.service_accounts.data-platform-prod, null), + try(local.service_accounts.project-factory-prod, null), + try(local.service_accounts.gke-prod, null), + ]) + condition { + title = "prod_stage3_sa_delegated_grants" + description = "Production host project delegated grants." + expression = format( + "api.getAttribute('iam.googleapis.com/modifiedGrantsByRole', []).hasOnly([%s])", + join(",", formatlist("'%s'", local.stage3_sas_delegated_grants)) + ) + } +} diff --git a/fast/stages/2-networking-e-nva-bgp/test-resources.tf b/fast/stages/2-networking-e-nva-bgp/test-resources.tf new file mode 100644 index 0000000000..b2816e4fdb --- /dev/null +++ b/fast/stages/2-networking-e-nva-bgp/test-resources.tf @@ -0,0 +1,245 @@ +/** + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +# tfdoc:file:description temporary instances for testing + +# # Untrusted (Landing) + +# module "test-vm-landing-untrusted-primary-0" { +# source = "../../../modules/compute-vm" +# project_id = module.landing-project.project_id +# zone = "${var.regions.primary}-b" +# name = "test-vm-lnd-unt-primary-0" +# network_interfaces = [{ +# network = module.landing-untrusted-vpc.self_link +# subnetwork = module.landing-untrusted-vpc.subnet_self_links["${var.regions.primary}/landing-untrusted-default-${local.region_shortnames[var.regions.primary]}"] +# }] +# tags = ["primary", "ssh"] +# boot_disk = { +# initialize_params = { +# image = "projects/debian-cloud/global/images/family/debian-11" +# } +# } +# options = { +# spot = true +# termination_action = "STOP" +# } +# metadata = { +# startup-script = < { external = v } + } + tunnels = try(var.vpn_onprem_primary_config.tunnels, {}) +} + +module "landing-to-onprem-secondary-vpn" { + count = var.vpn_onprem_secondary_config == null ? 0 : 1 + source = "../../../modules/net-vpn-ha" + project_id = module.landing-project.project_id + network = module.landing-trusted-vpc.self_link + region = var.regions.secondary + name = "vpn-to-onprem-${local.region_shortnames[var.regions.secondary]}" + router_config = try(var.vpn_onprem_secondary_config.router_config, {}) + peer_gateways = { + for k, v in local.onprem_peer_gateways.secondary : k => { external = v } + } + tunnels = try(var.vpn_onprem_secondary_config.tunnels, {}) +} diff --git a/tests/fast/stages/s2_networking_c_nva/stage.yaml b/tests/fast/stages/s2_networking_c_nva/stage.yaml index a179632604..f79d401553 100644 --- a/tests/fast/stages/s2_networking_c_nva/stage.yaml +++ b/tests/fast/stages/s2_networking_c_nva/stage.yaml @@ -13,5 +13,5 @@ # limitations under the License. counts: - modules: 41 - resources: 188 + modules: 45 + resources: 168 diff --git a/tests/fast/stages/s2_networking_e_nva_bgp/common.tfvars b/tests/fast/stages/s2_networking_e_nva_bgp/common.tfvars new file mode 100644 index 0000000000..a4b1cdd88d --- /dev/null +++ b/tests/fast/stages/s2_networking_e_nva_bgp/common.tfvars @@ -0,0 +1,106 @@ +automation = { + outputs_bucket = "test" +} +billing_account = { + id = "000000-111111-222222" +} +custom_roles = { + service_project_network_admin = "organizations/123456789012/roles/foo" +} +folder_ids = { + networking = null + networking-dev = null + networking-prod = null +} +service_accounts = { + data-platform-dev = "string" + data-platform-prod = "string" + gke-dev = "string" + gke-prod = "string" + project-factory-dev = "string" + project-factory-prod = "string" +} +organization = { + domain = "fast.example.com" + id = 123456789012 + customer_id = "C00000000" +} +prefix = "fast2" +vpn_onprem_primary_config = { + peer_external_gateways = { + default = { + redundancy_type = "SINGLE_IP_INTERNALLY_REDUNDANT" + interfaces = ["8.8.8.8"] + } + } + router_config = { + asn = 65501 + custom_advertise = { + all_subnets = false + ip_ranges = { + "10.1.0.0/16" = "gcp" + "35.199.192.0/19" = "gcp-dns" + "199.36.153.4/30" = "gcp-restricted" + } + } + } + tunnels = { + "0" = { + bgp_peer = { + address = "169.254.1.1" + asn = 65500 + } + bgp_session_range = "169.254.1.2/30" + shared_secret = "foo" + vpn_gateway_interface = 0 + } + "1" = { + bgp_peer = { + address = "169.254.2.1" + asn = 64513 + } + bgp_session_range = "169.254.2.2/30" + shared_secret = "foo" + vpn_gateway_interface = 1 + } + } +} +vpn_onprem_secondary_config = { + peer_external_gateways = { + default = { + redundancy_type = "SINGLE_IP_INTERNALLY_REDUNDANT" + interfaces = ["8.8.4.4"] + } + } + router_config = { + asn = 65501 + custom_advertise = { + all_subnets = false + ip_ranges = { + "10.1.0.0/16" = "gcp" + "35.199.192.0/19" = "gcp-dns" + "199.36.153.4/30" = "gcp-restricted" + } + } + } + tunnels = { + "0" = { + bgp_peer = { + address = "169.254.1.1" + asn = 65500 + } + bgp_session_range = "169.254.3.2/30" + shared_secret = "foo" + vpn_gateway_interface = 0 + } + "1" = { + bgp_peer = { + address = "169.254.2.1" + asn = 64513 + } + bgp_session_range = "169.254.4.2/30" + shared_secret = "foo" + vpn_gateway_interface = 1 + } + } +} diff --git a/tests/fast/stages/s2_networking_e_nva_bgp/stage.tfvars b/tests/fast/stages/s2_networking_e_nva_bgp/stage.tfvars new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/fast/stages/s2_networking_e_nva_bgp/stage.yaml b/tests/fast/stages/s2_networking_e_nva_bgp/stage.yaml new file mode 100644 index 0000000000..a179632604 --- /dev/null +++ b/tests/fast/stages/s2_networking_e_nva_bgp/stage.yaml @@ -0,0 +1,17 @@ +# Copyright 2023 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +counts: + modules: 41 + resources: 188 diff --git a/tests/fast/stages/s2_networking_e_nva_bgp/test_plan.py b/tests/fast/stages/s2_networking_e_nva_bgp/test_plan.py new file mode 100644 index 0000000000..2a1a08d543 --- /dev/null +++ b/tests/fast/stages/s2_networking_e_nva_bgp/test_plan.py @@ -0,0 +1,21 @@ +# Copyright 2023 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +def test_counts(plan_summary): + "Test stage." + summary = plan_summary("fast/stages/2-networking-e-nva", + tf_var_files=["common.tfvars"]) + assert summary.counts["modules"] == 39 + assert summary.counts["resources"] == 181 diff --git a/tests/fast/stages/s2_networking_e_nva_bgp/tftest.yaml b/tests/fast/stages/s2_networking_e_nva_bgp/tftest.yaml new file mode 100644 index 0000000000..f93aefd716 --- /dev/null +++ b/tests/fast/stages/s2_networking_e_nva_bgp/tftest.yaml @@ -0,0 +1,22 @@ +# Copyright 2023 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module: fast/stages/2-networking-e-nva +common_tfvars: + - common.tfvars + +tests: + stage: + extra_files: + - ../../plugins/2-networking-serverless-connector/*.tf