diff --git a/CHANGELOG.md b/CHANGELOG.md index 272c238c..debc44f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ All notable changes to this project will be documented in this file. **FAST** - Add support for Workload Identity Federation and CI/CD repositories +- Simplify VPN tunnel configuration in the Hub and Spoke VPN network stage ## [15.0.0] - 2022-04-05 diff --git a/fast/stages/02-networking-vpn/README.md b/fast/stages/02-networking-vpn/README.md index e9dd8cab..4924ca6b 100644 --- a/fast/stages/02-networking-vpn/README.md +++ b/fast/stages/02-networking-vpn/README.md @@ -244,9 +244,9 @@ Per variable `vpn_onprem_configs` such ranges are advertised to onprem - further - 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) -### Preliminar activities +### Preliminary activities -Before running `terraform apply` on this stage, make sure to adapt all of `variables.tf` to your needs, to update all reference to regions (e.g. `europe-west1` or `ew1`) in the whole directory to match your preferences. +Before running `terraform apply` on this stage, make sure to adapt all of `variables.tf` and `vpn-variables.tf` to your needs, to update all references to regions (e.g. `europe-west1` or `ew1`) in the whole directory to match your preferences. If you're not using FAST, you'll also need to create a `providers.tf` file to configure the GCS backend and the service account to use to run the deployment. @@ -274,7 +274,7 @@ The new VPC requires a set of dedicated CIDRs, one per region, added to variable Variables managing L7 Interal 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. HA VPN connectivity (see also [VPNs](#vpns)) to `landing` is managed by the `vpn-spoke-*.tf` files. -Copy `vpn-spoke-prod.tf` to `vpn-spoke-staging.tf` - replace "prod" with "staging" where relevant. +Copy `vpn-spoke-dev.tf` to `vpn-spoke-staging.tf` - replace `dev` with `staging` where relevant. VPN configuration also controls BGP advertisements, which requires the following variable changes: @@ -305,7 +305,8 @@ DNS configurations are centralised in the `dns-*.tf` files. Spokes delegate DNS | [variables.tf](./variables.tf) | Module variables. | | | | [vpn-onprem.tf](./vpn-onprem.tf) | VPN between landing and onprem. | net-vpn-ha | | | [vpn-spoke-dev.tf](./vpn-spoke-dev.tf) | VPN between landing and development spoke. | net-vpn-ha | | -| [vpn-spoke-prod.tf](./vpn-spoke-prod.tf) | VPN between landing and production spoke. | net-vpn-ha | | +| [vpn-spoke-prod-ew1.tf](./vpn-spoke-prod-ew1.tf) | VPN between landing and production spoke in ew1. | net-vpn-ha | | +| [vpn-spoke-prod-ew4.tf](./vpn-spoke-prod-ew4.tf) | VPN between landing and production spoke in ew4. | net-vpn-ha | | ## Variables @@ -327,7 +328,7 @@ DNS configurations are centralised in the `dns-*.tf` files. Spokes delegate DNS | [router_spoke_configs](variables-vpn.tf#L18) | Configurations for routers used for internal connectivity. | map(object({…})) | | {…} | | | [service_accounts](variables.tf#L184) | Automation service accounts in name => email format. | object({…}) | | null | 01-resman | | [vpn_onprem_configs](variables.tf#L196) | VPN gateway configuration for onprem interconnection. | map(object({…})) | | {…} | | -| [vpn_spoke_configs](variables-vpn.tf#L37) | VPN gateway configuration for spokes. | map(object({…})) | | {…} | | +| [vpn_spoke_configs](variables-vpn.tf#L37) | VPN gateway configuration for spokes. | map(object({…})) | | {…} | | ## Outputs diff --git a/fast/stages/02-networking-vpn/variables-vpn.tf b/fast/stages/02-networking-vpn/variables-vpn.tf index 1732a3a1..3a6d7567 100644 --- a/fast/stages/02-networking-vpn/variables-vpn.tf +++ b/fast/stages/02-networking-vpn/variables-vpn.tf @@ -37,52 +37,29 @@ variable "router_spoke_configs" { variable "vpn_spoke_configs" { description = "VPN gateway configuration for spokes." type = map(object({ - adv = object({ - default = bool - custom = list(string) - }) - session_range = string + default = bool + custom = list(string) })) default = { landing-ew1 = { - adv = { - default = false - custom = ["rfc_1918_10", "rfc_1918_172", "rfc_1918_192"] - } - # values for the landing router are pulled from the spoke range - session_range = null + default = false + custom = ["rfc_1918_10", "rfc_1918_172", "rfc_1918_192"] } landing-ew4 = { - adv = { - default = false - custom = ["rfc_1918_10", "rfc_1918_172", "rfc_1918_192"] - } - # values for the landing router are pulled from the spoke range - session_range = null + default = false + custom = ["rfc_1918_10", "rfc_1918_172", "rfc_1918_192"] } dev-ew1 = { - adv = { - default = false - custom = ["gcp_dev"] - } - # resize according to required number of tunnels - session_range = "169.254.0.0/27" + default = false + custom = ["gcp_dev"] } prod-ew1 = { - adv = { - default = false - custom = ["gcp_prod"] - } - # resize according to required number of tunnels - session_range = "169.254.0.64/27" + default = false + custom = ["gcp_prod"] } prod-ew4 = { - adv = { - default = false - custom = ["gcp_prod"] - } - # resize according to required number of tunnels - session_range = "169.254.0.96/27" + default = false + custom = ["gcp_prod"] } } } diff --git a/fast/stages/02-networking-vpn/vpn-spoke-dev.tf b/fast/stages/02-networking-vpn/vpn-spoke-dev.tf index 01d43d47..1ad329a0 100644 --- a/fast/stages/02-networking-vpn/vpn-spoke-dev.tf +++ b/fast/stages/02-networking-vpn/vpn-spoke-dev.tf @@ -19,14 +19,13 @@ locals { # define the structures used for BGP peers in the VPN resources vpn_spoke_bgp_peer_options = { - for k, v in var.vpn_spoke_configs : - k => v.adv == null ? null : { + for k, v in var.vpn_spoke_configs : k => v == null ? null : { advertise_groups = [] advertise_ip_ranges = { - for adv in(v.adv == null ? [] : v.adv.custom) : - var.custom_adv[adv] => adv + for range in(v == null ? [] : v.custom) : + try(var.custom_adv[range], range) => range } - advertise_mode = try(v.adv.default, false) ? "DEFAULT" : "CUSTOM" + advertise_mode = try(v.default, false) ? "DEFAULT" : "CUSTOM" route_priority = null } } @@ -45,20 +44,36 @@ module "landing-to-dev-ew1-vpn" { router_name = "landing-vpn-ew1" router_asn = var.router_spoke_configs.landing-ew1.asn peer_gcp_gateway = module.dev-to-landing-ew1-vpn.self_link - tunnels = { for t in range(2) : "tunnel-${t}" => { - bgp_peer = { - address = cidrhost(var.vpn_spoke_configs.dev-ew1.session_range, 1 + (t * 4)) - asn = var.router_spoke_configs.spoke-dev-ew1.asn + tunnels = { + 0 = { + bgp_peer = { + address = cidrhost("169.254.0.0/27", 1) + asn = var.router_spoke_configs.spoke-dev-ew1.asn + } + bgp_peer_options = local.vpn_spoke_bgp_peer_options.landing-ew1 + bgp_session_range = "${ + cidrhost("169.254.0.0/27", 2) + }/30" + ike_version = 2 + peer_external_gateway_interface = null + router = null + shared_secret = null + vpn_gateway_interface = 0 } - bgp_peer_options = local.vpn_spoke_bgp_peer_options.landing-ew1 - bgp_session_range = "${cidrhost( - var.vpn_spoke_configs.dev-ew1.session_range, 2 + (t * 4) - )}/30" - ike_version = 2 - peer_external_gateway_interface = null - router = null - shared_secret = null - vpn_gateway_interface = t + 1 = { + bgp_peer = { + address = cidrhost("169.254.0.0/27", 5) + asn = var.router_spoke_configs.spoke-dev-ew1.asn + } + bgp_peer_options = local.vpn_spoke_bgp_peer_options.landing-ew1 + bgp_session_range = "${ + cidrhost("169.254.0.0/27", 6) + }/30" + ike_version = 2 + peer_external_gateway_interface = null + router = null + shared_secret = null + vpn_gateway_interface = 1 } } depends_on = [ @@ -76,20 +91,36 @@ module "dev-to-landing-ew1-vpn" { router_name = "dev-spoke-vpn-ew1" router_asn = var.router_spoke_configs.spoke-dev-ew1.asn peer_gcp_gateway = module.landing-to-dev-ew1-vpn.self_link - tunnels = { for t in range(2) : "tunnel-${t}" => { - bgp_peer = { - address = cidrhost(var.vpn_spoke_configs.dev-ew1.session_range, 2 + (t * 4)) - asn = var.router_spoke_configs.landing-ew1.asn + tunnels = { + 0 = { + bgp_peer = { + address = cidrhost("169.254.0.0/27", 2) + asn = var.router_spoke_configs.landing-ew1.asn + } + bgp_peer_options = local.vpn_spoke_bgp_peer_options.dev-ew1 + bgp_session_range = "${ + cidrhost("169.254.0.0/27", 1) + }/30" + ike_version = 2 + peer_external_gateway_interface = null + router = null + shared_secret = module.landing-to-dev-ew1-vpn.random_secret + vpn_gateway_interface = 0 } - bgp_peer_options = local.vpn_spoke_bgp_peer_options.dev-ew1 - bgp_session_range = "${cidrhost( - var.vpn_spoke_configs.dev-ew1.session_range, 1 + (t * 4) - )}/30" - ike_version = 2 - peer_external_gateway_interface = null - router = null - shared_secret = module.landing-to-dev-ew1-vpn.random_secret - vpn_gateway_interface = t + 1 = { + bgp_peer = { + address = cidrhost("169.254.0.0/27", 6) + asn = var.router_spoke_configs.landing-ew1.asn + } + bgp_peer_options = local.vpn_spoke_bgp_peer_options.dev-ew1 + bgp_session_range = "${ + cidrhost("169.254.0.0/27", 5) + }/30" + ike_version = 2 + peer_external_gateway_interface = null + router = null + shared_secret = module.landing-to-dev-ew1-vpn.random_secret + vpn_gateway_interface = 1 } } } diff --git a/fast/stages/02-networking-vpn/vpn-spoke-prod-ew1.tf b/fast/stages/02-networking-vpn/vpn-spoke-prod-ew1.tf new file mode 100644 index 00000000..9562e4ce --- /dev/null +++ b/fast/stages/02-networking-vpn/vpn-spoke-prod-ew1.tf @@ -0,0 +1,107 @@ +/** + * 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 VPN between landing and production spoke in ew1. + +# local.vpn_spoke_bgp_peer_options is defined in the dev VPN file + +module "landing-to-prod-ew1-vpn" { + source = "../../../modules/net-vpn-ha" + project_id = module.landing-project.project_id + network = module.landing-vpc.self_link + region = "europe-west1" + name = "vpn-to-prod-ew1" + router_create = true + router_name = "landing-vpn-ew1" + router_asn = var.router_spoke_configs.landing-ew1.asn + peer_gcp_gateway = module.prod-to-landing-ew1-vpn.self_link + tunnels = { + 0 = { + bgp_peer = { + address = cidrhost("169.254.0.64/27", 1) + asn = var.router_spoke_configs.spoke-prod-ew1.asn + } + bgp_peer_options = local.vpn_spoke_bgp_peer_options.landing-ew1 + bgp_session_range = "${ + cidrhost("169.254.0.64/27", 2) + }/30" + ike_version = 2 + peer_external_gateway_interface = null + router = null + shared_secret = null + vpn_gateway_interface = 0 + } + 1 = { + bgp_peer = { + address = cidrhost("169.254.0.64/27", 5) + asn = var.router_spoke_configs.spoke-prod-ew1.asn + } + bgp_peer_options = local.vpn_spoke_bgp_peer_options.landing-ew1 + bgp_session_range = "${ + cidrhost("169.254.0.64/27", 6) + }/30" + ike_version = 2 + peer_external_gateway_interface = null + router = null + shared_secret = null + vpn_gateway_interface = 1 + } + } +} + +module "prod-to-landing-ew1-vpn" { + source = "../../../modules/net-vpn-ha" + project_id = module.prod-spoke-project.project_id + network = module.prod-spoke-vpc.self_link + region = "europe-west1" + name = "vpn-to-landing-ew1" + router_create = true + router_name = "prod-spoke-vpn-ew1" + router_asn = var.router_spoke_configs.spoke-prod-ew1.asn + peer_gcp_gateway = module.landing-to-prod-ew1-vpn.self_link + tunnels = { + 0 = { + bgp_peer = { + address = cidrhost("169.254.0.64/27", 2) + asn = var.router_spoke_configs.landing-ew1.asn + } + bgp_peer_options = local.vpn_spoke_bgp_peer_options.prod-ew1 + bgp_session_range = "${ + cidrhost("169.254.0.64/27", 1) + }/30" + ike_version = 2 + peer_external_gateway_interface = null + router = null + shared_secret = module.landing-to-prod-ew1-vpn.random_secret + vpn_gateway_interface = 0 + } + 1 = { + bgp_peer = { + address = cidrhost("169.254.0.64/27", 6) + asn = var.router_spoke_configs.landing-ew1.asn + } + bgp_peer_options = local.vpn_spoke_bgp_peer_options.prod-ew1 + bgp_session_range = "${ + cidrhost("169.254.0.64/27", 5) + }/30" + ike_version = 2 + peer_external_gateway_interface = null + router = null + shared_secret = module.landing-to-prod-ew1-vpn.random_secret + vpn_gateway_interface = 1 + } + } +} diff --git a/fast/stages/02-networking-vpn/vpn-spoke-prod-ew4.tf b/fast/stages/02-networking-vpn/vpn-spoke-prod-ew4.tf new file mode 100644 index 00000000..cbee0bef --- /dev/null +++ b/fast/stages/02-networking-vpn/vpn-spoke-prod-ew4.tf @@ -0,0 +1,107 @@ +/** + * 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 VPN between landing and production spoke in ew4. + +# local.vpn_spoke_bgp_peer_options is defined in the dev VPN file + +module "landing-to-prod-ew4-vpn" { + source = "../../../modules/net-vpn-ha" + project_id = module.landing-project.project_id + network = module.landing-vpc.self_link + region = "europe-west4" + name = "vpn-to-prod-ew4" + router_create = true + router_name = "landing-vpn-ew4" + router_asn = var.router_spoke_configs.landing-ew4.asn + peer_gcp_gateway = module.prod-to-landing-ew4-vpn.self_link + tunnels = { + 0 = { + bgp_peer = { + address = cidrhost("169.254.0.96/27", 1) + asn = var.router_spoke_configs.spoke-prod-ew4.asn + } + bgp_peer_options = local.vpn_spoke_bgp_peer_options.landing-ew4 + bgp_session_range = "${ + cidrhost("169.254.0.96/27", 2) + }/30" + ike_version = 2 + peer_external_gateway_interface = null + router = null + shared_secret = null + vpn_gateway_interface = 0 + } + 1 = { + bgp_peer = { + address = cidrhost("169.254.0.96/27", 5) + asn = var.router_spoke_configs.spoke-prod-ew4.asn + } + bgp_peer_options = local.vpn_spoke_bgp_peer_options.landing-ew4 + bgp_session_range = "${ + cidrhost("169.254.0.96/27", 6) + }/30" + ike_version = 2 + peer_external_gateway_interface = null + router = null + shared_secret = null + vpn_gateway_interface = 1 + } + } +} + +module "prod-to-landing-ew4-vpn" { + source = "../../../modules/net-vpn-ha" + project_id = module.prod-spoke-project.project_id + network = module.prod-spoke-vpc.self_link + region = "europe-west4" + name = "vpn-to-landing-ew4" + router_create = true + router_name = "prod-spoke-vpn-ew4" + router_asn = var.router_spoke_configs.spoke-prod-ew4.asn + peer_gcp_gateway = module.landing-to-prod-ew4-vpn.self_link + tunnels = { + 0 = { + bgp_peer = { + address = cidrhost("169.254.0.96/27", 2) + asn = var.router_spoke_configs.landing-ew4.asn + } + bgp_peer_options = local.vpn_spoke_bgp_peer_options.prod-ew4 + bgp_session_range = "${ + cidrhost("169.254.0.96/27", 1) + }/30" + ike_version = 2 + peer_external_gateway_interface = null + router = null + shared_secret = module.landing-to-prod-ew4-vpn.random_secret + vpn_gateway_interface = 0 + } + 1 = { + bgp_peer = { + address = cidrhost("169.254.0.96/27", 6) + asn = var.router_spoke_configs.landing-ew4.asn + } + bgp_peer_options = local.vpn_spoke_bgp_peer_options.prod-ew4 + bgp_session_range = "${ + cidrhost("169.254.0.96/27", 5) + }/30" + ike_version = 2 + peer_external_gateway_interface = null + router = null + shared_secret = module.landing-to-prod-ew4-vpn.random_secret + vpn_gateway_interface = 1 + } + } +} diff --git a/fast/stages/02-networking-vpn/vpn-spoke-prod.tf b/fast/stages/02-networking-vpn/vpn-spoke-prod.tf deleted file mode 100644 index 3001c4ec..00000000 --- a/fast/stages/02-networking-vpn/vpn-spoke-prod.tf +++ /dev/null @@ -1,139 +0,0 @@ -/** - * 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 VPN between landing and production spoke. - -# local.vpn_spoke_bgp_peer_options is defined in the dev VPN file - -module "landing-to-prod-ew1-vpn" { - source = "../../../modules/net-vpn-ha" - project_id = module.landing-project.project_id - network = module.landing-vpc.self_link - region = "europe-west1" - name = "vpn-to-prod-ew1" - router_create = true - router_name = "landing-vpn-ew1" - router_asn = var.router_spoke_configs.landing-ew1.asn - peer_gcp_gateway = module.prod-to-landing-ew1-vpn.self_link - tunnels = { for t in range(2) : "tunnel-${t}" => { - bgp_peer = { - address = cidrhost( - var.vpn_spoke_configs.prod-ew1.session_range, 1 + (t * 4) - ) - asn = var.router_spoke_configs.spoke-prod-ew1.asn - } - bgp_peer_options = local.vpn_spoke_bgp_peer_options.landing-ew1 - bgp_session_range = "${cidrhost( - var.vpn_spoke_configs.prod-ew1.session_range, 2 + (t * 4) - )}/30" - ike_version = 2 - peer_external_gateway_interface = null - router = null - shared_secret = null - vpn_gateway_interface = t - } - } -} - -module "prod-to-landing-ew1-vpn" { - source = "../../../modules/net-vpn-ha" - project_id = module.prod-spoke-project.project_id - network = module.prod-spoke-vpc.self_link - region = "europe-west1" - name = "vpn-to-landing-ew1" - router_create = true - router_name = "prod-spoke-vpn-ew1" - router_asn = var.router_spoke_configs.spoke-prod-ew1.asn - peer_gcp_gateway = module.landing-to-prod-ew1-vpn.self_link - tunnels = { for t in range(2) : "tunnel-${t}" => { - bgp_peer = { - address = cidrhost( - var.vpn_spoke_configs.prod-ew1.session_range, 2 + (t * 4) - ) - asn = var.router_spoke_configs.landing-ew1.asn - } - bgp_peer_options = local.vpn_spoke_bgp_peer_options.prod-ew1 - bgp_session_range = "${cidrhost( - var.vpn_spoke_configs.prod-ew1.session_range, 1 + (t * 4) - )}/30" - ike_version = 2 - peer_external_gateway_interface = null - router = null - shared_secret = module.landing-to-prod-ew1-vpn.random_secret - vpn_gateway_interface = t - } - } -} - -module "landing-to-prod-ew4-vpn" { - source = "../../../modules/net-vpn-ha" - project_id = module.landing-project.project_id - network = module.landing-vpc.self_link - region = "europe-west4" - name = "vpn-to-prod-ew4" - router_create = true - router_name = "landing-vpn-ew4" - router_asn = var.router_spoke_configs.landing-ew4.asn - peer_gcp_gateway = module.prod-to-landing-ew4-vpn.self_link - tunnels = { for t in range(2) : "tunnel-${t}" => { - bgp_peer = { - address = cidrhost( - var.vpn_spoke_configs.prod-ew4.session_range, 1 + (t * 4) - ) - asn = var.router_spoke_configs.spoke-prod-ew4.asn - } - bgp_peer_options = local.vpn_spoke_bgp_peer_options.landing-ew4 - bgp_session_range = "${cidrhost( - var.vpn_spoke_configs.prod-ew4.session_range, 2 + (t * 4) - )}/30" - ike_version = 2 - peer_external_gateway_interface = null - router = null - shared_secret = null - vpn_gateway_interface = t - } - } -} - -module "prod-to-landing-ew4-vpn" { - source = "../../../modules/net-vpn-ha" - project_id = module.prod-spoke-project.project_id - network = module.prod-spoke-vpc.self_link - region = "europe-west4" - name = "vpn-to-landing-ew4" - router_create = true - router_name = "prod-spoke-vpn-ew4" - router_asn = var.router_spoke_configs.spoke-prod-ew4.asn - peer_gcp_gateway = module.landing-to-prod-ew4-vpn.self_link - tunnels = { for t in range(2) : "tunnel-${t}" => { - bgp_peer = { - address = cidrhost( - var.vpn_spoke_configs.prod-ew4.session_range, 2 + (t * 4) - ) - asn = var.router_spoke_configs.landing-ew4.asn - } - bgp_peer_options = local.vpn_spoke_bgp_peer_options.prod-ew4 - bgp_session_range = "${cidrhost( - var.vpn_spoke_configs.prod-ew4.session_range, 1 + (t * 4) - )}/30" - ike_version = 2 - peer_external_gateway_interface = null - router = null - shared_secret = module.landing-to-prod-ew4-vpn.random_secret - vpn_gateway_interface = t - } - } -}