Implementing Private DNS Zones with Selective Forwarding for Hybrid Domain Resolution


4 views

Many organizations face the need to maintain both public and private subdomains under the same top-level domain (TLD). The traditional approach of creating separate zone files for private subdomains becomes cumbersome when dealing with multiple internal services.

Your existing solution with a dedicated zone for svn.example.com works but has scalability limitations:

zone "svn.example.com" IN {
    type master;
    file "/etc/named/svn.example.com.zone";
};

Each new private subdomain requires:

  • New zone declaration
  • Additional zone file
  • DNS server reload

The optimal solution combines zone delegation with conditional forwarding:

options {
    forwarders { 8.8.8.8; 8.8.4.4; }; // Public DNS resolvers
    forward first;
};

zone "example.com" {
    type master;
    file "/etc/named/example.com.zone";
    allow-query { any; };
};

zone "internal.example.com" {
    type forward;
    forwarders { 10.200.1.1; }; // Your internal DNS
};

Create a comprehensive zone file that handles both cases:

$TTL 86400
@       IN      SOA     ns1.example.com. admin.example.com. (
                        2023081501 ; Serial
                        3600       ; Refresh
                        1800       ; Retry
                        604800     ; Expire
                        86400 )    ; Minimum TTL

; Public records
@               IN  NS    ns1.example.com.
@               IN  A     203.0.113.1
www             IN  A     203.0.113.1
mail            IN  A     203.0.113.2

; Private records
svn             IN  A     10.200.1.1
git             IN  A     10.200.1.2
jenkins         IN  A     10.200.1.3

; Wildcard catch-all for public resolution
*               IN  CNAME example.com.
  1. Client queries svn.example.com
  2. DNS server checks local zone file first
  3. If no match found, forwards to public resolvers
  4. Returns either private IP or public resolution

For more complex scenarios, consider:

view "internal" {
    match-clients { 10.200.0.0/16; };
    recursion yes;
    zone "example.com" {
        type master;
        file "/etc/named/internal.example.com.zone";
    };
};

view "external" {
    match-clients { any; };
    recursion no;
    zone "example.com" {
        type master;
        file "/etc/named/external.example.com.zone";
    };
};

This BIND views configuration provides complete separation between internal and external DNS resolution while maintaining the same domain structure.

  • Implement zone file version control
  • Automate serial number increments
  • Set up monitoring for resolution failures
  • Document all private subdomains

When managing internal networks with existing public DNS infrastructure, administrators often need to make specific subdomains resolve differently internally while maintaining normal resolution for public records. The classic example is having svn.example.com point to an internal IP (10.200.1.1) while letting other *.example.com records resolve normally.

Traditional methods like creating separate zone files for each private subdomain become cumbersome at scale. Here's a better approach using BIND's forwarders and zone directives:

// named.conf fragment
options {
    forwarders { 8.8.8.8; 8.8.4.4; }; // Your public DNS servers
};

zone "example.com" {
    type master;
    file "/etc/named/zones/internal.example.com.zone";
    allow-query { internal-network; };
};

zone "." {
    type hint;
    file "/etc/named/named.ca";
};

The key is creating a zone file that contains your private records while letting other queries fall through:

; internal.example.com.zone
$TTL 86400
@       IN  SOA  ns1.example.com. admin.example.com. (
                  2023081501 ; serial
                  3600       ; refresh
                  1800       ; retry
                  604800     ; expire
                  86400 )    ; minimum

; Private records
svn         IN  A     10.200.1.1
gitlab      IN  A     10.200.1.2
jenkins     IN  A     10.200.1.3

; Public records delegation
www         IN  CNAME public-lb.example.com.
mail        IN  CNAME mailhandler.example.com.
*           IN  CNAME public-lb.example.com.

For more complex setups, consider using views and conditional forwarding:

view "internal" {
    match-clients { 10.200.0.0/16; };
    recursion yes;
    
    zone "example.com" {
        type master;
        file "/etc/named/zones/internal.example.com.zone";
    };
    
    zone "." {
        type hint;
        file "/etc/named/named.ca";
    };
};

view "external" {
    match-clients { any; };
    recursion no;
    
    zone "example.com" {
        type master;
        file "/etc/named/zones/public.example.com.zone";
    };
};

When implementing this hybrid approach, pay attention to TTL values. Private records should typically have shorter TTLs (3600 seconds) for flexibility, while public records can use longer values.

Remember to test your configuration with named-checkconf and named-checkzone before reloading BIND.