BIND DNS Server Hardening Checklist
BIND, when left unchecked, can become a significant liability, not just a performance bottleneck.
BIND DNS Server Hardening Checklist
BIND, when left unchecked, can become a significant liability, not just a performance bottleneck.
1. Restrict Zone Transfers
Zone transfers (AXFR/IXFR) allow a secondary DNS server to request a full copy of a zone from a primary. If an attacker can trick your server into transferring a zone it shouldn’t, they gain valuable information about your internal network.
Diagnosis:
Check your named.conf or zone files for allow-transfer or also-notify directives.
grep -E "allow-transfer|also-notify" /etc/named.conf /etc/bind/named.conf.local /etc/bind/zones/*.conf
Common Causes & Fixes:
-
Overly permissive
allow-transfer:- Cause:
allow-transfer { any; };orallow-transfer { localnets; };whenlocalnetsincludes too much. - Fix: Restrict to specific IP addresses of your known secondary servers.
zone "example.com" { type master; file "zones/db.example.com"; allow-transfer { 192.168.1.10; 192.168.1.11; }; // IPs of your secondary DNS servers }; - Why it works: Only explicitly listed IP addresses are allowed to request a full zone transfer, preventing unauthorized information disclosure.
- Cause:
-
Missing
allow-transfer(default is oftenany):- Cause: Forgetting to add the directive means BIND defaults to allowing transfers to anyone.
- Fix: Add
allow-transfer { <IP_of_secondary>; };to your zone definitions.zone "internal.example.com" { type master; file "zones/db.internal.example.com"; allow-transfer { 10.0.0.5; }; // IP of your internal secondary DNS }; - Why it works: Explicitly defines the authorized recipients for zone data.
-
also-notifyexposing too much:- Cause:
also-notifysends NOTIFY messages to listed hosts when zone data changes. If these hosts are not authorized secondary servers, it can reveal zone existence. - Fix: Ensure
also-notifyonly lists your legitimate secondary DNS servers.zone "example.com" { type master; file "zones/db.example.com"; also-notify { 192.168.1.10; 192.168.1.11; }; }; - Why it works: Limits the propagation of zone change notifications to trusted systems.
- Cause:
-
Wildcard
allow-transferinoptions:- Cause: A global
allow-transfer { any; };in theoptionsblock overrides zone-specific restrictions. - Fix: Remove the global
allow-transfer { any; };from theoptionsblock and define it per zone.options { directory "/var/cache/bind"; // Remove: allow-transfer { any; }; // ... other options }; - Why it works: Prevents accidental broad access granted by a global setting.
- Cause: A global
-
Allowing transfers for zones that shouldn’t be transferred:
- Cause: Applying
allow-transferto zones that are not intended to be secondary zones, like stub zones or forwarding zones. - Fix: Remove
allow-transferfrom zones where it’s not applicable or ensure it’s restricted to authorized secondaries.zone "forwarders.example.com" { type forward; forwarders { 8.8.8.8; 8.8.4.4; }; // No allow-transfer needed here }; - Why it works: Prevents unnecessary exposure of zone data for non-authoritative zone types.
- Cause: Applying
2. Disable Recursion for External Clients
Recursive queries allow clients to ask your DNS server to find records for any domain on the internet. If your BIND server is exposed to the internet and allows recursion from untrusted clients, it can be abused as an open resolver for DNS amplification attacks.
Diagnosis:
Check the recursion and allow-recursion directives in your named.conf.
grep -E "recursion|allow-recursion" /etc/named.conf /etc/bind/named.conf.local
Common Causes & Fixes:
-
recursion yes;in theoptionsblock withoutallow-recursion:- Cause: This enables recursion for all clients, making your server an open resolver.
- Fix: Explicitly define which clients are allowed to perform recursion, or disable recursion entirely if your server is authoritative-only.
options { directory "/var/cache/bind"; recursion yes; allow-recursion { 127.0.0.1; 192.168.1.0/24; }; // Allow recursion only for localhost and internal network // ... other options }; - Why it works: Limits the scope of recursive queries to trusted IP addresses or networks.
-
recursion yes;andallow-recursion { any; };:- Cause: The most dangerous combination, enabling recursion for everyone.
- Fix: Change
allow-recursion { any; };to a specific list of trusted client IPs or networks, or removeallow-recursionif recursion is not needed at all.options { directory "/var/cache/bind"; recursion yes; allow-recursion { localnets; }; // If localnets is correctly defined to be ONLY internal IPs // OR better: // allow-recursion { 10.1.1.0/24; 172.16.0.0/16; }; }; - Why it works: Restricts recursion to a defined set of trusted clients, preventing abuse.
-
Recursion enabled on an authoritative-only server:
- Cause: If a server is meant only to hold zones and answer authoritatively, recursion should be disabled.
- Fix: Set
recursion no;in theoptionsblock.options { directory "/var/cache/bind"; recursion no; // Disable recursion entirely // ... other options }; - Why it works: Prevents the server from performing recursive lookups, removing the attack vector for open resolvers.
-
allow-recursionapplied globally inoptionswhen not desired:- Cause: A global
allow-recursiondirective might be too broad for specific views. - Fix: Define
allow-recursionwithin specificviewblocks if using views, or ensure the globalallow-recursionis appropriately restricted.view "internal" { match-clients { 192.168.1.0/24; }; recursion yes; allow-recursion { 192.168.1.0/24; }; // Specific to this view zone "internal.example.com" { ... }; }; view "external" { match-clients { any; }; recursion no; // No recursion for external clients zone "example.com" { ... }; }; - Why it works: Allows granular control over recursion based on client source IP, enabling different policies for internal and external users.
- Cause: A global
-
recursion yes;andallow-recursionmissing from aviewblock:- Cause: If you use views, and
recursion yes;is in theoptionsblock, but a specificviewdoesn’t explicitly setallow-recursion, it might inherit too much. - Fix: Ensure each
viewhas explicitrecursionandallow-recursionsettings if the globaloptionsare not sufficient.options { recursion yes; // Potentially problematic if not managed by views }; view "internal" { match-clients { 192.168.1.0/24; }; allow-recursion { 192.168.1.0/24; }; // Explicitly set // ... }; view "external" { match-clients { any; }; allow-recursion { none; }; // Explicitly deny // ... }; - Why it works: Ensures that recursion is managed on a per-view basis, preventing unintended access.
- Cause: If you use views, and
3. Limit DNSSEC Validation (if not needed)
While DNSSEC is a crucial security feature, enabling DNSSEC validation on servers that don’t require it can consume more CPU and memory resources. If your server is purely authoritative or only forwards queries without validating, disabling validation can be a performance optimization.
Diagnosis:
Check the dnssec-enable and dnssec-validation directives in your named.conf.
grep -E "dnssec-enable|dnssec-validation" /etc/named.conf /etc/bind/named.conf.local
Common Causes & Fixes:
-
dnssec-enable yes;anddnssec-validation auto;on a server not needing validation:- Cause: Default settings might enable validation even if the server’s role doesn’t require it.
- Fix: Set
dnssec-enable no;anddnssec-validation no;in theoptionsblock.options { directory "/var/cache/bind"; dnssec-enable no; dnssec-validation no; // ... other options }; - Why it works: Prevents BIND from performing the computationally intensive DNSSEC validation process for every query.
-
dnssec-enable yes;butdnssec-validation no;(oroff):- Cause:
dnssec-enablewithout actual validation can still cause some overhead. - Fix: Ensure both are set to
noif validation is not a requirement.options { directory "/var/cache/bind"; dnssec-enable no; dnssec-validation no; // ... }; - Why it works: Explicitly disables all DNSSEC related operations that are not required for the server’s function.
- Cause:
4. Disable Server Identification
By default, BIND includes version information in its responses (e.g., Server: bind.example.com (BIND 9.16.1)). This can aid attackers in identifying your BIND version and exploiting known vulnerabilities.
Diagnosis:
Check the version directive in your named.conf.
grep -E "version" /etc/named.conf /etc/bind/named.conf.local
Common Causes & Fixes:
- Default
versiondirective or noversiondirective (default behavior):- Cause: BIND typically includes its version in responses.
- Fix: Use the
versiondirective to obscure the version or disable it.options { directory "/var/cache/bind"; version "not disclosed"; // Obscure the version // Or to completely disable: // version none; // ... }; - Why it works: Replaces the actual BIND version with a generic string or removes it entirely, making it harder for attackers to target specific vulnerabilities.
5. Use Response Rate Limiting (RRL)
DNS amplification attacks flood a target with spoofed DNS requests. Response Rate Limiting (RRL) helps mitigate this by limiting the rate at which identical responses are sent to the same client IP address.
Diagnosis:
Check for rate-limit statements in your named.conf.
grep -E "rate-limit" /etc/named.conf /etc/bind/named.conf.local
Common Causes & Fixes:
-
RRL not configured:
- Cause: The server is vulnerable to amplification attacks because rate limiting is not enabled.
- Fix: Add
rate-limitstatements to youroptionsor zone configurations.options { directory "/var/cache/bind"; rate-limit { responses-per-second 5; // Limit to 5 identical responses per second window 5; // Check over a 5-second window log-only no; // Actually drop excessive responses errors-per-second 2; // Limit error responses too }; // ... }; - Why it works: Prevents a single client from receiving an overwhelming number of identical DNS responses, significantly reducing the effectiveness of amplification attacks.
-
RRL configured too aggressively or too leniently:
- Cause: Incorrect
responses-per-secondorwindowvalues can lead to legitimate users being rate-limited or the server still being susceptible to attacks. - Fix: Adjust
responses-per-secondandwindowbased on your expected traffic load and observed attack patterns. Start with conservative values and tune.options { // ... rate-limit { responses-per-second 10; // Increased for higher legitimate traffic window 10; // Wider window for more tolerance log-only no; errors-per-second 3; }; // ... }; - Why it works: Balances the need to prevent abuse with the requirement to serve legitimate queries, optimizing performance and security.
- Cause: Incorrect
6. Use Views for Network Segmentation
Views allow BIND to present different zone data and configurations based on the client’s IP address. This is powerful for separating internal and external DNS views, preventing internal network details from being exposed externally.
Diagnosis:
Check for view blocks in your named.conf.
grep -E "view" /etc/named.conf /etc/bind/named.conf.local
Common Causes & Fixes:
-
No
viewblocks, exposing internal zones externally:- Cause: A single configuration serves all clients, meaning internal zones are visible to external queries.
- Fix: Create separate
viewblocks for internal and external clients.options { directory "/var/cache/bind"; recursion yes; allow-recursion { localnets; }; // Example: only internal clients recurse }; view "internal" { match-clients { 192.168.1.0/24; }; // Internal network recursion yes; include "/etc/bind/named.conf.internal"; // Include internal zones }; view "external" { match-clients { any; }; // All other clients recursion no; include "/etc/bind/named.conf.external"; // Include external zones }; - Why it works: Allows BIND to selectively serve different sets of zones and apply different access controls based on the client’s network origin.
-
match-clientstoo broad or too narrow:- Cause: Incorrect IP ranges in
match-clientscan lead to clients being in the wrong view, or no view at all. - Fix: Carefully define
match-clientsto accurately reflect your internal and external network segments.view "internal" { match-clients { 10.0.0.0/8; 172.16.0.0/12; 192.168.0.0/16; }; // Common private ranges // ... }; view "external" { match-clients { ! 10.0.0.0/8; ! 172.16.0.0/12; ! 192.168.0.0/16; }; // All non-private IPs // ... }; - Why it works: Ensures clients are correctly categorized into their intended views, enforcing the desired security and access policies.
- Cause: Incorrect IP ranges in
After implementing these hardening measures, the next error you might encounter is related to dnssec-key-dnskey-name validation failures if you are signing your zones and haven’t updated keys correctly.