Skip to content

Last updated: 2026-02-11

BIND DNSSEC & DNS over TLS Configuration Guide

This guide provides recommended DNSSEC and DNS over TLS (DoT) settings for BIND (Berkeley Internet Name Domain). DNSSEC adds cryptographic signatures to DNS records, allowing resolvers to verify that responses have not been tampered with. DNS over TLS encrypts DNS queries in transit. This guide covers authoritative zone signing, resolver-side validation, and DoT configuration.

Prerequisites

Version note: RHEL 8 ships BIND 9.11, which does not support dnssec-policy (requires 9.16+). On BIND 9.11, use manual key generation with auto-dnssec maintain; and dnssec-enable yes; in the zone configuration instead. See the Manual Key Generation section below.

Example zone configuration for BIND 9.11:

zone "example.com" {
    type primary;
    file "/var/named/example.com.zone";
    auto-dnssec maintain;
    dnssec-enable yes;
    inline-signing yes;
    key-directory "/var/named/keys";
};

Important: dnssec-enable was removed in BIND 9.18+ (Debian 12, Ubuntu 24.04). Do not use it on 9.18 or later — it will cause a startup failure. It is only needed on BIND 9.11-9.13.

Path note: This guide uses RHEL/CentOS paths (/etc/named.conf, /var/named/). On Debian/Ubuntu, use /etc/bind/named.conf, /var/cache/bind/ for zone files, and /etc/bind/bind.keys instead of /etc/named.root.key. On SLES, config is at /etc/named.conf but zone files are under /var/lib/named/.

DNSSEC Overview

DNSSEC uses a chain of trust from the DNS root to your zone:

  1. ZSK (Zone Signing Key) -- Signs individual DNS records in the zone
  2. KSK (Key Signing Key) -- Signs the DNSKEY RRset and is referenced by the parent zone's DS record
  3. DS Record -- Published in the parent zone, linking the parent's chain of trust to your KSK
  4. RRSIG -- Cryptographic signatures over each RRset
  5. NSEC/NSEC3 -- Authenticated denial of existence

Authoritative Server Configuration

Using dnssec-policy (Recommended)

BIND 9.16+ supports dnssec-policy for fully automated key generation, signing, and rollover:

dnssec-policy "standard" {
    keys {
        ksk key-directory lifetime unlimited algorithm ecdsap256sha256;
        zsk key-directory lifetime P90D algorithm ecdsap256sha256;
    };

    dnskey-ttl 3600;
    publish-safety PT1H;
    retire-safety PT1H;
    purge-keys P90D;

    signatures-refresh P5D;
    signatures-validity P14D;
    signatures-validity-dnskey P14D;

    max-zone-ttl 86400;
    zone-propagation-delay PT5M;
    parent-ds-ttl 3600;
    parent-propagation-delay PT1H;

    nsec3param iterations 0 optout no salt-length 0;
};

Apply the policy to a zone:

zone "example.com" {
    type primary;
    file "/var/named/example.com.zone";
    dnssec-policy "standard";
    inline-signing yes;
    key-directory "/var/named/keys";
};

Algorithm Selection

Use ECDSAP256SHA256 (algorithm 13) for new deployments. It provides strong security with small key and signature sizes:

Algorithm ID Recommendation
RSASHA256 8 Acceptable (use 2048-bit minimum)
RSASHA512 10 Acceptable
ECDSAP256SHA256 13 Recommended
ECDSAP384SHA384 14 Acceptable
ED25519 15 Good (limited resolver support)

Automatic Key Rollover

The dnssec-policy handles key rollover automatically based on the configured lifetimes. The ZSK lifetime of P90D (90 days) triggers automatic ZSK rollovers. The KSK lifetime of unlimited means KSK rollovers must be initiated manually.

Monitor key states:

rndc dnssec -status example.com     # BIND 9.18+; on 9.16, use: rndc signing -list example.com

Manual Key Generation

If not using dnssec-policy, generate keys manually:

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

# Generate ZSK
dnssec-keygen -a ECDSAP256SHA256 example.com

Include the keys in your zone file:

$INCLUDE Kexample.com.+013+12345.key
$INCLUDE Kexample.com.+013+67890.key

Sign the zone:

dnssec-signzone -A -3 $(head -c 16 /dev/urandom | od -A n -t x1 | tr -d ' \n') \
  -N INCREMENT -o example.com -t example.com.zone

DS Record Publication

After signing your zone, extract the DS record and publish it at your registrar or parent zone:

dnssec-dsfromkey /var/named/keys/Kexample.com.+013+12345.key

This outputs DS records in multiple digest formats:

example.com. IN DS 12345 13 2 ABCDEF1234567890...

Publish the SHA-256 (digest type 2) DS record with your domain registrar.

Resolver Configuration (DNSSEC Validation)

Configure BIND as a validating resolver:

options {
    directory "/var/named";

    // Enable DNSSEC validation
    dnssec-validation auto;

    // Use the built-in root trust anchors
    // (managed-keys-directory for RFC 5011 auto-updates)
    managed-keys-directory "/var/named/dynamic";
    bindkeys-file "/etc/named.root.key";

    recursion yes;
    allow-recursion { localnets; localhost; };
};

The dnssec-validation auto; setting uses the built-in root zone trust anchor and automatically validates DNSSEC-signed zones.

Trust Anchors

BIND ships with the root zone trust anchor. For custom trust anchors:

trust-anchors {
    example.com. initial-key 257 3 13 "base64-encoded-key-data";
};

Complete Configuration

Authoritative Server

