Introduction
DNSSEC (DNS Security Extensions) adds cryptographic signatures to DNS records, creating a chain of trust from root servers to your domain. When this chain breaks - due to missing signatures, expired keys, or misconfiguration - validating resolvers return SERVFAIL instead of DNS answers. DNSSEC failures are particularly difficult because the error doesn't indicate where the chain broke, and fixing issues requires understanding both DNS and cryptographic key management.
Symptoms
- DNS queries return SERVFAIL for specific domains
- Domain works with +cd flag (checking disabled)
- Some resolvers return answers while others fail
- Error messages: "DNSSEC validation failed" or "BOGUS"
- Domain recently enabled DNSSEC and stopped resolving
- DNSSEC rollover caused resolution failures
- DNSviz or similar tools show broken chain of trust
Common Causes
- DNSKEY missing or mismatched with DS at parent
- DS record at parent doesn't match zone DNSKEY
- Expired RRSIG signatures (not renewed)
- Missing RRSIG for records in signed zone
- Zone not properly signed after record changes
- Key rollover incomplete or incorrect
- Trust anchor issues on validating resolver
- Algorithm mismatch between DS and DNSKEY
Step-by-Step Fix
- 1.Confirm DNSSEC is the problem by disabling validation.
```bash # Test with DNSSEC checking disabled dig example.com +cd
# +cd = checking disabled # If this returns answers but normal query fails, DNSSEC is broken
# Compare: echo "With DNSSEC validation:" dig example.com echo -e "\nWithout validation:" dig example.com +cd
# If +cd works, problem is DNSSEC chain
# Check specific resolver behavior dig @8.8.8.8 example.com +dnssec dig @8.8.8.8 example.com +cd ```
- 1.Use DNSSEC diagnostic tools to trace the chain.
```bash # Check DNSSEC chain with dnsviz dnsviz example.com
# Online: https://dnsviz.net
# Use delv (BIND's DNSSEC validation tool) delv example.com A
# delv shows validation result and chain
# Check specific server with DNSSEC dig @ns1.example.com example.com DNSKEY +dnssec dig @ns1.example.com example.com A +dnssec
# Look for: # - DNSKEY records with RRSIG # - A records with RRSIG # - AD (Authenticated Data) flag ```
- 1.Verify DNSKEY records exist and are valid.
```bash # Get DNSKEY records dig example.com DNSKEY +dnssec +short
# Output should show: # 257 3 13 <key-data> ; KSK (Key Signing Key) # 256 3 13 <key-data> ; ZSK (Zone Signing Key) # RRSIG DNSKEY ...
# Key flags: # 256 = ZSK (Zone Signing Key) - signs zone records # 257 = KSK (Key Signing Key) - signs DNSKEY set
# Check DNSKEY signatures dig example.com DNSKEY +dnssec
# Look for RRSIG record covering DNSKEY set # ;; ANSWER SECTION: # example.com. DNSKEY 257 3 13 ... # example.com. DNSKEY 256 3 13 ... # example.com. RRSIG DNSKEY 13 2 3600 ... ; Signature
# Signature must be present and valid ```
- 1.Check DS record at parent zone matches DNSKEY.
```bash # DS (Delegation Signer) record at parent must match DNSKEY
# Get DS from parent zone (TLD server) dig @a.gtld-servers.net example.com DS +short
# Output format: # Key-tag Algorithm Digest-type Digest
# Get DNSKEY from zone dig @ns1.example.com example.com DNSKEY +short
# Calculate expected DS from DNSKEY # Use dnssec-dsfromkey tool: dnssec-dsfromkey -K /etc/bind/keys Kexample.com.+013+12345.key
# Compare DS values - must match!
# Common mismatches: # - Wrong algorithm (DS says 5, DNSKEY says 13) # - Wrong digest type (DS-1, DS-2, DS-4) # - Key tag doesn't match DNSKEY # - DS for old key, DNSKEY changed ```
- 1.Check RRSIG signatures are present and not expired.
```bash # Every record type in signed zone needs RRSIG
# Check A record signature dig @ns1.example.com example.com A +dnssec
# Look for RRSIG: # ;; ANSWER SECTION: # example.com. A 192.0.2.1 # example.com. RRSIG A 13 2 3600 20260504... 20260404...
# RRSIG fields: # Type covered: A # Algorithm: 13 (ECDSAP256SHA256) # Labels: 2 # Original TTL: 3600 # Signature expiration: date # Signature inception: date
# Check signature dates expiration=$(dig example.com A +dnssec | grep RRSIG | awk '{print $8}') echo "Signature expires: $expiration"
# Convert to readable date date -d @$expiration
# If expired, zone needs re-signing! ```
- 1.Verify zone is properly signed after changes.
```bash # After modifying a signed zone, you MUST re-sign
# For BIND, use dnssec-signzone dnssec-signzone -A -K /etc/bind/keys -o example.com example.com.zone
# Check zone is signed: named-checkzone example.com example.com.zone.signed
# For dynamic zones, use auto-signing # In named.conf: zone "example.com" { type master; file "example.com.zone.signed"; auto-dnssec maintain; inline-signing yes; };
# After changes: rndc sign example.com # Or rndc reload example.com
# Verify new signatures dig @localhost example.com A +dnssec | grep RRSIG ```
- 1.Check for algorithm compatibility issues.
```bash # DNSSEC algorithms must match throughout chain
# Algorithm numbers: # 3 = DSA (deprecated) # 5 = RSASHA1 (deprecated) # 7 = RSASHA1-NSEC3-SHA1 (deprecated) # 8 = RSASHA256 # 10 = RSASHA512 # 13 = ECDSAP256SHA256 (recommended) # 14 = ECDSAP384SHA384
# Check algorithms: echo "DNSKEY algorithm:" dig example.com DNSKEY +short | awk '{print $3}'
echo "DS algorithm:" dig @a.gtld-servers.net example.com DS +short | awk '{print $2}'
echo "RRSIG algorithm:" dig example.com A +dnssec | grep RRSIG | awk '{print $4}'
# All must match! # DS algorithm must equal DNSKEY algorithm # RRSIG algorithm must match DNSKEY algorithm
# Common issue: Updated DNSKEY to algorithm 13 # But DS still has algorithm 5 (old) ```
- 1.Debug key rollover issues.
```bash # Key rollover is complex process # Failed rollover breaks DNSSEC
# Check current keys dnssec-keygen -K /etc/bind/keys -l example.com
# Or list keys: ls -la /etc/bind/keys/Kexample.com*
# For KSK rollover: # 1. Publish new KSK # 2. Wait for propagation # 3. Get new DS from parent # 4. Wait for old DS to expire # 5. Remove old KSK
# For ZSK rollover: # 1. Publish new ZSK # 2. Wait for propagation # 3. Re-sign zone with new ZSK # 4. Remove old ZSK
# Check if rollover is in progress: dig example.com DNSKEY +dnssec # Should show both old and new keys during rollover
# Use BIND's rollover management: # auto-dnssec maintain; inline-signing yes; # BIND handles rollover automatically ```
- 1.Fix NSEC/NSEC3 chain issues.
```bash # NSEC/NSEC3 proves non-existence of records
# Check NSEC3 parameters dig example.com NSEC3PARAM +dnssec
# For NSEC: dig example.com NSEC +dnssec
# Common NSEC/NSEC3 issues: # - Missing NSEC3PARAM after zone signing # - NSEC3 salt changed without re-signing # - Broken NSEC chain
# Verify negative responses are signed: dig nonexistent.example.com A +dnssec
# Should return: # - NXDOMAIN status # - NSEC/NSEC3 proving name doesn't exist # - RRSIG covering NSEC/NSEC3
# If NSEC/NSEC3 missing, "nonexistent" queries fail validation ```
- 1.Repair broken DNSSEC configuration.
```bash # Emergency fix: Disable DNSSEC temporarily
# Remove DS at parent zone (registrar) # This breaks chain of trust # Domain will resolve but without DNSSEC
# Or remove DNSKEY from zone: # Zone will be unsigned, DS will fail # Remove DS at parent too
# Proper repair: # 1. Generate new keys dnssec-keygen -a ECDSAP256SHA256 -K /etc/bind/keys example.com dnssec-keygen -a ECDSAP256SHA256 -f KSK -K /etc/bind/keys example.com
# 2. Sign zone dnssec-signzone -A -K /etc/bind/keys -o example.com example.com.zone
# 3. Get new DS dnssec-dsfromkey -K /etc/bind/keys Kexample.com.+013+<ksk-id>.key
# 4. Submit DS to registrar
# 5. Wait for propagation
# 6. Verify chain delv example.com A ```
Verification
Complete DNSSEC validation verification:
```bash # 1. Test with validation enabled echo "=== DNSSEC Validation ===" delv example.com A 2>&1 | head -10
# 2. Check DNSKEY echo -e "\n=== DNSKEY ===" dig example.com DNSKEY +dnssec +short
# 3. Check DS at parent echo -e "\n=== DS at Parent ===" dig @a.gtld-servers.net example.com DS +short
# 4. Verify signatures not expired echo -e "\n=== Signature Expiration ===" dig example.com A +dnssec | grep RRSIG | awk '{print "Expires at:", $8}' dig example.com DNSKEY +dnssec | grep RRSIG | awk '{print "Expires at:", $8}'
# 5. Check full chain with +trace echo -e "\n=== Chain Trace ===" dig example.com +trace +dnssec 2>&1 | grep -E "DNSKEY|DS|RRSIG" | head -20
# 6. Verify NSEC3 for negative responses echo -e "\n=== Negative Response ===" dig nonexistent.example.com A +dnssec 2>&1 | grep -E "NSEC|RRSIG"
# 7. Test from validating resolver echo -e "\n=== Public Resolver Test ===" dig @8.8.8.8 example.com A +dnssec # Should have AD flag in response ```
DNSSEC Algorithm Quick Reference
```bash # Recommended algorithms: # Algorithm 13 (ECDSAP256SHA256) - Most common, good performance # Algorithm 14 (ECDSAP384SHA384) - Stronger, slightly slower
# Deprecated (avoid): # Algorithm 3 (DSA) # Algorithm 5 (RSASHA1) # Algorithm 7 (RSASHA1-NSEC3-SHA1)
# Digest types for DS: # 1 (SHA-1) - Deprecated # 2 (SHA-256) - Recommended # 4 (SHA-384) - Stronger
# Key types: # 256 (ZSK) - Zone Signing Key # 257 (KSK) - Key Signing Key
# Best practice: # - Use algorithm 13 for both ZSK and KSK # - Use digest type 2 for DS # - Rotate ZSK annually, KSK every 2-5 years # - Use automatic signing (inline-signing) ```
DNSSEC failures cause complete resolution failure for validating resolvers. Always test with +cd to confirm DNSSEC is the issue, then trace the chain of trust to find the break point.