Skip to main content

Getting started with DNSSEC

DNSSEC (DNS Security Extensions) adds a layer of authentication to DNS by validating digital signatures across a chain of trust, from the DNS root to the specific record being requested. It protects against cache poisoning attacks, where an attacker redirects users to a malicious site even when a valid web address is entered.

DNSSEC does not encrypt traffic between the end user and the resolver. For confidentiality and integrity at that layer, use DNS over HTTPS (DoH) or DNS over TLS (DoT).

Enable DNSSEC

  1. In the CDB Technical Web Portal, navigate to DNS > All zones and open the zone, or create one (see manage a DNS zone).

  2. Enable advanced interface mode if not already active.

  3. Turn on the DNSSEC toggle in the zone header.

Zone settings header showing the DNSSEC toggle set to OFF and the Interface mode toggle set to Advanced
  1. In the confirmation dialog, click Yes, enable.
Confirmation dialog asking whether to enable DNSSEC with Yes, enable and Cancel buttons
  1. The DNSSEC Configuration dialog opens and shows the DS record details for the zone — copy the value in the DS record field.
DNSSEC Configuration dialog showing Key tag, Algorithm, Digest type, Digest, DS record, and Public key fields with copy buttons
  1. Paste the DS record value into your domain registrar's control panel.

If you are transferring a DNS zone from another provider, disable DNSSEC at the old provider first and wait for the DS record TTL to expire before enabling it here.

DS propagation can take up to an hour at most registrars, and up to 24 hours in some cases. The DNSSEC toggle remains OFF until CDB confirms the DS record at your registrar — once confirmed, the toggle switches to ON and the zone's DNSSEC status changes to active.

To view the DS record again after closing the dialog, click the DNSSEC toggle — the confirmation and DNSSEC Configuration dialogs reopen.

DNSSEC status lifecycle

After enabling DNSSEC, the zone moves through a lifecycle driven by what CDB observes at your domain registrar — not just by your request. CDB periodically scans the registrar for the DS record and updates the zone's DNSSEC status accordingly.

StatusMeaningAction required
pendingDNSSEC is enabled, signing keys are generated, but CDB has not yet detected the DS record at the registrar.Add the DS record CDB provided to your registrar.
activeA valid DS record is detected at the registrar. The chain of trust is established.None — DNSSEC is working.
pending-disabledDisable was requested, but a DS record is still published at the registrar.Remove the DS record at the registrar to complete the process.
disabledDNSSEC is off and no DS record is present.None.

The transition from pending to active can take from a few minutes to several hours, depending on your registrar's propagation speed and the DS record's TTL. Once active, the status remains active — removing the DS record at the registrar does not automatically change it.

Disable DNSSEC

Remove the DS record from your registrar before disabling DNSSEC in CDB. If DNSSEC is disabled in CDB while the DS record is still published, resolvers will try to validate signatures that no longer exist, causing DNS resolution failures.

  1. Remove the DS record from your domain registrar's control panel.

  2. Wait for the DS record TTL to expire so cached records clear.

  3. In the Customer Portal, turn off the DNSSEC toggle for the zone.

The DNS API supports enabling and disabling DNSSEC, retrieving DS records, checking zone status, and verifying DS propagation at the registrar. The Python and Go SDKs cover enable, get DS record, and disable operations.

An API token is required. Set these environment variables before running the examples:

export GCORE_API_KEY="{YOUR_API_KEY}"
export ZONE_NAME="{YOUR_ZONE_NAME}"

Enable DNSSEC

Enabling DNSSEC generates signing keys for the zone and returns the DS (Delegation Signer) record — the ds field in the response contains the complete DS record string to add at your registrar.

import os
import gcore

client = gcore.CDB(api_key=os.environ["GCORE_API_KEY"])

result = client.dns.zones.dnssec.update(
    os.environ["ZONE_NAME"],
    enabled=True,
)

