Disabling recursion on authoritative BIND servers is surprisingly difficult to do correctly, and most attempts leave them vulnerable to becoming open resolvers.

Let’s see what happens when a recursive query hits a server that shouldn’t be recursing.

Imagine you’ve got a zone, example.com, and your authoritative server ns1.example.com (IP 192.168.1.10) is configured to serve it. A client on the internet (10.0.0.1) wants to know the IP of www.example.com.

dig @192.168.1.10 www.example.com A

If ns1.example.10 is truly authoritative and not recursing, it should respond directly with the A record for www.example.com from its zone file. It doesn’t need to ask any other servers. If it does try to ask other servers, that’s recursion, and it’s wrong for an authoritative-only server.

Here’s how to make sure it only answers authoritatively and doesn’t fall into the trap of recursion:

The Problem: Accidental Recursion

The most common mistake is simply not understanding what BIND considers a "recursive query." A query is recursive if the client asks the server to perform the lookup on its behalf, rather than just asking for the information the server itself possesses. Even on a server configured with allow-recursion { none; };, if you don’t explicitly restrict who can make recursive queries, BIND might still attempt to recurse for clients it deems "local" or if other configurations implicitly allow it. This turns your authoritative server into a potential open resolver, which is a huge security risk.

Common Cause 1: Missing allow-recursion or Incorrect Scope

  • Diagnosis: Check your named.conf (or included files) for options { allow-recursion { ... }; };. If it’s missing or set to any; or a broad subnet that includes external IPs, you have a problem.

  • Fix: Ensure allow-recursion is explicitly set to none; and that no other allow-recursion clauses (e.g., in view blocks or server statements) override this for the zones you’re serving authoritatively. A common setup is:

    options {
        directory "/var/cache/bind";
        recursion no; // This is the primary directive, but allow-recursion refines it
        allow-query { any; }; // Allows any client to query for your zones
        allow-recursion { none; }; // Explicitly denies recursion for everyone
        // ... other options
    };
    
    // If you have views, ensure no view overrides this for your authoritative zones
    view "internal" {
        match-clients { 192.168.0.0/16; };
        recursion yes; // This would be okay for an internal resolver, but BAD if you want ns1.example.com to be authoritative-only
        // ...
    };
    
    zone "example.com" {
        type master;
        file "/etc/bind/zones/db.example.com";
        allow-query { any; }; // Allows anyone to query this zone
    };
    

    The fix here is to ensure your global options block has recursion no; and allow-recursion { none; };. If you use view statements, you must also ensure that no view statement that matches your authoritative server’s IP and is intended for authoritative-only queries has recursion yes; or an allow-recursion clause that permits it. The safest is to have recursion no; in the global options and not have any view block that enables recursion for the clients hitting your authoritative server.

  • Why it works: The recursion no; option globally disables the recursive resolver logic. allow-recursion { none; }; then explicitly denies any attempt to perform recursion, even if other configurations might have implicitly allowed it. This double-check ensures no client can trick the server into recursing.

Common Cause 2: recursion yes; in a View

  • Diagnosis: If you are using view statements, a recursion yes; within a view that matches your authoritative server’s clients can override the global recursion no;.

  • Fix: Remove recursion yes; from any view that is intended to host authoritative zones. If a view is only for authoritative zones, it should not have recursion enabled at all.

    options {
        recursion no;
        allow-recursion { none; };
        // ...
    };
    
    view "authoritative-zones" {
        match-clients { any; }; // Or more specific if needed
        // NO recursion yes; here
        // NO allow-recursion { ... }; that permits recursion
        zone "example.com" { ... };
    };
    
    view "internal-resolver" {
        match-clients { 192.168.1.0/24; };
        recursion yes; // This is fine for a separate resolver view
        allow-recursion { 192.168.1.0/24; };
        // ...
    };
    
  • Why it works: view statements allow BIND to serve different configurations based on the client’s IP address. If a view that matches your authoritative server’s clients has recursion yes;, it effectively enables recursion for those clients, overriding the global recursion no; setting. By removing it from the authoritative view, you ensure that only the specific "internal-resolver" view (or similar) can perform recursion.

