Signing Your DNS Zones with DNSSEC on BIND Authoritative Servers

Signing Your DNS Zones with DNSSEC on BIND Authoritative Servers

After configuring DNSSEC validation on your resolvers, the next step is signing your own authoritative zones. This post covers the complete process of implementing DNSSEC on BIND authoritative servers, from key generation to zone signing and key rollovers.

Understanding DNSSEC Keys

DNSSEC uses two types of cryptographic keys:

Key Signing Key (KSK)

  • Signs the DNSKEY RRset (the zone's public keys)
  • Longer lifetime (typically 1-2 years)
  • Larger key size for stronger security
  • Its hash (DS record) is published in the parent zone

Zone Signing Key (ZSK)

  • Signs all other records in the zone
  • Shorter lifetime (typically 1-3 months)
  • Smaller key size for performance
  • Rolled more frequently

Choosing DNSSEC Algorithms

Modern BIND supports several algorithms:

Algorithm Number Recommendation
RSASHA256 8 Good, widely supported
RSASHA512 10 Good, wider margins
ECDSAP256SHA256 13 Recommended, smaller keys
ECDSAP384SHA384 14 Strong, larger signatures
ED25519 15 Best, smallest signatures

For new deployments, use ECDSAP256SHA256 (13) or ED25519 (15) for best performance and security.

Manual DNSSEC Configuration

Generating Keys Manually

Create the key directory:

mkdir -p /var/cache/bind/keys
chown bind:bind /var/cache/bind/keys
chmod 700 /var/cache/bind/keys

Generate the KSK:

cd /var/cache/bind/keys

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

# Or with RSA (2048-bit minimum, 4096 recommended for KSK)
dnssec-keygen -a RSASHA256 -b 4096 -f KSK -n ZONE example.com

Generate the ZSK:

# Generate ZSK with ECDSAP256SHA256
dnssec-keygen -a ECDSAP256SHA256 -n ZONE example.com

# Or with RSA (2048-bit)
dnssec-keygen -a RSASHA256 -b 2048 -n ZONE example.com

This creates four files per key:

  • Kexample.com.+013+12345.key - Public key (DNSKEY record)
  • Kexample.com.+013+12345.private - Private key

Manual Zone Signing

Include keys in your zone file:

; /var/cache/bind/zones/db.example.com
$TTL 86400
$INCLUDE /var/cache/bind/keys/Kexample.com.+013+12345.key
$INCLUDE /var/cache/bind/keys/Kexample.com.+013+67890.key

@   IN  SOA ns1.example.com. admin.example.com. (
        2024011001  ; Serial
        3600        ; Refresh
        1800        ; Retry
        604800      ; Expire
        86400 )     ; Minimum TTL

    IN  NS  ns1.example.com.
    IN  NS  ns2.example.com.
    IN  MX  10 mail.example.com.
    IN  A   192.168.1.10

ns1 IN  A   192.168.1.1
ns2 IN  A   192.168.1.2
mail IN A   192.168.1.20
www IN  A   192.168.1.10

Sign the zone:

# Sign zone with automatic NSEC3
dnssec-signzone -A -3 $(head -c 16 /dev/urandom | xxd -p) \
    -N INCREMENT -o example.com -t \
    -K /var/cache/bind/keys \
    /var/cache/bind/zones/db.example.com

This creates db.example.com.signed containing:

  • RRSIG records (signatures)
  • NSEC3 records (authenticated denial of existence)
  • NSEC3PARAM record

Automated DNSSEC with dnssec-policy

BIND 9.16+ supports automatic key management with dnssec-policy. This is the recommended approach:

Define DNSSEC Policies

// /etc/bind/named.conf.options

dnssec-policy "standard" {
    // Key algorithm and parameters
    keys {
        // KSK: ECDSA P-256, unlimited lifetime (manual rollover)
        ksk key-directory lifetime unlimited algorithm ecdsap256sha256;
        // ZSK: ECDSA P-256, 90-day lifetime (auto rollover)
        zsk key-directory lifetime 90d algorithm ecdsap256sha256;
    };

    // Timing parameters
    dnskey-ttl 3600;              // TTL for DNSKEY records
    publish-safety 1h;             // Safety margin for publishing
    retire-safety 1h;              // Safety margin for retiring
    purge-keys 90d;                // Remove old keys after 90 days

    // Signature parameters
    signatures-refresh 5d;         // Re-sign 5 days before expiry
    signatures-validity 14d;       // Signatures valid for 14 days
    signatures-validity-dnskey 14d;

    // NSEC3 parameters (authenticated denial)
    nsec3param iterations 0 optout no salt-length 0;

    // Key storage
    key-directory "/var/cache/bind/keys";
};

// More conservative policy for critical zones
dnssec-policy "critical" {
    keys {
        ksk key-directory lifetime unlimited algorithm ecdsap384sha384;
        zsk key-directory lifetime 30d algorithm ecdsap384sha384;
    };
    dnskey-ttl 1800;
    signatures-refresh 3d;
    signatures-validity 7d;
    signatures-validity-dnskey 7d;
    nsec3param iterations 0 optout no salt-length 0;
    key-directory "/var/cache/bind/keys";
};

// Default policy (built-in)
// Uses ECDSAP256SHA256, 3-year KSK, 13-month ZSK

Apply Policy to Zones

// /etc/bind/named.conf.local

zone "example.com" {
    type primary;
    file "/var/cache/bind/zones/db.example.com";
    dnssec-policy "standard";
    inline-signing yes;  // Sign dynamically, keep unsigned source
    key-directory "/var/cache/bind/keys";
};

zone "critical.example.com" {
    type primary;
    file "/var/cache/bind/zones/db.critical.example.com";
    dnssec-policy "critical";
    inline-signing yes;
    key-directory "/var/cache/bind/keys";
};

Inline Signing

With inline-signing yes, BIND:

  • Keeps your original zone file unchanged
  • Creates a signed version automatically
  • Re-signs when signatures approach expiry
  • Handles key rollovers automatically

Publishing the DS Record

Once your zone is signed, you must publish the DS record in the parent zone to complete the chain of trust.

Generate DS Record

# Generate DS records from your KSK
dnssec-dsfromkey -2 /var/cache/bind/keys/Kexample.com.+013+12345.key

# Output:
# example.com. IN DS 12345 13 2 E2D3C916F6DED0ED3D9A8E6C1C0A2ED7...

The DS record contains:

  • Key tag (12345)
  • Algorithm (13 = ECDSAP256SHA256)
  • Digest type (2 = SHA-256)
  • Digest (hash of the KSK)

Submit DS Record to Registrar

For domains registered with a registrar:

  1. Log into your registrar's control panel
  2. Find DNS/DNSSEC settings
  3. Add the DS record with:
    • Key Tag: 12345
    • Algorithm: 13
    • Digest Type: 2
    • Digest: (the hash value)

For subzones under your control, add the DS record to the parent zone:

; In parent zone
subdomain IN DS 12345 13 2 E2D3C916F6DED0ED3D9A8E6C1C0A2ED7...

NSEC vs NSEC3

DNSSEC provides authenticated denial of existence through NSEC or NSEC3 records.

NSEC (Original)

// Enable NSEC (default)
dnssec-policy "nsec-policy" {
    keys {
        ksk lifetime unlimited algorithm ecdsap256sha256;
        zsk lifetime 90d algorithm ecdsap256sha256;
    };
    // No nsec3param = use NSEC
};

Pros: Simpler, smaller responses Cons: Allows zone enumeration (walking)

// Enable NSEC3
dnssec-policy "nsec3-policy" {
    keys {
        ksk lifetime unlimited algorithm ecdsap256sha256;
        zsk lifetime 90d algorithm ecdsap256sha256;
    };
    // NSEC3 parameters
    // iterations: 0 recommended (RFC 9276)
    // optout: no (sign all records)
    // salt-length: 0 recommended (RFC 9276)
    nsec3param iterations 0 optout no salt-length 0;
};

Pros: Prevents zone enumeration Cons: Slightly larger responses, more CPU

Key Rollover Procedures

Automatic ZSK Rollover

With dnssec-policy, ZSK rollovers happen automatically. Monitor with:

# Check key states
rndc dnssec -status example.com

# View upcoming rollovers
rndc signing -list example.com

Manual KSK Rollover (Double-DS Method)

KSK rollovers require coordination with the parent zone:

Step 1: Generate new KSK

# Generate successor KSK
rndc dnssec -rollover -key 12345 example.com

# Or manually
dnssec-keygen -a ECDSAP256SHA256 -f KSK -n ZONE example.com

Step 2: Add new DS to parent

# Generate DS for new key
dnssec-dsfromkey /var/cache/bind/keys/Kexample.com.+013+NEW_ID.key

# Submit new DS to registrar (keep old DS)

Step 3: Wait for propagation

Wait at least 2x the TTL of the DS record in the parent zone.

Step 4: Complete rollover

# Retire old KSK
rndc dnssec -checkds -key 12345 published example.com

# After TTL expires, remove old DS from parent

Step 5: Remove old DS from parent

Remove the old DS record from your registrar.

Monitoring DNSSEC Health

Check Zone Signatures

#!/bin/bash
# check-dnssec.sh - Monitor DNSSEC signature expiry

ZONE="example.com"
WARN_DAYS=7

# Get signature expiry from zone
EXPIRY=$(dig +dnssec @localhost $ZONE SOA | \
    grep RRSIG | \
    awk '{print $9}' | \
    head -1)

if [ -n "$EXPIRY" ]; then
    # Parse DNSSEC timestamp (YYYYMMDDHHMMSS)
    EXPIRY_EPOCH=$(date -d "${EXPIRY:0:8} ${EXPIRY:8:2}:${EXPIRY:10:2}:${EXPIRY:12:2}" +%s 2>/dev/null)
    NOW_EPOCH=$(date +%s)
    DAYS_LEFT=$(( (EXPIRY_EPOCH - NOW_EPOCH) / 86400 ))
    
    echo "Zone: $ZONE"
    echo "Signature expires: $EXPIRY"
    echo "Days remaining: $DAYS_LEFT"
    
    if [ $DAYS_LEFT -lt $WARN_DAYS ]; then
        echo "WARNING: Signatures expiring soon!"
        exit 1
    fi
else
    echo "ERROR: Could not retrieve DNSSEC signatures for $ZONE"
    exit 2
fi

Verify Chain of Trust

# Full chain verification
delv @8.8.8.8 example.com SOA +rtrace

# Check DS record matches
dig DS example.com @parent-ns.tld
dnssec-dsfromkey /var/cache/bind/keys/Kexample.com.+013+12345.key

# Verify signatures
dnssec-verify -o example.com /var/cache/bind/zones/db.example.com.signed

Online Validation Tools

Use these services to verify your DNSSEC deployment:

Complete Configuration Example

Here's a production-ready DNSSEC configuration:

// /etc/bind/named.conf.options

options {
    directory "/var/cache/bind";
    
    // DNSSEC for authoritative responses
    dnssec-validation auto;
    
    // Key and managed-keys directory
    key-directory "/var/cache/bind/keys";
    managed-keys-directory "/var/cache/bind/managed-keys";
    
    // Logging
    querylog yes;
};

// Standard DNSSEC policy
dnssec-policy "production" {
    keys {
        ksk key-directory lifetime unlimited algorithm ecdsap256sha256;
        zsk key-directory lifetime P90D algorithm ecdsap256sha256;
    };
    
    // Timing
    dnskey-ttl PT1H;
    publish-safety PT1H;
    retire-safety PT1H;
    purge-keys P90D;
    
    // Signatures
    signatures-refresh P5D;
    signatures-validity P14D;
    signatures-validity-dnskey P14D;
    
    // NSEC3 (RFC 9276 recommendations)
    nsec3param iterations 0 optout no salt-length 0;
};

logging {
    channel dnssec_log {
        file "/var/log/named/dnssec.log" versions 3 size 10m;
        severity info;
        print-time yes;
        print-severity yes;
    };
    category dnssec { dnssec_log; };
};
// /etc/bind/named.conf.local

zone "example.com" {
    type primary;
    file "/var/cache/bind/zones/db.example.com";
    dnssec-policy "production";
    inline-signing yes;
    
    // Allow zone transfers to secondaries
    allow-transfer { key "transfer-key"; };
    also-notify { 192.168.1.2; };
};

zone "1.168.192.in-addr.arpa" {
    type primary;
    file "/var/cache/bind/zones/db.192.168.1";
    dnssec-policy "production";
    inline-signing yes;
    allow-transfer { key "transfer-key"; };
};

Troubleshooting DNSSEC

Common Issues

Signatures expired:

# Check signature validity
rndc signing -list example.com

# Force re-signing
rndc sign example.com

DS record mismatch:

# Compare DS records
dig DS example.com @parent-ns
dnssec-dsfromkey Kexample.com.+013+12345.key

# Ensure key tag, algorithm, and digest match

Key not found:

# Check key files exist and permissions
ls -la /var/cache/bind/keys/
chown bind:bind /var/cache/bind/keys/*
chmod 600 /var/cache/bind/keys/*.private

Zone won't load:

# Check configuration
named-checkconf
named-checkzone example.com /var/cache/bind/zones/db.example.com

# Check logs
tail -f /var/log/named/dnssec.log

Security Best Practices

  1. Protect private keys - Use proper file permissions (600) and consider HSM for high-security deployments
  2. Monitor signature expiry - Set up alerts for signatures approaching expiry
  3. Test rollovers - Practice key rollovers in a test environment first
  4. Use NSEC3 - Prevents zone enumeration attacks
  5. Keep BIND updated - Security fixes often affect DNSSEC
  6. Document procedures - Especially for manual KSK rollovers
  7. Backup keys securely - Store encrypted backups offline

Conclusion

DNSSEC on authoritative servers completes the chain of trust for your domains. Using dnssec-policy with inline signing automates most of the complexity, while proper monitoring ensures your zones remain securely signed.

Key takeaways:

  • Use ECDSAP256SHA256 or ED25519 algorithms
  • Enable inline-signing for automatic signature maintenance
  • Use NSEC3 to prevent zone enumeration
  • Monitor signature expiry proactively
  • Plan and test key rollovers carefully

The next post will cover DNS over TLS (DoT), adding transport encryption to your DNS infrastructure for privacy and security.

Read more

HAProxy Monitoring with Prometheus: Complete Observability Guide

HAProxy Monitoring with Prometheus: Complete Observability Guide

Monitoring HAProxy is essential for maintaining reliable load balancing infrastructure. Prometheus provides powerful metrics collection, alerting capabilities, and seamless Grafana integration for visualizing HAProxy performance and health. Why Prometheus for HAProxy? Prometheus offers: * Pull-based metrics - Prometheus scrapes HAProxy metrics endpoints * Time-series database - Store historical data for trend analysis

By Patrick de Ruiter