feat(2-networking): add NS And DS record dynamically from sub-zones records (#3818)
* feat(2-networking): add NS And DS record dynamically from sub-zones records * tfdoc * feat(test): 2-networking adding dns_delegations test with and without DSSEC --------- Co-authored-by: Ludovico Magnocavallo <ludomagno@google.com> Co-authored-by: Simone Ruffilli <sruffilli@google.com>
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
- [Networking projects](#networking-projects)
|
||||
- [VPCs](#vpcs)
|
||||
- [DNS](#dns)
|
||||
- [Delegated Public Zones](#delegated-public-zones)
|
||||
- [Firewall Policies](#firewall-policies)
|
||||
- [Cloud NAT and Routers](#cloud-nat-and-routers)
|
||||
- [VPC Connectivity](#vpc-connectivity)
|
||||
@@ -200,6 +201,68 @@ In the default dataset, DNS is centralized in the `net-core-0` (hub) project. It
|
||||
|
||||
The spoke VPCs have their own private zones for subdomains (e.g., `dev.test.`) and use the hub for all other DNS lookups.
|
||||
|
||||
#### Delegated Public Zones
|
||||
|
||||
The factory also supports delegating public zones, which is useful for scenarios where a parent public zone is managed in one project, and you want to delegate a subdomain to a different project.
|
||||
|
||||
To configure this, you first define the parent public zone. Then, for the delegated zone, you specify `delegation_config` pointing to the parent zone. This automatically creates the necessary `NS` (and `DS` records if you are using DNSSEC) records in the parent zone.
|
||||
|
||||
Here's an example of how to set this up:
|
||||
|
||||
```yaml
|
||||
# Parent public zone (e.g., in dns/zones/net-core-0/pub-gcp-example-com.yaml)
|
||||
#
|
||||
# yaml-language-server: $schema=../../../../../schemas/dns.schema.json
|
||||
|
||||
project_id: $project_ids:net-core-0
|
||||
domain: gcp.example.com.
|
||||
public:
|
||||
enable_logging: false
|
||||
dnssec_config:
|
||||
state: "on"
|
||||
non_existence: "nsec3"
|
||||
key_signing_key:
|
||||
algorithm: "ecdsap256sha256"
|
||||
key_length: 256
|
||||
zone_signing_key:
|
||||
algorithm: "ecdsap256sha256"
|
||||
key_length: 256
|
||||
recordsets:
|
||||
"A localhost":
|
||||
records: ["127.0.0.1"]
|
||||
delegations:
|
||||
- net-dev-0/pub-dev-gcp-example-com
|
||||
```
|
||||
|
||||
```yaml
|
||||
# Delegated child zone (e.g., in dns/zones/net-dev-0/pub-dev-gcp-example-com.yaml)
|
||||
#
|
||||
# yaml-language-server: $schema=../../../../../schemas/dns.schema.json
|
||||
|
||||
project_id: $project_ids:net-dev-0
|
||||
domain: dev.gcp.example.com.
|
||||
public:
|
||||
enable_logging: false
|
||||
dnssec_config:
|
||||
state: "on"
|
||||
non_existence: "nsec3"
|
||||
key_signing_key:
|
||||
algorithm: "ecdsap256sha256"
|
||||
key_length: 256
|
||||
zone_signing_key:
|
||||
algorithm: "ecdsap256sha256"
|
||||
key_length: 256
|
||||
recordsets:
|
||||
"A localhost":
|
||||
records: ["127.0.0.1"]
|
||||
|
||||
```
|
||||
|
||||
In this example:
|
||||
- A public zone for `gcp.example.com.` is created in the `net-core-0` project.
|
||||
- A separate public zone for `dev.gcp.example.com.` is created in the `net-dev-0` project.
|
||||
- The `delegation_config` in the parent zone tells the factory to find the child zone named `pub-dev-gcp-example-com` in the `net-dev-0` project and create the necessary `NS` and `DS` records to make the delegation effective.
|
||||
|
||||
### Firewall Policies
|
||||
|
||||
This stage supports both VPC-level firewall rules and hierarchical firewall policies.
|
||||
|
||||
@@ -75,6 +75,30 @@ locals {
|
||||
}
|
||||
)
|
||||
}
|
||||
# DNS delegations: auto-create NS and DS records in parent zones
|
||||
# from child zone outputs (name_servers, dns_keys).
|
||||
# Grouped by parent zone key so a single module call per parent
|
||||
# handles all its delegation recordsets.
|
||||
dns_delegation_recordsets = {
|
||||
for zone_key, zone_config in local.dns_zones :
|
||||
zone_key => merge(
|
||||
{
|
||||
for child_key in zone_config.delegations :
|
||||
"NS ${module.dns-zones[child_key].domain}" => {
|
||||
records = module.dns-zones[child_key].name_servers
|
||||
}
|
||||
},
|
||||
{
|
||||
for child_key in zone_config.delegations :
|
||||
"DS ${module.dns-zones[child_key].domain}" => {
|
||||
records = [module.dns-zones[child_key].dns_keys.key_signing_keys[0].ds_record]
|
||||
}
|
||||
if try(local.dns_zones[child_key].zone_config.public.dnssec_config.state, "off") == "on"
|
||||
}
|
||||
)
|
||||
if length(try(zone_config.delegations, [])) > 0
|
||||
}
|
||||
|
||||
# DNS response policies
|
||||
_dns_response_policies_files = try(
|
||||
fileset(local.paths.dns_response_policies, "**/*.yaml"), []
|
||||
@@ -115,6 +139,19 @@ module "dns-zones" {
|
||||
depends_on = [module.vpc-factory]
|
||||
}
|
||||
|
||||
module "dns-delegations" {
|
||||
source = "../../../modules/dns"
|
||||
for_each = local.dns_delegation_recordsets
|
||||
project_id = local.dns_zones[each.key].project_id
|
||||
name = replace(each.key, "/", "-")
|
||||
recordsets = each.value
|
||||
context = {
|
||||
project_ids = local.ctx_projects.project_ids
|
||||
networks = local.ctx_vpcs.self_links
|
||||
}
|
||||
depends_on = [module.dns-zones]
|
||||
}
|
||||
|
||||
module "dns-response-policies" {
|
||||
source = "../../../modules/dns-response-policy"
|
||||
for_each = local.dns_response_policies
|
||||
|
||||
@@ -38,6 +38,13 @@
|
||||
},
|
||||
"public": {
|
||||
"$ref": "#/$defs/public_zone"
|
||||
},
|
||||
"delegations": {
|
||||
"type": "array",
|
||||
"description": "List of child zone keys (e.g. 'net-dev-0/pvt-dev-zone') whose NS and DS records are auto-created in this parent zone.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
- **peering**: *reference([peering_zone](#refs-peering_zone))*
|
||||
- **forwarding**: *reference([forwarding_zone](#refs-forwarding_zone))*
|
||||
- **public**: *reference([public_zone](#refs-public_zone))*
|
||||
- **delegations**: *array*
|
||||
- items: *string*
|
||||
|
||||
## Definitions
|
||||
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
# Copyright 2026 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.
|
||||
|
||||
# yaml-language-server: $schema=../../schemas/defaults.schema.json
|
||||
|
||||
context:
|
||||
cidr_ranges_sets:
|
||||
healthchecks:
|
||||
- 35.191.0.0/16
|
||||
- 130.211.0.0/22
|
||||
- 209.85.152.0/22
|
||||
- 209.85.204.0/22
|
||||
rfc1918:
|
||||
- 10.0.0.0/8
|
||||
- 172.16.0.0/12
|
||||
- 192.168.0.0/16
|
||||
locations:
|
||||
primary: europe-west1
|
||||
secondary: europe-west3
|
||||
iam_principals: {}
|
||||
projects:
|
||||
defaults:
|
||||
locations:
|
||||
storage: eu
|
||||
vpcs:
|
||||
auto_create_subnetworks: false
|
||||
delete_default_route_on_create: true
|
||||
mtu: 1500
|
||||
output_files:
|
||||
storage_bucket: $storage_buckets:iac-0/iac-outputs
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
# skip boilerplate check
|
||||
---
|
||||
# start of document (---) avoids errors if the file only contains comments
|
||||
|
||||
# yaml-language-server: $schema=../../../../../schemas/dns.schema.json
|
||||
|
||||
project_id: $project_ids:net-core-0
|
||||
domain: child-dnssec.example.com.
|
||||
public:
|
||||
dnssec_config:
|
||||
state: "on"
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
# skip boilerplate check
|
||||
---
|
||||
# start of document (---) avoids errors if the file only contains comments
|
||||
|
||||
# yaml-language-server: $schema=../../../../../schemas/dns.schema.json
|
||||
|
||||
project_id: $project_ids:net-core-0
|
||||
domain: child.example.com.
|
||||
public:
|
||||
dnssec_config:
|
||||
state: "off"
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
# skip boilerplate check
|
||||
---
|
||||
# start of document (---) avoids errors if the file only contains comments
|
||||
|
||||
# yaml-language-server: $schema=../../../../../schemas/dns.schema.json
|
||||
|
||||
project_id: $project_ids:net-core-0
|
||||
domain: example.com.
|
||||
public:
|
||||
dnssec_config:
|
||||
state: "off"
|
||||
delegations:
|
||||
- net-core-0/pub-child
|
||||
- net-core-0/pub-child-dnssec
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
# skip boilerplate check
|
||||
---
|
||||
# start of document (---) avoids errors if the file only contains comments
|
||||
|
||||
# yaml-language-server: $schema=../../../schemas/project.schema.json
|
||||
name: prod-net-core-0
|
||||
parent: $folder_ids:networking
|
||||
services:
|
||||
- container.googleapis.com
|
||||
- compute.googleapis.com
|
||||
- dns.googleapis.com
|
||||
- iap.googleapis.com
|
||||
- networkmanagement.googleapis.com
|
||||
- networksecurity.googleapis.com
|
||||
- servicenetworking.googleapis.com
|
||||
- stackdriver.googleapis.com
|
||||
- vpcaccess.googleapis.com
|
||||
shared_vpc_host_config:
|
||||
enabled: true
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
# skip boilerplate check
|
||||
---
|
||||
# start of document (---) avoids errors if the file only contains comments
|
||||
|
||||
# yaml-language-server: $schema=../../../../schemas/vpc.schema.json
|
||||
|
||||
project_id: $project_ids:net-core-0
|
||||
name: core-0
|
||||
delete_default_routes_on_create: true
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
# skip boilerplate check
|
||||
|
||||
# yaml-language-server: $schema=../../../../../schemas/subnet.schema.json
|
||||
|
||||
name: core-default
|
||||
region: $locations:primary
|
||||
ip_cidr_range: 10.71.0.0/24
|
||||
description: Default primary-region subnet for core
|
||||
|
||||
35
tests/fast/stages/s2_networking/dns_delegations.tfvars
Normal file
35
tests/fast/stages/s2_networking/dns_delegations.tfvars
Normal file
@@ -0,0 +1,35 @@
|
||||
automation = {
|
||||
outputs_bucket = "test"
|
||||
}
|
||||
billing_account = {
|
||||
id = "000000-111111-222222"
|
||||
}
|
||||
factories_config = {
|
||||
dataset = "./data-testdns-delegation"
|
||||
paths = {
|
||||
defaults = "defaults.yaml"
|
||||
}
|
||||
}
|
||||
folder_ids = {
|
||||
"networking" = "folders/12345678"
|
||||
"networking/prod" = "folders/23456789"
|
||||
"networking/dev" = "folders/34567890"
|
||||
}
|
||||
organization = {
|
||||
domain = "fast.example.com"
|
||||
id = 123456789012
|
||||
customer_id = "C00000000"
|
||||
}
|
||||
prefix = "fast"
|
||||
service_accounts = {
|
||||
"iac-0/iac-pf-rw" = "iac-pf-rw@test.iam.gserviceaccount.com"
|
||||
"iac-0/iac-pf-ro" = "iac-pf-ro@test.iam.gserviceaccount.com"
|
||||
}
|
||||
storage_buckets = {
|
||||
"iac-0/iac-outputs" = "test"
|
||||
}
|
||||
tag_values = {
|
||||
"environment/development" = "tagValues/12345"
|
||||
"environment/production" = "tagValues/12346"
|
||||
}
|
||||
|
||||
52
tests/fast/stages/s2_networking/dns_delegations.yaml
Normal file
52
tests/fast/stages/s2_networking/dns_delegations.yaml
Normal file
@@ -0,0 +1,52 @@
|
||||
# Copyright 2026 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:
|
||||
google_compute_network: 1
|
||||
google_compute_route: 3
|
||||
google_compute_shared_vpc_host_project: 1
|
||||
google_compute_subnetwork: 1
|
||||
google_dns_managed_zone: 4
|
||||
google_dns_record_set: 3
|
||||
google_project: 1
|
||||
google_project_iam_member: 7
|
||||
google_project_service: 9
|
||||
google_project_service_identity: 7
|
||||
google_storage_bucket_object: 2
|
||||
modules: 9
|
||||
resources: 42
|
||||
terraform_data: 2
|
||||
values:
|
||||
module.dns-delegations["net-core-0/pub-parent"].google_dns_record_set.dns_record_set["NS child.example.com."]:
|
||||
managed_zone: net-core-0-pub-parent
|
||||
name: child.example.com.
|
||||
project: fast-prod-net-core-0
|
||||
routing_policy: []
|
||||
ttl: 300
|
||||
type: NS
|
||||
module.dns-delegations["net-core-0/pub-parent"].google_dns_record_set.dns_record_set["NS child-dnssec.example.com."]:
|
||||
managed_zone: net-core-0-pub-parent
|
||||
name: child-dnssec.example.com.
|
||||
project: fast-prod-net-core-0
|
||||
routing_policy: []
|
||||
ttl: 300
|
||||
type: NS
|
||||
module.dns-delegations["net-core-0/pub-parent"].google_dns_record_set.dns_record_set["DS child-dnssec.example.com."]:
|
||||
managed_zone: net-core-0-pub-parent
|
||||
name: child-dnssec.example.com.
|
||||
project: fast-prod-net-core-0
|
||||
routing_policy: []
|
||||
ttl: 300
|
||||
type: DS
|
||||
|
||||
@@ -17,6 +17,11 @@ module: fast/stages/2-networking
|
||||
tests:
|
||||
# peering is renamed to simple so as to trigger tflint
|
||||
simple:
|
||||
dns_delegations:
|
||||
inventory:
|
||||
- dns_delegations.yaml
|
||||
extra_dirs:
|
||||
- ../../../tests/fast/stages/s2_networking/data-testdns-delegation
|
||||
ncc:
|
||||
nva:
|
||||
vlan_attachments:
|
||||
|
||||
Reference in New Issue
Block a user