DNSSEC and DNS-over-HTTPS: Hardening Your Resolution Infrastructure

Why DNS Security Matters More Than Ever

DNS is the phone book of the internet — and like a phone book, it was designed for convenience, not security. The original DNS protocol transmits queries and responses in cleartext over UDP, with no cryptographic verification that the answer you receive matches what the zone owner published. DNSSEC and DNS-over-HTTPS (DoH) address these two distinct problems: data integrity and query privacy. This guide covers deploying both in a production environment, from zone signing through recursive resolver hardening.

Understanding the Attack Surface

Before hardening, understand what you are hardening against:

  • Cache poisoning (Kaminsky attack): An attacker injects forged records into a resolver’s cache, redirecting users to malicious infrastructure. DNSSEC prevents this by signing records cryptographically.
  • DNS hijacking: Compromise of a resolver or registrar to modify authoritative records. DNSSEC allows clients to detect tampered records even if the resolver is compromised.
  • DNS surveillance: ISPs, network operators, and on-path attackers can log all DNS queries, revealing browsing patterns and internal hostname lookups. DoH and DNS-over-TLS (DoT) encrypt the transport layer.
  • DNS tunneling: Adversaries exfiltrate data or establish C2 channels by encoding data in DNS query names. Resolver-level monitoring and RPZ policies mitigate this.

DNSSEC: Signing Your Zones

DNSSEC adds cryptographic signatures to DNS records. When a resolver retrieves a signed record, it can verify the signature using the zone’s public key, which is itself authenticated via a chain of trust back to the DNS root.

The Key Hierarchy

DNSSEC uses two key types per zone:

  • Zone Signing Key (ZSK): Signs all records in the zone. Rotated frequently (every 30-90 days) to limit exposure.
  • Key Signing Key (KSK): Signs only the DNSKEY record set. Rotated less frequently (annually or as needed). The KSK’s fingerprint is registered as a DS record in the parent zone, establishing the chain of trust.

Signing a Zone with BIND

# Generate ZSK (algorithm 13 = ECDSAP256SHA256, recommended)
dnssec-keygen -a ECDSAP256SHA256 -n ZONE example-corp.com

# Generate KSK
dnssec-keygen -a ECDSAP256SHA256 -n ZONE -f KSK example-corp.com

# Sign the zone (validity 30 days, re-sign after 10 days)
dnssec-signzone -A -3 $(head -c 1000 /dev/urandom | sha1sum | cut -b 1-16) \
    -N increment -o example-corp.com -t \
    -e +2592000 -i 864000 \
    example-corp.com.zone \
    Kexample-corp.com.+013+*.key

# Output: example-corp.com.zone.signed (and dsset-example-corp.com. for parent submission)

The dsset-example-corp.com. file contains the DS records you must submit to your domain registrar or parent zone operator to complete the chain of trust. Without this step, DNSSEC validation will fail — resolvers will be unable to verify your zone’s signatures.

Automated Zone Signing with BIND Inline Signing

Manual zone signing is error-prone. BIND’s inline signing feature handles ZSK rotation automatically:

# named.conf zone declaration with inline signing
zone "example-corp.com" {
    type master;
    file "/var/named/example-corp.com.zone";
    inline-signing yes;
    auto-dnssec maintain;
    key-directory "/var/named/keys";
};

With auto-dnssec maintain, BIND monitors key timing metadata, automatically signs new records, and performs key rollovers according to the timing parameters embedded in the key files. Set the Inactive and Delete timing on each ZSK to drive automated rotation.

KSK Rollover: The High-Stakes Operation

KSK rollover is the most operationally sensitive DNSSEC procedure because it requires coordinating with your parent zone operator (typically your registrar). A failed rollover breaks DNSSEC validation for your entire domain.

The RFC 5011 automated rollover procedure:

  1. Publish new KSK: Add the new KSK to your DNSKEY record set. Both old and new KSK are now published. Wait for the TTL of the DNSKEY RRset to expire across the internet (typically 24-48 hours).
  2. Submit DS record to parent: Add the DS record for the new KSK to the parent zone via your registrar. Do NOT yet remove the old DS record. Wait for the DS TTL to expire.
  3. Retire old KSK: Remove the old KSK from the DNSKEY record set. Remove the old DS record from the parent zone. Wait for all caches to expire before declaring the rollover complete.

Total rollover time: 48-96 hours minimum. Never rush a KSK rollover — a premature removal of the old DS record while resolvers still cache it will cause validation failures.

DNSSEC Validation on Recursive Resolvers

Signing your zones is only half the equation. Your recursive resolvers must also be configured to validate DNSSEC signatures.

# Unbound resolver: enable DNSSEC validation
server:
    # Load the IANA root trust anchor
    auto-trust-anchor-file: "/var/lib/unbound/root.key"

    # Harden against cache poisoning
    harden-dnssec-stripped: yes
    harden-glue: yes
    harden-referral-path: yes

    # Return SERVFAIL for bogus (validation-failed) responses
    val-permissive-mode: no

