How to Set Up Split-Horizon DNS
Split-horizon DNS (also called split-brain DNS) allows you to return different DNS query results depending on the source of the request. This is commonly used when you need internal clients to resolve a hostname to a private IP address while external clients resolve the same hostname to a public IP. On your Breeze instance, this technique is invaluable for hybrid environments where services are accessed both internally and externally.
Understanding Split-Horizon DNS
With split-horizon DNS, a single DNS server maintains two separate zone files for the same domain. Requests originating from trusted internal networks receive records from the internal zone, while all other requests receive records from the external zone. This eliminates the need for internal traffic to traverse the public internet when accessing local services.
Installing BIND for Split-Horizon
BIND (Berkeley Internet Name Domain) is the most common DNS server software that supports split-horizon configurations natively through its view directive.
sudo apt update
sudo apt install bind9 bind9utils bind9-doc -y
sudo systemctl enable named
sudo systemctl start named
Configuring Views in named.conf
Edit your BIND configuration to define two views: one for internal clients and one for external clients.
acl "internal-nets" {
10.0.0.0/8;
172.16.0.0/12;
192.168.0.0/16;
localhost;
};
view "internal" {
match-clients { internal-nets; };
zone "example.com" {
type master;
file "/etc/bind/zones/internal/db.example.com";
};
};
view "external" {
match-clients { any; };
zone "example.com" {
type master;
file "/etc/bind/zones/external/db.example.com";
};
};
Creating Zone Files
Create the directory structure and zone files:
sudo mkdir -p /etc/bind/zones/internal
sudo mkdir -p /etc/bind/zones/external
The internal zone file points to private IPs:
; /etc/bind/zones/internal/db.example.com
$TTL 86400
@ IN SOA ns1.example.com. admin.example.com. (
2026030201 ; Serial
3600 ; Refresh
1800 ; Retry
604800 ; Expire
86400 ) ; Minimum TTL
@ IN NS ns1.example.com.
@ IN A 10.0.1.50
app IN A 10.0.1.51
db IN A 10.0.1.52
The external zone file points to public IPs:
; /etc/bind/zones/external/db.example.com
$TTL 86400
@ IN SOA ns1.example.com. admin.example.com. (
2026030201 ; Serial
3600 ; Refresh
1800 ; Retry
604800 ; Expire
86400 ) ; Minimum TTL
@ IN NS ns1.example.com.
@ IN A 203.0.113.10
app IN A 203.0.113.11
Testing Your Configuration
- Check syntax with
sudo named-checkconfandsudo named-checkzone example.com /etc/bind/zones/internal/db.example.com - From an internal host:
dig @your-breeze-ip example.comshould return the private IP - From an external host:
dig @your-breeze-ip example.comshould return the public IP - Restart BIND after changes:
sudo systemctl restart named
Best Practices
- Always increment the serial number when updating zone files
- Keep both zone files synchronized for shared records
- Use TSIG keys for secure zone transfers if you have secondary DNS servers
- Monitor DNS resolution logs to verify correct view matching
- Consider using response policy zones (RPZ) for additional filtering