// /etc/named.conf

options {
    directory "/var/named";
    listen-on { any; };
    listen-on-v6 { any; };

    allow-transfer { none; };
    allow-query { any; };

    recursion no;

    // Key directory for DNSSEC keys
    key-directory "/var/named/keys";
};

// DNSSEC signing policy
dnssec-policy "standard" {
    keys {
        ksk key-directory lifetime unlimited algorithm ecdsap256sha256;
        zsk key-directory lifetime P90D algorithm ecdsap256sha256;
    };

    dnskey-ttl 3600;
    publish-safety PT1H;
    retire-safety PT1H;
    purge-keys P90D;

    signatures-refresh P5D;
    signatures-validity P14D;
    signatures-validity-dnskey P14D;

    max-zone-ttl 86400;
    zone-propagation-delay PT5M;
    parent-ds-ttl 3600;
    parent-propagation-delay PT1H;

    nsec3param iterations 0 optout no salt-length 0;
};

// Signed zone
zone "example.com" {
    type primary;
    file "/var/named/example.com.zone";
    dnssec-policy "standard";
    inline-signing yes;
    key-directory "/var/named/keys";
};

Validating Resolver

// /etc/named.conf (resolver)

options {
    directory "/var/named";
    listen-on { 127.0.0.1; 10.0.0.1; };

    recursion yes;
    allow-recursion { localnets; localhost; };

    dnssec-validation auto;
    managed-keys-directory "/var/named/dynamic";
    bindkeys-file "/etc/named.root.key";

    // Rate limiting
    rate-limit {
        responses-per-second 10;
    };

    // Minimal responses
    minimal-responses yes;
};

zone "." {
    type hint;
    file "named.ca";
};

DNS over TLS (DoT)

BIND 9.18 and later supports DNS over TLS for both serving encrypted queries and forwarding to upstream resolvers over TLS.

Version note: DoT support requires BIND 9.18+. The tls configuration block used below is not available in earlier versions. RHEL 9 and SLES 15 ship BIND 9.16, which does not support DoT natively. On those systems, consider using a TLS-terminating proxy such as stunnel or dnsdist for encrypted DNS transport.

TLS Profile

Define a TLS profile with your certificate and key:

tls local-tls {
    cert-file "/etc/named/ssl/fullchain.pem";
    key-file "/etc/named/ssl/privkey.pem";
    protocols { TLSv1.2; TLSv1.3; };
    ciphers "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305";
    prefer-server-ciphers yes;
};

Listening for DoT

Accept DNS over TLS queries on port 853:

options {
    listen-on port 853 tls local-tls { any; };
    listen-on-v6 port 853 tls local-tls { any; };
};

Forwarding over TLS

Configure a TLS profile for upstream resolvers and forward queries over TLS:

tls upstream-tls {
    ca-file "/etc/pki/tls/certs/ca-bundle.crt";   // Debian/Ubuntu: /etc/ssl/certs/ca-certificates.crt
    remote-hostname "dns.quad9.net";
};

options {
    forwarders port 853 tls upstream-tls {
        9.9.9.9;
        149.112.112.112;
    };
    forward only;
};

The remote-hostname option verifies the upstream server's TLS certificate hostname.

Complete DoT Server Configuration

// /etc/named.conf (DoT-enabled authoritative server)

tls local-tls {
    cert-file "/etc/named/ssl/fullchain.pem";
    key-file "/etc/named/ssl/privkey.pem";
    protocols { TLSv1.2; TLSv1.3; };
    prefer-server-ciphers yes;
};

options {
    directory "/var/named";

    // Plain DNS
    listen-on { any; };
    listen-on-v6 { any; };

    // DNS over TLS
    listen-on port 853 tls local-tls { any; };
    listen-on-v6 port 853 tls local-tls { any; };

    allow-transfer { none; };
    allow-query { any; };
    recursion no;

    key-directory "/var/named/keys";
};

dnssec-policy "standard" {
    keys {
        ksk key-directory lifetime unlimited algorithm ecdsap256sha256;
        zsk key-directory lifetime P90D algorithm ecdsap256sha256;
    };
    dnskey-ttl 3600;
    signatures-refresh P5D;
    signatures-validity P14D;
    signatures-validity-dnskey P14D;
    max-zone-ttl 86400;
    nsec3param iterations 0 optout no salt-length 0;
};

zone "example.com" {
    type primary;
    file "/var/named/example.com.zone";
    dnssec-policy "standard";
    inline-signing yes;
    key-directory "/var/named/keys";
};

DoT Verification

Test DNS over TLS with kdig:

kdig @ns.example.com +tls example.com A

Or with OpenSSL:

echo | openssl s_client -connect ns.example.com:853 2>/dev/null | grep -E 'Protocol|Cipher'

Firewall

Open port 853 for DNS over TLS:

firewall-cmd --permanent --add-port=853/tcp
firewall-cmd --reload

Verification

Check that a zone is signed:

dig @localhost example.com DNSKEY +dnssec +multiline

Verify RRSIG records exist:

dig @localhost example.com A +dnssec

Look for the ad (Authenticated Data) flag in responses:

dig @resolver example.com A +dnssec

Use delv for detailed DNSSEC validation:

delv @localhost example.com A +rtrace

Verify the DS record matches:

dig example.com DS +short

Check DNSSEC chain of trust:

drill -S example.com

Check the DNSSEC signing status in BIND:

rndc dnssec -status example.com     # BIND 9.18+; on 9.16, use: rndc signing -list example.com
rndc zonestatus example.com