With harden-dnssec-stripped: yes, Unbound will return SERVFAIL rather than an unsigned answer if a zone appears to have had its DNSSEC records stripped by a middlebox — a common attack vector against DNSSEC.

DNS-over-HTTPS: Encrypting the Transport

DNSSEC protects data integrity but does nothing for query privacy. DoH encrypts DNS queries inside HTTPS, making them indistinguishable from regular web traffic and preventing on-path surveillance.

Deploying a DoH Resolver with Unbound and a TLS Terminator

# nginx as DoH frontend (TLS termination)
server {
    listen 443 ssl http2;
    server_name dns.example-corp.com;

    ssl_certificate     /etc/letsencrypt/live/dns.example-corp.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/dns.example-corp.com/privkey.pem;
    ssl_protocols       TLSv1.3;
    ssl_ciphers         TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256;

    location /dns-query {
        proxy_pass         http://127.0.0.1:8053;
        proxy_set_header   Host $host;
        proxy_set_header   X-Real-IP $remote_addr;
        # Critical: DoH requires application/dns-message content type
        proxy_set_header   Accept "application/dns-message";
    }
}

# Unbound DoH backend
server:
    interface: 127.0.0.1@8053
    port: 8053
    do-daemonize: no
    # DoH uses HTTP/2 wire format
    http-endpoint: "/dns-query"
    http-max-streams: 100
    http-query-buffer-size: 4m
    http-response-buffer-size: 4m
    use-syslog: yes

Enforcing DoH for Internal Clients

Internal clients should be configured to use your self-hosted DoH resolver rather than public resolvers. This preserves your ability to enforce DNS-based security policies (RPZ, blocking) while providing transport encryption.

# macOS managed profile (mobileconfig) snippet
DNSSettings

    DNSProtocol
    HTTPS
    ServerURL
    https://dns.example-corp.com/dns-query

For Linux endpoints managed via configuration management, set the systemd-resolved DoH endpoint in /etc/systemd/resolved.conf and disable fallback to plain DNS with DNSOverTLS=yes.

DNS-over-TLS: The Simpler Alternative

DoT (RFC 7858) uses a dedicated TCP port (853) and is simpler to deploy than DoH but more easily blocked by network operators who want to force plain DNS. For enterprise internal use, DoT is often preferable because it is distinguishable from web traffic, making it easier to allow explicitly in firewall policy while blocking plain DNS on port 53.

# Unbound DoT configuration
server:
    interface: 0.0.0.0@853
    tls-service-key: /etc/unbound/tls/server.key
    tls-service-pem: /etc/unbound/tls/server.crt
    tls-port: 853
    # Enforce TLS 1.3 minimum
    tls-ciphers: "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256"

Response Policy Zones: DNS-Layer Security Controls

Once you control your recursive resolver, you can add Response Policy Zones (RPZ) to block malicious domains at the DNS layer — before any TCP connection is established. RPZ is the mechanism behind most enterprise DNS security products.

# BIND RPZ configuration
options {
    response-policy {
        zone "rpz.example-corp.com" policy NXDOMAIN;
    };
};

zone "rpz.example-corp.com" {
    type master;
    file "/var/named/rpz.example-corp.com.zone";
    allow-query { none; };
};

# rpz.example-corp.com.zone entries:
# Block known malware C2 domain
malware-c2.badactor.example.    IN  CNAME   .
# Redirect phishing domain to internal warning page
phishing.example.               IN  A       10.0.1.99

Maintain your RPZ data from threat intelligence feeds: abuse.ch URLhaus, Spamhaus, and commercial TI providers all publish RPZ-compatible blocklists. Automate the feed ingestion and zone reload to keep coverage current.

Monitoring and Alerting

A hardened resolver without monitoring is incomplete. Key metrics to collect:

  • DNSSEC validation failures: Alert on SERVFAIL responses caused by validation failure — these indicate either a zone misconfiguration or active tampering.
  • RPZ block rate: Trending increases in RPZ hits may indicate an internal host reaching out to known-bad infrastructure (malware C2, phishing).
  • Query volume by client: Anomalous query rates from a single internal IP are a DNS tunneling or reconnaissance indicator.
  • NX domain rate: A host generating high NXDOMAIN rates may be infected malware attempting to reach DGA (domain generation algorithm) C2 domains.

Conclusion

Hardening your DNS resolution infrastructure requires addressing two separate problems: data integrity (DNSSEC) and query privacy (DoH/DoT). Neither alone is sufficient. A fully hardened deployment signs your authoritative zones with DNSSEC, validates signatures on all recursive resolvers, encrypts resolver queries with DoH or DoT, and adds RPZ policies for DNS-layer threat blocking. Each layer independently adds measurable defensive value, and together they transform DNS from one of your most exploited attack surfaces into a hardened, monitored, and privacy-preserving component of your security architecture.

Scroll to Top