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; }; or allow-transfer { localnets; }; when localnets includes 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.
  • Missing allow-transfer (default is often any):

    • 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-notify exposing too much:

    • Cause: also-notify sends 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-notify only 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.
  • Wildcard allow-transfer in options:

    • Cause: A global allow-transfer { any; }; in the options block overrides zone-specific restrictions.
    • Fix: Remove the global allow-transfer { any; }; from the options block 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.
  • Allowing transfers for zones that shouldn’t be transferred:

    • Cause: Applying allow-transfer to zones that are not intended to be secondary zones, like stub zones or forwarding zones.
    • Fix: Remove allow-transfer from 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.

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 the options block without allow-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; and allow-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 remove allow-recursion if 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 the options block.
      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-recursion applied globally in options when not desired:

    • Cause: A global allow-recursion directive might be too broad for specific views.
    • Fix: Define allow-recursion within specific view blocks if using views, or ensure the global allow-recursion is 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.
  • recursion yes; and allow-recursion missing from a view block:

    • Cause: If you use views, and recursion yes; is in the options block, but a specific view doesn’t explicitly set allow-recursion, it might inherit too much.
    • Fix: Ensure each view has explicit recursion and allow-recursion settings if the global options are 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.

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; and dnssec-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; and dnssec-validation no; in the options block.
      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; but dnssec-validation no; (or off):

    • Cause: dnssec-enable without actual validation can still cause some overhead.
    • Fix: Ensure both are set to no if 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.

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 version directive or no version directive (default behavior):
    • Cause: BIND typically includes its version in responses.
    • Fix: Use the version directive 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-limit statements to your options or 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-second or window values can lead to legitimate users being rate-limited or the server still being susceptible to attacks.
    • Fix: Adjust responses-per-second and window based 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.

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 view blocks, exposing internal zones externally:

    • Cause: A single configuration serves all clients, meaning internal zones are visible to external queries.
    • Fix: Create separate view blocks 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-clients too broad or too narrow:

    • Cause: Incorrect IP ranges in match-clients can lead to clients being in the wrong view, or no view at all.
    • Fix: Carefully define match-clients to 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.

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.

Want structured learning?

Take the full Bind course →