Common Cause 3: allow-transfer Misconfiguration (Indirectly Related)

  • Diagnosis: While not directly about recursion, an overly permissive allow-transfer can lead to others obtaining your zone data, which could be used in conjunction with other misconfigurations to probe or exploit your server.

  • Fix: Restrict allow-transfer to only your secondary DNS servers.

    zone "example.com" {
        type master;
        file "/etc/bind/zones/db.example.com";
        allow-transfer { 192.168.1.11; }; // IP of your secondary server
    };
    
  • Why it works: This prevents unauthorized servers from requesting a full copy of your zone, limiting the information available to potential attackers and ensuring zone data integrity.

Common Cause 4: Unnecessary forwarders Statement

  • Diagnosis: If you have a forwarders statement in your options block (or within a view that’s active for your authoritative server’s clients), BIND might attempt to use them if it receives a query it can’t answer authoritatively.

  • Fix: Remove the forwarders statement from your global options or any view intended for authoritative-only use.

    options {
        directory "/var/cache/bind";
        recursion no;
        allow-recursion { none; };
        // REMOVE this line if it exists and you don't want recursion:
        // forwarders { 8.8.8.8; 8.8.4.4; };
    };
    
  • Why it works: The forwarders statement tells BIND where to send queries it cannot answer itself. By removing it, you eliminate a pathway for BIND to engage in recursive lookups.

Common Cause 5: dnssec-enable yes; and dnssec-validation auto; (Can be Confusing)

  • Diagnosis: While DNSSEC itself doesn’t cause recursion, the presence of dnssec-enable yes; and dnssec-validation auto; in your options block can be confusing. If recursion is not explicitly no, these settings can contribute to BIND attempting to perform validation, which involves recursive lookups.

  • Fix: Ensure recursion no; and allow-recursion { none; }; are present. You can still enable DNSSEC signing for your zones (dnssec-enable yes; in the zone statement) without enabling recursive validation for clients. If you do want validation for your own internal clients (on a separate resolver), that should be on a different server or in a dedicated view with recursion yes;. For an authoritative-only server, you generally don’t need dnssec-validation auto;.

    options {
        directory "/var/cache/bind";
        recursion no;
        allow-recursion { none; };
        dnssec-enable yes; // This is for signing your zones, not for validation for clients
        // dnssec-validation auto; // Generally NOT needed on authoritative-only servers
    };
    
    zone "example.com" {
        type master;
        file "/etc/bind/zones/db.example.com";
        dnssec-enable yes; // Sign this zone
        // ...
    };
    
  • Why it works: dnssec-enable yes; globally enables DNSSEC features, including signing zones. dnssec-validation auto; tells BIND to perform DNSSEC validation for queries it makes. By keeping recursion no; and allow-recursion { none; };, you prevent BIND from performing the recursive lookups required for validation. Signing your own zones is an authoritative function; validating others’ zones is a recursive function.

Common Cause 6: Incorrect listen-on or listen-on-v6

  • Diagnosis: If BIND is listening on an IP address that is not intended for authoritative-only use, and that IP has a permissive allow-recursion or recursion yes; configuration elsewhere (e.g., in a view), it could be an entry point for unwanted recursion.

  • Fix: Explicitly define which interfaces BIND should listen on, and ensure these interfaces are only associated with authoritative service if that’s the intent.

    options {
        directory "/var/cache/bind";
        recursion no;
        allow-recursion { none; };
    
        listen-on port 53 { 192.168.1.10; 127.0.0.1; }; // Only listen on the authoritative IP and localhost
        listen-on-v6 port 53 { ::1; }; // Example for IPv6
    };
    
  • Why it works: By specifying listen-on, you control exactly which network interfaces and IP addresses BIND will bind to for listening for queries. If you only list your authoritative IP and localhost, you prevent BIND from accepting queries on other interfaces where a different, potentially recursive, configuration might be active.

After ensuring all these points are addressed, the next error you’ll likely encounter is a SERVFAIL response from a client that incorrectly expected your authoritative server to perform a recursive lookup for them.

Want structured learning?

Take the full Bind course →