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) foroptions { allow-recursion { ... }; };. If it’s missing or set toany;or a broad subnet that includes external IPs, you have a problem. -
Fix: Ensure
allow-recursionis explicitly set tonone;and that no otherallow-recursionclauses (e.g., inviewblocks orserverstatements) 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
optionsblock hasrecursion no;andallow-recursion { none; };. If you useviewstatements, you must also ensure that noviewstatement that matches your authoritative server’s IP and is intended for authoritative-only queries hasrecursion yes;or anallow-recursionclause that permits it. The safest is to haverecursion no;in the global options and not have anyviewblock 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
viewstatements, arecursion yes;within a view that matches your authoritative server’s clients can override the globalrecursion 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:
viewstatements allow BIND to serve different configurations based on the client’s IP address. If aviewthat matches your authoritative server’s clients hasrecursion yes;, it effectively enables recursion for those clients, overriding the globalrecursion 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-transfercan 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-transferto 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
forwardersstatement in youroptionsblock (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
forwardersstatement from your globaloptionsor 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
forwardersstatement 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;anddnssec-validation auto;in youroptionsblock can be confusing. Ifrecursionis not explicitlyno, these settings can contribute to BIND attempting to perform validation, which involves recursive lookups. -
Fix: Ensure
recursion no;andallow-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 withrecursion yes;. For an authoritative-only server, you generally don’t needdnssec-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 keepingrecursion no;andallow-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-recursionorrecursion 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.