print(f"DS record: {result.ds}")
print(f"Key tag:   {result.key_tag}")
print(f"Algorithm: {result.algorithm}")
package main

import (
    "context"
    "fmt"
    "os"

    "github.com/G-Core/gcore-go"
    "github.com/G-Core/gcore-go/dns"
    "github.com/G-Core/gcore-go/option"
)

func main() {
    client := gcore.NewClient(option.WithAPIKey(os.Getenv("GCORE_API_KEY")))

    result, err := client.DNS.Zones.Dnssec.Update(
        context.Background(),
        os.Getenv("ZONE_NAME"),
        dns.ZoneDnssecUpdateParams{Enabled: gcore.Bool(true)},
    )
    if err != nil {
        fmt.Fprintf(os.Stderr, "enable DNSSEC: %v\n", err)
        os.Exit(1)
    }

    fmt.Printf("DS record: %s\n", result.Ds)
    fmt.Printf("Key tag:   %d\n", result.KeyTag)
    fmt.Printf("Algorithm: %s\n", result.Algorithm)
}
curl -X PATCH "https://api.cdb-staging.cdn.orange.com/dns/v2/zones/${ZONE_NAME}/dnssec" \
  -H "Authorization: APIKey ${GCORE_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"enabled": true}'

Copy the ds field value from the response and add it to your registrar's control panel. The zone enters the pending state until CDB detects the DS record at your registrar, which can take from a few minutes to several hours.

Get DS record

To retrieve the DS record for a zone that already has DNSSEC enabled:

import os
import gcore

client = gcore.CDB(api_key=os.environ["GCORE_API_KEY"])

ds_info = client.dns.zones.dnssec.get(os.environ["ZONE_NAME"])

print(f"DS record: {ds_info.ds}")
print(f"Key tag:   {ds_info.key_tag}")
print(f"Algorithm: {ds_info.algorithm}")
package main

import (
    "context"
    "fmt"
    "os"

    "github.com/G-Core/gcore-go"
    "github.com/G-Core/gcore-go/option"
)

func main() {
    client := gcore.NewClient(option.WithAPIKey(os.Getenv("GCORE_API_KEY")))

    dsInfo, err := client.DNS.Zones.Dnssec.Get(
        context.Background(),
        os.Getenv("ZONE_NAME"),
    )
    if err != nil {
        fmt.Fprintf(os.Stderr, "get DS record: %v\n", err)
        os.Exit(1)
    }

    fmt.Printf("DS record: %s\n", dsInfo.Ds)
    fmt.Printf("Key tag:   %d\n", dsInfo.KeyTag)
    fmt.Printf("Algorithm: %s\n", dsInfo.Algorithm)
}
curl "https://api.cdb-staging.cdn.orange.com/dns/v2/zones/${ZONE_NAME}/dnssec" \
  -H "Authorization: APIKey ${GCORE_API_KEY}"

DNSSEC status lifecycle

The zone response includes two fields that reflect the current DNSSEC state, updated by CDB's periodic scan of your registrar:

  • dnssec_status — the current lifecycle state: pending, active, pending-disabled, or disabled
  • dnssec_status_modified_on — RFC 3339 timestamp of the last status change

Both fields are absent from the response if DNSSEC has never been enabled on the zone. A missing field is not an error — it means the zone has no DNSSEC history.

StatusMeaning
pendingDNSSEC enabled, keys generated, DS record not yet detected at the registrar
activeDS record detected at the registrar — chain of trust established
pending-disabledDisable requested, DS record still present at the registrar
disabledDNSSEC off, no DS record present

The transition from pending to active can take from a few minutes to several hours, depending on your registrar's propagation speed and the DS record's TTL. Once active, the status remains active — removing the DS record at the registrar does not automatically change it. To turn DNSSEC off, use the disable endpoint.

To check the current status, fetch the zone and inspect the dnssec_status field:

