LFCE 37 ๐ง BIND9 โ Forwarding and Recursion
Forwarding and recursion are two mechanisms BIND9 uses to resolve names it is not authoritative for. Recursion is the iterative process: the server starts at the root, follows referrals down the hierarchy, and caches the result. Forwarding is a shortcut: the server sends the query to an upstream resolver and waits for the answer, instead of performing the iterative lookups itself. The two are related but distinct. A server can recurse without forwarding, forward without recursing (in forward only mode), or use a combination of both (in forward first mode). The configuration determines which mechanism is used and when.
The previous chapter covered authoritative zones and records. This chapter covers the resolver side of BIND9: the recursion directive that enables iterative resolution, the forwarders list that specifies upstream servers, the forward directive that controls fallback behavior, and the security boundaries (allow-recursion, allow-query-cache) that prevent the server from becoming an open resolver.
Key point: forward only and forward first are not the same as recursion. forward only disables iterative resolution entirely; if the forwarder does not answer, the query fails. forward first tries the forwarder first, and if the forwarder does not respond, the server falls back to iterative recursion. The forward directive has no effect unless forwarders is non-empty, and turning off recursion also turns off forwarding .
Why forwarding and recursion are separate controls
The iterative-resolution problem.
Recursion is the default behavior of a resolver. When a client asks for www.example.com and the server does not have it cached, the server queries a root server, receives a referral to the .com TLD servers, queries one of those, receives a referral to the example.com authoritative servers, and queries one of those for the final answer. This process is iterative: the server does the work of following the delegation chain . It requires outbound access to the root servers and the ability to reach arbitrary authoritative servers on port 53.
The forwarding problem.
Some network environments restrict outbound DNS traffic. A server behind a firewall may be allowed to query only a specific upstream resolver, not the root servers or arbitrary authoritative servers. Forwarding solves this by sending all recursive queries to the designated upstream, which performs the iterative resolution on the server’s behalf. The local server caches the answers, but it does not perform the lookups itself. This is useful when the network policy requires all DNS traffic to pass through a controlled egress point.
The fallback problem.
forward first is the default when forwarders is configured . It attempts the forwarder first. If the forwarder responds, the answer is used. If the forwarder does not respond within a timeout, the server falls back to iterative recursion. This provides resilience: if the forwarder is down, the server can still resolve names, provided it has outbound access. forward only removes this fallback. The server never attempts iterative resolution; if the forwarder fails, the query fails . The choice between the two depends on whether the server is allowed to perform iterative lookups.
a. Enabling recursion and restricting it
Recursion is enabled with recursion yes; in the options block. It is the default in BIND9, but it must be explicitly restricted to prevent the server from being used as an open resolver.
# Enable recursion and restrict it to trusted clients
options {
directory "/var/cache/bind";
recursion yes; # Enable iterative resolution
allow-recursion { trusted; }; # Only trusted clients may recurse
allow-query-cache { trusted; }; # Only trusted clients may read the cache
};
acl "trusted" {
192.168.1.0/24; # Local network
127.0.0.1; # Loopback
::1;
};
The allow-recursion ACL is the primary security boundary. Without it, the server accepts recursive queries from any client, which makes it an amplification vector . The allow-query-cache ACL controls who can read cached answers. If it is not set, BIND falls back to allow-recursion; if that is also not set, the default is localnets and localhost . The two should be configured together so that the same set of clients can both trigger recursive lookups and read the cached results.
b. Configuring forwarders and forward mode
Forwarding is configured with two statements: forwarders specifies the upstream servers, and forward specifies the fallback behavior.
# Forward all recursive queries to upstream resolvers
options {
directory "/var/cache/bind";
recursion yes; # Required for forwarding to work
allow-recursion { trusted; };
forwarders {
8.8.8.8; # Google Public DNS
8.8.4.4;
1.1.1.1; # Cloudflare DNS
};
forward first; # Try forwarders first; fall back to recursion
};
The forwarders list can contain multiple addresses. BIND selects among them using an RTT-based algorithm, preferring the fastest responder . The forward directive has two values. first (the default) tries the forwarders, and if they fail to respond, falls back to iterative recursion. only queries only the forwarders and never performs iterative resolution . A server configured with forward only behaves like a stub resolver with a cache: it relies entirely on the upstream for resolution.
The recursion directive must remain enabled for forwarding to function. The BIND documentation and mailing list confirm that turning off recursion also turns off forwarding . A server with recursion no and a forwarders list will not forward queries; the forwarders are ignored.
c. Zone-level forwarding and the empty forwarders list
Forwarding can be configured per-zone, overriding the global settings. A zone statement of type forward applies forwarding only to queries within that zone.
# Forward only queries for partner.example.com to a specific server
zone "partner.example.com" {
type forward;
forward only; # Only use the forwarder for this zone
forwarders { 10.0.0.53; }; # Internal resolver for the partner domain
};
# Disable forwarding for a specific zone (use recursion instead)
zone "internal.example.com" {
type forward;
forwarders { }; # Empty list: no forwarding for this zone
};
The empty forwarders { }; statement is the mechanism for turning off forwarding for a specific zone when a global forwarder is configured. It tells BIND that queries for this zone should not be forwarded, even though the global forwarders list would otherwise apply . This is useful when the global forwarder is appropriate for most queries but not for a specific internal domain that must be resolved iteratively.
Complete Example Session
# ============================================
# PART 1: THE ACL FOR TRUSTED CLIENTS
# ============================================
# Define the trusted network before the options block
acl "trusted" {
192.168.1.0/24;
127.0.0.1;
::1;
};
# ============================================
# PART 2: ENABLE RECURSION WITH RESTRICTIONS
# ============================================
options {
directory "/var/cache/bind";
recursion yes;
allow-recursion { trusted; };
allow-query-cache { trusted; };
allow-query { any; }; # Authoritative queries from anyone
};
# ============================================
# PART 3: CONFIGURE FORWARDERS
# ============================================
options {
forwarders {
8.8.8.8;
8.8.4.4;
};
forward first; # Try forwarders, fall back to recursion
};
# ============================================
# PART 4: VALIDATE THE CONFIGURATION
# ============================================
named-checkconf
# No output means the syntax is valid
# ============================================
# PART 5: RESTART AND TEST RECURSION
# ============================================
systemctl restart named
dig @localhost google.com
# Output: an answer with the "ra" flag (recursion available)
# ============================================
# PART 6: TEST FORWARDING IS ACTIVE
# ============================================
# Query a name that is not cached
dig @localhost example.org
# The answer comes from the forwarder or from recursion
# Check the query log to confirm the forwarder was used
# ============================================
# PART 7: TEST THE CACHE
# ============================================
dig @localhost google.com
# The second query returns faster because the answer is cached
# ============================================
# PART 8: TEST THE RESTRICTION
# ============================================
# From a client outside the trusted ACL, recursion should be refused
# dig @server google.com from an untrusted IP
# Output: status: REFUSED (or no answer)
# ============================================
# PART 9: SWITCH TO FORWARD ONLY
# ============================================
# Edit the options block
# forward only;
# Restart and test
systemctl restart named
dig @localhost google.com
# If the forwarder is reachable, the query succeeds
# If the forwarder is unreachable, the query fails (no recursion fallback)
# ============================================
# PART 10: VERIFY WITH rndc
# ============================================
rndc status
# Output shows the server is running
rndc dumpdb -cache
# Dumps the cache to a file for inspection
The ten parts demonstrate the complete forwarding and recursion configuration: ACL definition, recursion enablement with restrictions, forwarder configuration, validation, testing recursion, testing forwarding, testing the cache, testing the restriction, switching to forward only, and verification with rndc. The critical distinction is between Parts 5 and 9: with forward first, the server can still resolve names if the forwarder fails; with forward only, it cannot.
Quick Reference
Recursion and Forwarding Statements
| Statement | Values | Purpose |
|---|---|---|
recursion | yes / no | Enable or disable iterative resolution |
allow-recursion | ACL | Clients allowed to make recursive queries |
allow-query-cache | ACL | Clients allowed to read cached answers |
forwarders | IP list | Upstream resolvers for forwarding |
forward | first / only | Fallback behavior when forwarders fail |
Forward Modes
| Mode | Behavior | Fallback |
|---|---|---|
first | Try forwarders, then recurse | Yes |
only | Only forwarders, never recurse | No |
Security Defaults
| Setting | Default | Risk |
|---|---|---|
recursion | yes | Server may be an open resolver |
allow-recursion | localnets; localhost; | Local network only |
allow-query-cache | Falls back to allow-recursion | Cache exposure |
Best Practices
โ Do This:
# Restrict recursion to trusted clients
allow-recursion { trusted; }; # โ
# Set allow-query-cache to match
allow-query-cache { trusted; }; # โ
# Keep recursion enabled when using forwarders
recursion yes; # โ
# Use forward first for resilience
forward first; # โ
# Use an empty forwarders list to disable forwarding for a zone
zone "internal" { type forward; forwarders { }; }; # โ
# Validate before reloading
named-checkconf; rndc reload # โ
โ Don’t Do This:
# Leave recursion open to the internet
allow-recursion { any; }; # Amplification vector # โ
# Set recursion no and expect forwarders to work
recursion no; forwarders { 8.8.8.8; }; # Ignored # โ
# Use forward only without a reliable forwarder
forward only; # No fallback if forwarder fails # โ
# Omit the ACL for allow-recursion
# Default is localnets; may not include your client subnet # โ
# Assume forwarding overrides authoritative data
# Authoritative zones are answered locally, not forwarded # โ
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| Forwarders not used | recursion no disables forwarding | Set recursion yes |
| Recursion refused for local clients | ACL does not include the subnet | Add the subnet to allow-recursion |
| Queries fail when forwarder is down | forward only prevents fallback | Use forward first |
| Open resolver | allow-recursion { any; } | Restrict to trusted ACL |
| Zone not forwarded despite global forwarders | Zone has empty forwarders { }; | Remove the empty statement or add forwarders |
| Cache not used | allow-query-cache too restrictive | Match it to allow-recursion |
Real-World Examples
1. Enable recursion with ACL
options { recursion yes; allow-recursion { trusted; }; };
2. Configure forwarders
options { forwarders { 8.8.8.8; 1.1.1.1; }; forward first; };
3. Forward only mode
options { forwarders { 10.0.0.53; }; forward only; };
4. Empty forwarders for a zone
zone "internal" { type forward; forwarders { }; };
5. Zone-specific forwarder
zone "partner.com" { type forward; forward only; forwarders { 10.0.0.54; }; };
6. Trusted ACL
acl "trusted" { 192.168.1.0/24; 127.0.0.1; };
7. Validate configuration
named-checkconf
8. Test recursion
dig @localhost google.com
9. Check cache
rndc dumpdb -cache
10. Reload without restart
rndc reload
Visual
Recursion vs Forwarding
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ RECURSION: SERVER DOES THE WORK โ
โ โ
โ Client โโโถ BIND9 โโโถ Root โโโถ TLD โโโถ Authoritative โ
โ (iterative lookups, caches answers) โ
โ โ
โ Requires outbound access to root and authoritative servers. โ
โ โ
โ FORWARDING: UPSTREAM DOES THE WORK โ
โ โ
โ Client โโโถ BIND9 โโโถ Forwarder โโโถ Answer โ
โ (single query to upstream, caches answer) โ
โ โ
โ Requires outbound access only to the forwarder. โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Forward First vs Forward Only
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ FALLBACK BEHAVIOR โ
โ โ
โ forward first: โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ 1. Send query to forwarder โ โ
โ โ 2. If forwarder answers โ return answer โ โ
โ โ 3. If forwarder times out โ perform recursion โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ forward only: โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ 1. Send query to forwarder โ โ
โ โ 2. If forwarder answers โ return answer โ โ
โ โ 3. If forwarder times out โ FAIL (no recursion) โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ first is resilient. only is dependent on the forwarder. โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Security Boundaries
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ TWO ACLs CONTROL ACCESS โ
โ โ
โ allow-recursion { trusted; }; โ
โ โโโ Who can trigger recursive lookups โ
โ โโโ Without it: open resolver โ
โ โ
โ allow-query-cache { trusted; }; โ
โ โโโ Who can read cached answers โ
โ โโโ Without it: falls back to allow-recursion โ
โ โ
โ Both should match the same trusted set. โ
โ Authoritative queries are controlled by allow-query. โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Summary
| Item | Value |
|---|---|
| Recursion directive | recursion yes; (default) |
| Recursion ACL | allow-recursion { trusted; }; |
| Cache ACL | allow-query-cache { trusted; }; |
| Forwarders | forwarders { ip; }; |
| Forward mode | forward first; (default) or forward only; |
| Forwarding requires | recursion yes; |
| Empty forwarders | forwarders { }; disables forwarding for a zone |
| Zone-level forwarding | zone "name" { type forward; ... }; |
| Default forward mode | first (fallback to recursion) |
| Security risk | allow-recursion { any; } |
Key takeaways:
- Recursion and forwarding are separate mechanisms. Recursion performs iterative lookups from the root. Forwarding sends queries to an upstream resolver. The
recursiondirective must be enabled for forwarding to work . forward firstis resilient;forward onlyis dependent.firsttries the forwarder and falls back to recursion if it fails.onlynever recurses; if the forwarder fails, the query fails .allow-recursionis the security boundary. Without it, the server is an open resolver and an amplification vector. The default islocalnetsandlocalhost, which may not include your client subnet .allow-query-cacheshould matchallow-recursion. It controls who can read cached answers. If it is not set, it falls back toallow-recursion.- The empty
forwarders { };list disables forwarding for a zone. This overrides the global forwarders list and forces iterative recursion for that zone . - Authoritative data overrides forwarding. Queries for zones the server is authoritative for are answered locally, not forwarded. Forwarding applies only to names the server is not authoritative for .
Remember: Forwarding is a shortcut for recursion. The server sends the query upstream instead of doing the iterative work itself. forward first tries the shortcut and falls back to the full path; forward only relies entirely on the shortcut. The recursion directive must be enabled for either to work. The allow-recursion and allow-query-cache ACLs are the security boundaries that prevent the resolver from being abused. Configure the forwarders, choose the mode, restrict the clients, and validate before reloading.
Stop using slow, ad-bloated tool sites! ๐คฎ
๐ Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
โ
Finance (Mortgage, Interest, Inflation)
โ
Tech (Base64, JSON, Dev Suite, IP)
โ
Health (BMI, BMR, TDEE)
โ
Productivity (Timer, Workspace, QR)
โก๏ธ Fast & Private
๐ No data leaves your device
๐ 100% Free
๐ Use it now: https://tools.kandz.me
๐ Bookmark itโyouโll need it later!