From 790935785bf8df3fa5d8a7b11d9bda57d5650732 Mon Sep 17 00:00:00 2001 From: Gonzalo Rojas Date: Fri, 12 Jun 2026 12:15:05 -0300 Subject: [PATCH 1/2] feat(cloud-dns): add optional DNSSEC support for public zones Adds a dnssec_enabled variable (default false) that configures dnssec_config on the managed zone. Only applies to public zones, since Cloud DNS does not support DNSSEC on private zones. Addresses Trivy alert GCP-0013 (Cloud DNS should use DNSSEC). --- .../gcp/cloud-dns/.terraform.lock.hcl | 20 +++++++++++ infrastructure/gcp/cloud-dns/README.md | 3 +- infrastructure/gcp/cloud-dns/main.tf | 7 ++++ .../gcp/cloud-dns/tests/cloud_dns.tftest.hcl | 36 +++++++++++++++++++ infrastructure/gcp/cloud-dns/variables.tf | 6 ++++ 5 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 infrastructure/gcp/cloud-dns/.terraform.lock.hcl diff --git a/infrastructure/gcp/cloud-dns/.terraform.lock.hcl b/infrastructure/gcp/cloud-dns/.terraform.lock.hcl new file mode 100644 index 00000000..a947a847 --- /dev/null +++ b/infrastructure/gcp/cloud-dns/.terraform.lock.hcl @@ -0,0 +1,20 @@ +# This file is maintained automatically by "tofu init". +# Manual edits may be lost in future updates. + +provider "registry.opentofu.org/hashicorp/google" { + version = "6.50.0" + constraints = ">= 5.0.0, < 7.0.0" + hashes = [ + "h1:MAAe4zFFdqS9M5rpmJK/vKgdb6ZMD/s/0Xd97yTDipA=", + "zh:1d4695f807d998f11fcdcfa174766287b82a8093513af857bcdad2d81c642480", + "zh:3173ac5df0294624d113812e49e2a55714aff7db617488168cecdf4168df9e29", + "zh:34d2b3d44c23bd6354fc4ab5917b302872ea1ab8de107034567f955b1717fa5b", + "zh:3a77f3cc2f3664cd5aaeeef4d044e6ec1695a079588fffec3ca03953664e5f04", + "zh:6b444e4b629ea8dc8cb112a39dde098dc5584d26d6de4177558f556a9a226696", + "zh:96545c8cd4d3a57069c5d1799eab5aedd887e16d98b5559a195f6d2c2d9bc674", + "zh:ba464caafde95ee16671d6b5ec90f053ed77a9d06c567456db6efd9160fa3165", + "zh:d876938e5b0d3f57a984d9be72467995f87fef6569968623415dc51d9f54d30b", + "zh:dfd908d873e314ab807d0abc9cfd42d2611cd06dc1b9ec719ebdbb738e8e68d6", + "zh:f9f16819a7738d564afd45fd169ba61004ec4e4e7089d2a4950cb8895be1fe1f", + ] +} diff --git a/infrastructure/gcp/cloud-dns/README.md b/infrastructure/gcp/cloud-dns/README.md index 5dc4c9d1..60899f77 100644 --- a/infrastructure/gcp/cloud-dns/README.md +++ b/infrastructure/gcp/cloud-dns/README.md @@ -49,7 +49,7 @@ resource "example_resource" "this" { | Name | Version | |------|---------| -| [google](#provider\_google) | >= 5.0, < 7.0 | +| [google](#provider\_google) | 6.50.0 | ## Resources @@ -61,6 +61,7 @@ resource "example_resource" "this" { | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| +| [dnssec\_enabled](#input\_dnssec\_enabled) | Enable DNSSEC for the zone. Only applies to public zones; requires publishing the DS record at the domain registrar to take effect. | `bool` | `false` | no | | [domain\_name](#input\_domain\_name) | The domain name for the DNS zone (without trailing dot, e.g. example.com) | `string` | n/a | yes | | [private\_zone\_networks](#input\_private\_zone\_networks) | VPC network self-links for private zones | `list(string)` | `[]` | no | | [project\_id](#input\_project\_id) | The GCP project ID | `string` | n/a | yes | diff --git a/infrastructure/gcp/cloud-dns/main.tf b/infrastructure/gcp/cloud-dns/main.tf index 630b6a70..c6ec6880 100644 --- a/infrastructure/gcp/cloud-dns/main.tf +++ b/infrastructure/gcp/cloud-dns/main.tf @@ -8,6 +8,13 @@ resource "google_dns_managed_zone" "zone" { dns_name = "${var.domain_name}." visibility = var.visibility + dynamic "dnssec_config" { + for_each = var.visibility == "public" && var.dnssec_enabled ? [1] : [] + content { + state = "on" + } + } + dynamic "private_visibility_config" { for_each = var.visibility == "private" ? [1] : [] content { diff --git a/infrastructure/gcp/cloud-dns/tests/cloud_dns.tftest.hcl b/infrastructure/gcp/cloud-dns/tests/cloud_dns.tftest.hcl index d8dabbfd..58fc587e 100644 --- a/infrastructure/gcp/cloud-dns/tests/cloud_dns.tftest.hcl +++ b/infrastructure/gcp/cloud-dns/tests/cloud_dns.tftest.hcl @@ -74,3 +74,39 @@ run "labels_applied_from_tags" { error_message = "Labels should be applied from tags variable" } } + +run "dnssec_disabled_by_default" { + command = plan + + assert { + condition = length(google_dns_managed_zone.zone.dnssec_config) == 0 + error_message = "DNSSEC should not be configured by default" + } +} + +run "dnssec_enabled_on_public_zone" { + command = plan + + variables { + dnssec_enabled = true + } + + assert { + condition = google_dns_managed_zone.zone.dnssec_config[0].state == "on" + error_message = "DNSSEC should be on when dnssec_enabled is true on a public zone" + } +} + +run "dnssec_ignored_on_private_zone" { + command = plan + + variables { + visibility = "private" + dnssec_enabled = true + } + + assert { + condition = length(google_dns_managed_zone.zone.dnssec_config) == 0 + error_message = "DNSSEC should not be configured on private zones even when dnssec_enabled is true" + } +} diff --git a/infrastructure/gcp/cloud-dns/variables.tf b/infrastructure/gcp/cloud-dns/variables.tf index 5a0b9b9d..659bfb2c 100644 --- a/infrastructure/gcp/cloud-dns/variables.tf +++ b/infrastructure/gcp/cloud-dns/variables.tf @@ -26,6 +26,12 @@ variable "private_zone_networks" { default = [] } +variable "dnssec_enabled" { + type = bool + description = "Enable DNSSEC for the zone. Only applies to public zones; requires publishing the DS record at the domain registrar to take effect." + default = false +} + variable "tags" { type = map(string) description = "A mapping of labels to assign to the DNS managed zone" From 5b8519b5cab7f9bd143a78674988c77adef9984f Mon Sep 17 00:00:00 2001 From: Gonzalo Rojas Date: Fri, 12 Jun 2026 12:46:43 -0300 Subject: [PATCH 2/2] feat(cloud-dns): enable DNSSEC by default on public zones Zone signing is inert until the consumer publishes the DS record at the domain registrar, so existing zones keep resolving unchanged. Consumers can opt out with dnssec_enabled = false. Resolves Trivy alert GCP-0013 (Cloud DNS should use DNSSEC). --- infrastructure/gcp/cloud-dns/README.md | 2 +- .../gcp/cloud-dns/tests/cloud_dns.tftest.hcl | 14 +++++++------- infrastructure/gcp/cloud-dns/variables.tf | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/infrastructure/gcp/cloud-dns/README.md b/infrastructure/gcp/cloud-dns/README.md index 60899f77..8320ae47 100644 --- a/infrastructure/gcp/cloud-dns/README.md +++ b/infrastructure/gcp/cloud-dns/README.md @@ -61,7 +61,7 @@ resource "example_resource" "this" { | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [dnssec\_enabled](#input\_dnssec\_enabled) | Enable DNSSEC for the zone. Only applies to public zones; requires publishing the DS record at the domain registrar to take effect. | `bool` | `false` | no | +| [dnssec\_enabled](#input\_dnssec\_enabled) | Enable DNSSEC for the zone. Only applies to public zones; signing is inert until the DS record is published at the domain registrar. | `bool` | `true` | no | | [domain\_name](#input\_domain\_name) | The domain name for the DNS zone (without trailing dot, e.g. example.com) | `string` | n/a | yes | | [private\_zone\_networks](#input\_private\_zone\_networks) | VPC network self-links for private zones | `list(string)` | `[]` | no | | [project\_id](#input\_project\_id) | The GCP project ID | `string` | n/a | yes | diff --git a/infrastructure/gcp/cloud-dns/tests/cloud_dns.tftest.hcl b/infrastructure/gcp/cloud-dns/tests/cloud_dns.tftest.hcl index 58fc587e..d4a3ba85 100644 --- a/infrastructure/gcp/cloud-dns/tests/cloud_dns.tftest.hcl +++ b/infrastructure/gcp/cloud-dns/tests/cloud_dns.tftest.hcl @@ -75,25 +75,25 @@ run "labels_applied_from_tags" { } } -run "dnssec_disabled_by_default" { +run "dnssec_enabled_by_default_on_public_zone" { command = plan assert { - condition = length(google_dns_managed_zone.zone.dnssec_config) == 0 - error_message = "DNSSEC should not be configured by default" + condition = google_dns_managed_zone.zone.dnssec_config[0].state == "on" + error_message = "DNSSEC should be on by default for public zones" } } -run "dnssec_enabled_on_public_zone" { +run "dnssec_can_be_disabled" { command = plan variables { - dnssec_enabled = true + dnssec_enabled = false } assert { - condition = google_dns_managed_zone.zone.dnssec_config[0].state == "on" - error_message = "DNSSEC should be on when dnssec_enabled is true on a public zone" + condition = length(google_dns_managed_zone.zone.dnssec_config) == 0 + error_message = "DNSSEC should not be configured when dnssec_enabled is false" } } diff --git a/infrastructure/gcp/cloud-dns/variables.tf b/infrastructure/gcp/cloud-dns/variables.tf index 659bfb2c..57857d60 100644 --- a/infrastructure/gcp/cloud-dns/variables.tf +++ b/infrastructure/gcp/cloud-dns/variables.tf @@ -28,8 +28,8 @@ variable "private_zone_networks" { variable "dnssec_enabled" { type = bool - description = "Enable DNSSEC for the zone. Only applies to public zones; requires publishing the DS record at the domain registrar to take effect." - default = false + description = "Enable DNSSEC for the zone. Only applies to public zones; signing is inert until the DS record is published at the domain registrar." + default = true } variable "tags" {