Back to blog
Best PracticesCloud SecurityGCPNetworking

DNSSEC Not Enabled on GCP Cloud DNS Zones

Learn why DNSSEC matters for GCP Cloud DNS, the spoofing risks of leaving it off, and step-by-step gcloud and Terraform commands to enable and verify it.

TL;DR

This check flags GCP Cloud DNS managed zones running without DNSSEC, which leaves resolvers unable to verify your DNS records and opens the door to cache poisoning and spoofing. Enable DNSSEC on the zone and publish the resulting DS record at your registrar.

DNS is the address book of the internet, and like any address book, it only works if you can trust the entries. The trouble is that classic DNS has no built-in way to prove a response actually came from the authoritative source. A resolver asks "what is the IP for app.example.com?" and accepts whatever comes back, no signatures, no verification. That gap is exactly what DNSSEC was designed to close.

The dns_nodnsec check looks at your GCP Cloud DNS managed zones and reports any that have DNSSEC turned off. If you run public-facing zones in Cloud DNS, this is one of the cheaper, higher-leverage fixes you can make.


What this check detects

The check inspects each Cloud DNS managed zone in your GCP project and reads its DNSSEC configuration. A zone passes when DNSSEC signing is set to on and fails when it is off (or was never configured).

Concretely, every Cloud DNS managed zone has a dnssecConfig block. When DNSSEC is disabled, that block reports a state of off, which means Cloud DNS is serving unsigned responses for the zone.

Note: DNSSEC works by signing DNS records with a private key. Resolvers validate those records using a chain of trust that starts at the DNS root and runs down through the parent zone via a DS (Delegation Signer) record. Without signing enabled, there is nothing for a validating resolver to check.


Why it matters

Without DNSSEC, a zone's responses are forgeable. An attacker who can inject a response, whether through cache poisoning, an on-path position, or a compromised upstream resolver, can convince clients that your domain points somewhere it does not.

Here is what that looks like in practice:

  • Cache poisoning. An attacker floods a recursive resolver with forged answers for your domain. If one lands before the legitimate response, the resolver caches it and hands the bad IP to every client behind it until the TTL expires.
  • Traffic redirection. Users typing your domain get silently routed to a phishing page or a malicious server. Because the browser sees the expected hostname, the deception is hard to spot.
  • Credential and data theft. A spoofed login page that looks identical to yours harvests credentials, MFA codes, or session tokens.
  • Email interception. Forged MX records can divert mail, undermining anything that relies on email-based verification.

There is also a compliance angle. Several government and industry frameworks, including FedRAMP and various national CERT recommendations, expect DNSSEC on public zones. A zone without it can show up as a finding during audits.

Warning: DNSSEC protects the integrity and authenticity of DNS responses, not their confidentiality. Queries and answers are still sent in plaintext. If you also need privacy, look at DNS over HTTPS or DNS over TLS at the resolver level. The two are complementary, not interchangeable.


How to fix it

Enabling DNSSEC in Cloud DNS is a two-step process: turn on signing for the zone, then publish the DS record at your domain registrar so the parent zone points to your keys. Both steps are required. Signing without a published DS record gives you no validation chain.

Step 1: Enable DNSSEC on the zone

Using the gcloud CLI:

gcloud dns managed-zones update example-zone \
  --dnssec-state=on

You can confirm it took effect:

gcloud dns managed-zones describe example-zone \
  --format="value(dnssecConfig.state)"

The output should read on.

Step 2: Retrieve the DS record

Cloud DNS generates a key-signing key for the zone. You need its DS record to give to the registrar:

gcloud dns dns-keys list \
  --zone=example-zone \
  --format="json"

Find the key with "type": "keySigning" and note its id, then pull the DS record details:

gcloud dns dns-keys describe KEY_ID \
  --zone=example-zone \
  --format="value(ds_record())"

This returns the key tag, algorithm, digest type, and digest, which together form the DS record.

Step 3: Publish the DS record at your registrar

Log in to wherever your domain is registered (Google Domains successor, Cloudflare Registrar, GoDaddy, Namecheap, and so on) and add the DS record using the values from step 2. Each registrar's UI differs slightly, but they all ask for the same four fields: key tag, algorithm, digest type, and digest.

Danger: Do not delete or rotate DNSSEC keys while a DS record referencing them is still live at the registrar. If the parent's DS record no longer matches an active key in your zone, validating resolvers will treat the entire zone as bogus and your domain will go dark for those users. Always publish the new DS record and wait out the TTL before removing old keys.

Terraform example

If you manage Cloud DNS as code, set dnssec_config on the zone:

resource "google_dns_managed_zone" "example" {
  name     = "example-zone"
  dns_name = "example.com."

  dnssec_config {
    state = "on"
  }
}

Terraform handles the signing side, but it cannot publish the DS record at a third-party registrar. That step stays manual unless your registrar is also Terraform-managed.

Tip: After publishing the DS record, verify the full chain of trust with an external validator such as dnssec-analyzer.verisignlabs.com or by querying a validating resolver. A quick check: dig +dnssec example.com should return an ad (authenticated data) flag once the chain is complete.


How to prevent it from happening again

Fixing one zone by hand is fine. Making sure the next zone someone creates does not ship without DNSSEC is what actually moves the needle.

Enforce it in Terraform

If all your zones live in Terraform, a policy-as-code gate catches the gap before it reaches production. With OPA and Conftest:

package dns

deny[msg] {
  resource := input.resource.google_dns_managed_zone[name]
  resource.dnssec_config[_].state != "on"
  msg := sprintf("Cloud DNS zone '%s' must have DNSSEC enabled", [name])
}

Run it in CI against your plan output so a pull request that creates an unsigned zone fails the pipeline.

Audit existing zones across projects

Sweep every project for zones that slipped through:

for project in $(gcloud projects list --format="value(projectId)"); do
  gcloud dns managed-zones list --project="$project" \
    --format="table(name, dnssecConfig.state)" 2>/dev/null
done

Continuous monitoring

Manual sweeps drift out of date the moment someone creates a new zone. Lensix runs the dns_nodnsec check on a schedule across your connected GCP projects, so a newly created unsigned zone surfaces as a finding without anyone remembering to look.


Best practices

  • Enable DNSSEC on every public zone. Internal-only private zones that never leave your VPC gain little from it, but anything resolvable from the internet should be signed.
  • Keep DS records in sync with key rotation. If you rotate key-signing keys, follow a proper rollover (publish new DS, wait for propagation, then retire the old key). Cloud DNS handles zone-signing key rotation automatically, but KSK changes that affect the DS record need coordination with the registrar.
  • Watch your TTLs. A misstep with DNSSEC can persist for as long as the relevant TTL. Reasonable TTLs limit how long an error lingers and how long recovery takes.
  • Pair DNSSEC with the rest of your DNS hardening. CAA records to restrict certificate issuance, SPF, DKIM, and DMARC for email, and validating resolvers on your own infrastructure all stack with DNSSEC for a stronger posture.
  • Test before you trust. After any DNSSEC change, validate the chain externally rather than assuming the change worked. A broken chain fails closed, taking your domain offline for validating clients.

DNSSEC has a reputation for being fiddly, and the key management corner of it earns that. But the day-one act of turning it on for a Cloud DNS zone is two commands and a registrar form. For a public domain that real users and services depend on, that is a small price for removing an entire class of spoofing attacks.