curl "https://api.cdb-staging.cdn.orange.com/dns/v2/zones/${ZONE_NAME}" \
  -H "Authorization: APIKey ${GCORE_API_KEY}"

Example responses for each state:

pending — keys generated, DS record not yet at registrar:

{
  "name": "example.com",
  "enabled": true,
  "status": "active",
  "dnssec_enabled": true,
  "dnssec_status": "pending",
  "dnssec_status_modified_on": "2026-06-25T09:14:02Z"
}

active — DS record detected, chain of trust established:

{
  "name": "example.com",
  "enabled": true,
  "status": "active",
  "dnssec_enabled": true,
  "dnssec_status": "active",
  "dnssec_status_modified_on": "2026-06-25T11:48:37Z"
}

pending-disabled — disable requested, DS record still at registrar:

{
  "name": "example.com",
  "enabled": true,
  "status": "active",
  "dnssec_enabled": false,
  "dnssec_status": "pending-disabled",
  "dnssec_status_modified_on": "2026-06-26T08:02:15Z"
}

disabled — DNSSEC off, no DS record present:

{
  "name": "example.com",
  "enabled": true,
  "status": "active",
  "dnssec_enabled": false,
  "dnssec_status": "disabled",
  "dnssec_status_modified_on": "2026-06-27T10:30:00Z"
}

dnssec_status is distinct from the top-level status field — status reflects zone delegation to CDB nameservers, while dnssec_status reflects the DNSSEC lifecycle.

Check DS record at registrar

To verify whether a valid DS record is currently detected at the registrar, use the parent-ds endpoint:

curl "https://api.cdb-staging.cdn.orange.com/dns/v2/zones/${ZONE_NAME}/dnssec/parent-ds" \
  -H "Authorization: APIKey ${GCORE_API_KEY}"

The response shows whether the DS record is detected and which parent zone was checked:

{
  "has_valid_parent_ds": true,
  "parent_zone": "com."
}

Disable DNSSEC

Send an update with enabled: false to disable DNSSEC. This is safe to call once the DS record is removed from the registrar and its TTL has expired.

import os
import gcore

client = gcore.CDB(api_key=os.environ["GCORE_API_KEY"])

client.dns.zones.dnssec.update(
    os.environ["ZONE_NAME"],
    enabled=False,
)
print("DNSSEC disabled.")
package main

import (
    "context"
    "fmt"
    "os"

    "github.com/G-Core/gcore-go"
    "github.com/G-Core/gcore-go/dns"
    "github.com/G-Core/gcore-go/option"
)

func main() {
    client := gcore.NewClient(option.WithAPIKey(os.Getenv("GCORE_API_KEY")))

    _, err := client.DNS.Zones.Dnssec.Update(
        context.Background(),
        os.Getenv("ZONE_NAME"),
        dns.ZoneDnssecUpdateParams{Enabled: gcore.Bool(false)},
    )
    if err != nil {
        fmt.Fprintf(os.Stderr, "disable DNSSEC: %v\n", err)
        os.Exit(1)
    }

    fmt.Println("DNSSEC disabled.")
}
curl -X PATCH "https://api.cdb-staging.cdn.orange.com/dns/v2/zones/${ZONE_NAME}/dnssec" \
  -H "Authorization: APIKey ${GCORE_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"enabled": false}'

If a DS record is still published at the registrar when the request is sent, the API returns 409 Conflict. This protects against breaking DNS validation — resolvers reject responses if the DS record points to keys that no longer exist.

To proceed despite the active DS record, pass force_disable: true in the request body. The zone moves to pending-disabled immediately and transitions to disabled once the DS record is removed at the registrar:

curl -X PATCH "https://api.cdb-staging.cdn.orange.com/dns/v2/zones/${ZONE_NAME}/dnssec" \
  -H "Authorization: APIKey ${GCORE_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"enabled": false, "force_disable": true}'

After disabling, wait for the DS record TTL to expire before considering the transition complete — cached records at upstream resolvers remain valid until the TTL passes.