When managing an internal network with BIND DNS servers, administrators often need to resolve certain domain records to internal IP addresses while maintaining external resolution for other records. This becomes particularly challenging when dealing with domains that have both public and private resources.
The typical solution of creating individual zone files for each host (like host1.example.com) works for specific records but breaks down when you need to:
# Current working but limited configuration zone "host1.example.com" { type master; file "/etc/bind/zones/db.host1.example.com"; }; zone "host2.example.com" { type master; file "/etc/bind/zones/db.host2.example.com"; };
The problem emerges when attempting to override the apex record (example.com) while preserving external resolution for other records in the same domain.
The proper solution involves configuring BIND to act as a delegating nameserver for specific records while forwarding others:
zone "example.com" { type master; file "/etc/bind/zones/db.example.com.internal"; allow-query { any; }; };
Then in your zone file (db.example.com.internal):
$TTL 86400 @ IN SOA ns1.example.com. admin.example.com. ( 2023081501 ; Serial 3600 ; Refresh 1800 ; Retry 604800 ; Expire 86400 ; Minimum TTL ) ; Name servers IN NS ns1.example.com. IN NS ns2.example.com. ; Internal records @ IN A 192.168.1.1 host1 IN A 192.168.1.2 host2 IN A 192.168.1.3 www IN CNAME host1 ; Delegation for other records * IN NS external-ns.example.com. external-ns IN A 203.0.113.1
For more complex scenarios, consider using BIND's Response Policy Zones feature:
options { response-policy { zone "rpz.example.com"; }; }; zone "rpz.example.com" { type master; file "/etc/bind/zones/db.rpz.example.com"; allow-query { none; }; };
RPZ file content:
$TTL 3600 @ IN SOA localhost. root.localhost. ( 2023081501 ; serial 1h ; refresh 30m ; retry 1w ; expire 1h ; minimum ) IN NS localhost. ; Override specific records host1.example.com A 192.168.1.2 host2.example.com A 192.168.1.3 example.com A 192.168.1.1
After implementing either solution, verify your configuration:
# Check configuration syntax named-checkconf # Test resolution from internal clients dig host1.example.com dig otherhost.example.com dig example.com
When implementing these solutions:
- Use proper TTL values to balance cache efficiency and responsiveness
- Consider implementing DNS caching for external queries
- Monitor query response times for both internal and external records
Remember to:
- Restrict zone transfers
- Implement proper ACLs for queries
- Regularly update BIND to the latest stable version
When running an internal BIND DNS server, you might need to override certain public DNS records (like host1.example.com
) to point to internal IPs while preserving external resolution for other records (like otherhost.example.com
). The challenge arises when trying to override the domain apex (example.com
) without hijacking all subdomains.
Creating a zone for example.com
in BIND makes it authoritative for all records under that domain, causing resolution failures for non-overridden records. This happens because:
zone "example.com" { type master; file "db.example.com"; # Now responsible for ALL example.com queries };
BIND provides two effective approaches:
Method 1: Per-Host Zones with Empty Non-Terminal Wildcards
# Override specific hosts zone "host1.example.com" { type master; file "db.host1.example.com"; }; zone "host2.example.com" { type master; file "db.host2.example.com"; }; # Allow other queries to resolve externally zone "example.com" { type forward; forwarders { 8.8.8.8; 8.8.4.4; }; };
In each host zone file (db.host1.example.com
):
$TTL 3600 @ IN SOA ns1.example.com. admin.example.com. ( 2023081501 ; serial 3600 ; refresh 900 ; retry 604800 ; expire 86400 ; minimum ) @ IN NS ns1.example.com. @ IN A 192.168.1.10 * IN CNAME . # Wildcard that doesn't match non-terminals
Method 2: Response Policy Zones (RPZ)
More scalable for multiple overrides:
options { response-policy { zone "rpz-example"; }; }; zone "rpz-example" { type master; file "db.rpz-example"; allow-query { none; }; };
RPZ zone file (db.rpz-example
):
$TTL 3600 @ IN SOA localhost. root.localhost. (1 3600 1200 604800 10800) @ IN NS localhost. ; Override rules host1.example.com A 192.168.1.10 host2.example.com A 192.168.1.20 example.com A 192.168.1.1
Always verify with:
dig +norecurse host1.example.com @localhost dig otherhost.example.com @localhost
For large-scale deployments:
- Use
zone-stats yes
in options to monitor RPZ performance - Consider using
qname-wait-recurse no
to prevent resolution delays - Cache external lookups with proper TTL settings