Kurumsal ağlarda DNS yönetimi düşündüğünden çok daha karmaşık bir hal alabilir. Özellikle hem iç ağdan hem de internetten erişilen servisler söz konusu olduğunda, aynı alan adının farklı IP adresleri döndürmesi gerektiği durumlarla sıkça karşılaşırsın. İşte tam bu noktada Split-Horizon DNS (ya da Split-Brain DNS olarak da bilinir) devreye girer. BIND ile bu yapıyı kurmak ilk bakışta karmaşık görünse de mantığını kavradığında son derece temiz ve yönetilebilir bir altyapı ortaya çıkar.
Split-Horizon DNS Nedir ve Neden Lazım?
Basit bir senaryo düşün: Şirketinde example.com adında bir alan adın var. Bu alan adının altında mail.example.com servisi çalışıyor. İç ağdan bu servise 192.168.1.50 adresiyle ulaşılıyor, ama internet üzerinden 203.0.113.25 gibi bir public IP üzerinden erişiliyor. Eğer tek bir DNS zone’un varsa ya herkese iç IP’yi söylersin (dışarıdan erişemezler) ya da herkese dış IP’yi söylersin (iç ağdan gereksiz yere firewall’dan geçer, belki de hiç çalışmaz).
Split-Horizon DNS tam olarak bu problemi çözer: kim sorduğuna göre farklı cevap ver. İç ağdaki istemciler mail.example.com için 192.168.1.50 alırken, dışarıdaki kullanıcılar 203.0.113.25 alır.
Bu yapının getirdiği avantajlar:
- Güvenlik: İç sunucuların gerçek IP adresleri dış dünyaya sızmaz
- Performans: İç ağ trafiği gereksiz yere internete çıkmaz, NAT hairpin sorunları yaşanmaz
- Esneklik: Test ortamları, staging sunucuları gibi iç kaynaklara kolay erişim
- Bakım kolaylığı: Tek bir alan adı yönetimi, iki farklı view
BIND’de View Mekanizması
BIND’in bu işi yapmasını sağlayan şey view direktifidir. View’lar sayesinde BIND, isteğin geldiği kaynak IP adresine göre farklı zone dosyalarını kullanır. Temel mantık şu: önce ACL (Access Control List) tanımla, sonra her view’a hangi ACL’nin geçerli olduğunu söyle, her view için ayrı zone dosyası kullan.
Ortam Hazırlığı
Önce BIND’in kurulu olduğundan emin olalım. Ubuntu/Debian için:
sudo apt update
sudo apt install bind9 bind9utils bind9-doc -y
RHEL/CentOS/AlmaLinux için:
sudo dnf install bind bind-utils -y
sudo systemctl enable --now named
Yapılandırma dosyalarımız genellikle şu konumlarda bulunur:
- Ubuntu/Debian:
/etc/bind/ - RHEL tabanlı:
/etc/named/veya/etc/named.conf
Ben bu yazıda RHEL tabanlı sistemi esas alacağım ama Debian için farklar oldukça minimal, yol isimlerini değiştirmen yeterli olacak.
Senaryo Tanımı
Gerçek dünya senaryomuzu netleştirelim:
- Şirket alan adı:
example.com - İç ağ:
192.168.1.0/24 - DNS sunucu iç IP:
192.168.1.10 - DNS sunucu dış IP:
203.0.113.10 - Web sunucu iç IP:
192.168.1.20 - Web sunucu dış IP:
203.0.113.20 - Mail sunucu iç IP:
192.168.1.50 - Mail sunucu dış IP:
203.0.113.25
named.conf Yapılandırması
Ana konfigürasyon dosyasını oluşturalım. Önce ACL tanımlarımızı yapalım:
sudo nano /etc/named.conf
// ACL Tanımları
acl "internal_networks" {
192.168.1.0/24;
192.168.2.0/24;
10.0.0.0/8;
127.0.0.1;
::1;
};
acl "external_networks" {
any;
};
options {
listen-on port 53 { 192.168.1.10; 203.0.113.10; 127.0.0.1; };
listen-on-v6 port 53 { ::1; };
directory "/var/named";
dump-file "/var/named/data/cache_dump.db";
statistics-file "/var/named/data/named_stats.txt";
recursion yes;
allow-recursion { internal_networks; };
allow-query { any; };
dnssec-validation auto;
// View'lar kullanıldığında bu önemli
notify no;
};
logging {
channel default_log {
file "/var/log/named/default.log" versions 5 size 20m;
print-time yes;
print-severity yes;
print-category yes;
severity dynamic;
};
category default { default_log; };
category queries { default_log; };
};
// İç view
view "internal" {
match-clients { internal_networks; };
recursion yes;
zone "example.com" IN {
type master;
file "/var/named/internal/example.com.zone";
allow-update { none; };
};
zone "1.168.192.in-addr.arpa" IN {
type master;
file "/var/named/internal/192.168.1.rev";
allow-update { none; };
};
// Root hints
zone "." IN {
type hint;
file "named.ca";
};
};
// Dış view
view "external" {
match-clients { external_networks; };
recursion no;
zone "example.com" IN {
type master;
file "/var/named/external/example.com.zone";
allow-update { none; };
};
zone "." IN {
type hint;
file "named.ca";
};
};
Burada dikkat edilmesi gereken önemli bir nokta: recursion no dış view için. Dışarıdaki rastgele kullanıcıların DNS sunucumuzu recursive resolver olarak kullanmasını istemiyoruz, bu güvenlik açısından kritik.
İç Zone Dosyası
İç ağ için zone dosyasını oluşturalım:
sudo mkdir -p /var/named/internal
sudo nano /var/named/internal/example.com.zone
$TTL 3600
@ IN SOA ns1.example.com. admin.example.com. (
2024011501 ; Serial (YYYYMMDDNN formatında)
3600 ; Refresh
900 ; Retry
604800 ; Expire
300 ) ; Minimum TTL
; Name Servers
@ IN NS ns1.example.com.
@ IN NS ns2.example.com.
; İç ağ için A kayıtları - gerçek iç IP'ler
ns1 IN A 192.168.1.10
ns2 IN A 192.168.1.11
@ IN A 192.168.1.20
www IN A 192.168.1.20
mail IN A 192.168.1.50
ftp IN A 192.168.1.30
intranet IN A 192.168.1.100
dev IN A 192.168.1.200
staging IN A 192.168.1.201
; Mail kayıtları
@ IN MX 10 mail.example.com.
; İç ağa özel servisler
vpn IN A 192.168.1.5
backup IN A 192.168.1.60
monitor IN A 192.168.1.70
; TXT kayıtları
@ IN TXT "v=spf1 ip4:192.168.1.50 ~all"
Dış Zone Dosyası
Dış kullanıcılar için zone dosyası. Bu dosyada iç IP’lerden hiç bahsetmiyoruz:
sudo mkdir -p /var/named/external
sudo nano /var/named/external/example.com.zone
$TTL 3600
@ IN SOA ns1.example.com. admin.example.com. (
2024011501 ; Serial - iç zone ile aynı olması şart değil
3600 ; Refresh
900 ; Retry
604800 ; Expire
300 ) ; Minimum TTL
; Name Servers - dış IP'ler
@ IN NS ns1.example.com.
@ IN NS ns2.example.com.
; Dış ağ için A kayıtları - public IP'ler
ns1 IN A 203.0.113.10
ns2 IN A 203.0.113.11
@ IN A 203.0.113.20
www IN A 203.0.113.20
mail IN A 203.0.113.25
ftp IN A 203.0.113.30
; Mail kayıtları
@ IN MX 10 mail.example.com.
; SPF, DKIM gibi TXT kayıtlar
@ IN TXT "v=spf1 ip4:203.0.113.25 ~all"
; Dışarıya açık olmayan servisleri burada tanımlamıyoruz
; dev, staging, intranet, backup gibi kaynaklar dış zone'da yok
Dikkat ettiysen dev, staging, intranet, backup gibi host’lar dış zone’da hiç yok. Bu kasıtlı, dışarıdan bu isimlere yapılan sorgular NXDOMAIN döndürecek.
Reverse Zone ve PTR Kayıtları
İç ağ için reverse zone da ekleyelim:
sudo nano /var/named/internal/192.168.1.rev
$TTL 3600
@ IN SOA ns1.example.com. admin.example.com. (
2024011501
3600
900
604800
300 )
@ IN NS ns1.example.com.
@ IN NS ns2.example.com.
; PTR kayıtları
5 IN PTR vpn.example.com.
10 IN PTR ns1.example.com.
11 IN PTR ns2.example.com.
20 IN PTR www.example.com.
30 IN PTR ftp.example.com.
50 IN PTR mail.example.com.
60 IN PTR backup.example.com.
70 IN PTR monitor.example.com.
100 IN PTR intranet.example.com.
200 IN PTR dev.example.com.
201 IN PTR staging.example.com.
Dosya İzinleri ve Servis Yönetimi
Zone dosyalarının izinlerini doğru ayarlamak şart:
# Dizin ve dosya sahipliği
sudo chown -R named:named /var/named/internal
sudo chown -R named:named /var/named/external
sudo chmod 750 /var/named/internal
sudo chmod 750 /var/named/external
sudo chmod 640 /var/named/internal/*.zone
sudo chmod 640 /var/named/internal/*.rev
sudo chmod 640 /var/named/external/*.zone
# Log dizini
sudo mkdir -p /var/log/named
sudo chown named:named /var/log/named
# SELinux varsa (RHEL tabanlı)
sudo chcon -R -t named_zone_t /var/named/internal
sudo chcon -R -t named_zone_t /var/named/external
Yapılandırmayı test edip servisi başlatalım:
# Konfigürasyon syntax kontrolü
sudo named-checkconf /etc/named.conf
# Zone dosyası kontrolü - iç
sudo named-checkzone example.com /var/named/internal/example.com.zone
# Zone dosyası kontrolü - dış
sudo named-checkzone example.com /var/named/external/example.com.zone
# Servisi başlat/yeniden başlat
sudo systemctl restart named
sudo systemctl status named
# Firewall kuralı (gerekirse)
sudo firewall-cmd --permanent --add-service=dns
sudo firewall-cmd --reload
Test ve Doğrulama
Yapılandırmanın doğru çalışıp çalışmadığını test edelim:
# İç ağdan test (192.168.1.x bir makineden)
dig @192.168.1.10 www.example.com A
# Beklenen: 192.168.1.20
# Dış ağdan simüle et - source IP belirterek
dig @203.0.113.10 www.example.com A
# Beklenen: 203.0.113.20
# İç ağdan iç-özel bir kayıt
dig @192.168.1.10 intranet.example.com A
# Beklenen: 192.168.1.100
# Dış ağdan iç-özel kayıt (var olmamalı)
dig @203.0.113.10 intranet.example.com A
# Beklenen: NXDOMAIN
# Reverse lookup testi
dig @192.168.1.10 -x 192.168.1.50
# Beklenen: mail.example.com
# View'ın hangisini kullandığını görmek için
dig @192.168.1.10 www.example.com +short
dig @203.0.113.10 www.example.com +short
Bir bash scripti yazarak toplu test yapabilirsin:
#!/bin/bash
# dns_split_test.sh
INTERNAL_DNS="192.168.1.10"
EXTERNAL_DNS="203.0.113.10"
DOMAIN="example.com"
echo "=== Split-Horizon DNS Test ==="
echo ""
echo "[+] İç DNS Sorguları:"
for host in www mail ftp intranet dev staging; do
result=$(dig @${INTERNAL_DNS} ${host}.${DOMAIN} A +short 2>/dev/null)
echo " ${host}.${DOMAIN}: ${result:-NXDOMAIN}"
done
echo ""
echo "[+] Dış DNS Sorguları:"
for host in www mail ftp intranet dev staging; do
result=$(dig @${EXTERNAL_DNS} ${host}.${DOMAIN} A +short 2>/dev/null)
echo " ${host}.${DOMAIN}: ${result:-NXDOMAIN}"
done
echo ""
echo "[+] Güvenlik Testi - iç IP'ler dışarı sızıyor mu?"
internal_ips=$(dig @${EXTERNAL_DNS} ${DOMAIN} A +short | grep "^192.168." 2>/dev/null)
if [ -n "$internal_ips" ]; then
echo " UYARI: İç IP'ler dış sorguda görünüyor! ${internal_ips}"
else
echo " OK: İç IP'ler dış sorgularda görünmüyor."
fi
Yaygın Sorunlar ve Çözümleri
View Sırası Önemli
BIND view’ları sırayla değerlendirir. any içeren bir view en sona konulmalıdır. Aksi halde sonraki view’lar hiç değerlendirilmez. İç view her zaman önce gelmelidir.
Zone Dosyaları View İçinde Tekrarlanmalı
Hint zone ve localhost zone gibi ortak zone’ları her view içinde ayrı ayrı tanımlamak gerekir. Bir view içinde tanımlanan zone diğer view için geçerli değildir. Bu başlangıçta can sıkıcı görünse de mantıklı: her view kendi başına bağımsız bir DNS sunucusu gibi davranır.
Serial Numarası Yönetimi
İç ve dış zone’ların serial numaralarını bağımsız yönetebilirsin ama karışıklık yaratmaması için her ikisini de güncellemeyi alışkanlık haline getir. Özellikle secondary DNS sunucuların varsa serial tutarlılığı kritik önem taşır.
Recursion Güvenliği
Dış view’da mutlaka recursion no; kullan. Bazı yöneticiler bunu atlar ve DNS sunucusu açık recursive resolver haline gelir. Bu hem güvenlik açığı hem de olası DDoS amplifikasyon saldırılarına zemin hazırlar.
İkincil DNS Sunucu ile Çalışma
Production ortamında tek DNS sunucu olmaz. İkincil sunucu eklerken view yapısını orada da birebir kurman gerekir. Master’dan slave’e zone transfer yaparken view’ların aynı isimde olması şart:
// Secondary sunucudaki named.conf örneği
view "internal" {
match-clients { internal_networks; };
zone "example.com" IN {
type slave;
masters { 192.168.1.10; };
file "/var/named/slaves/internal/example.com.zone";
};
};
view "external" {
match-clients { external_networks; };
zone "example.com" IN {
type slave;
masters { 203.0.113.10; };
file "/var/named/slaves/external/example.com.zone";
};
};
Dinamik Kayıt Güncellemeleri
DHCP entegrasyonu veya dinamik güncellemeler için allow-update direktifini view bazında ayrı ayrı yapılandırabilirsin:
view "internal" {
match-clients { internal_networks; };
zone "example.com" IN {
type master;
file "/var/named/internal/example.com.zone";
allow-update { key "internal-dhcp-key"; };
// Yalnızca iç DHCP sunucusunun güncellemesine izin ver
};
};
TSIG anahtarı oluşturmak için:
# TSIG anahtarı oluştur
sudo tsig-keygen -a hmac-sha256 internal-dhcp-key
# Çıktıyı named.conf başına ekle
# key "internal-dhcp-key" {
# algorithm hmac-sha256;
# secret "...base64 encoded secret...";
# };
Sonuç
Split-Horizon DNS, kurumsal ağlarda güvenlik ve performansın kesiştiği kritik bir altyapı bileşenidir. BIND’in view mekanizması bu işi son derece temiz bir şekilde yapmanı sağlar. Önemli olan birkaç temel prensip var: ACL tanımlarını doğru yap, view sıralamasına dikkat et, dış view’da recursion’ı kapat ve her view için zone dosyalarını bağımsız yönet.
Gerçek dünya senaryolarında bu yapıya ek olarak DNSSEC imzalaması, rate limiting ve response policy zone’ları da eklenmesi önerilir. Özellikle dışarıya açık view için DNSSEC son derece değerlidir. Ama bunlar ayrı bir yazının konusu.
Zone dosyalarında değişiklik yaptıktan sonra serial numarasını güncellemeyi ve rndc reload komutunu kullanmayı alışkanlık haline getir. named-checkzone ile her değişikliği restart öncesinde doğrula, production kesintilerinin önüne geçersin.
# Hızlı reload komutu - servisi yeniden başlatmadan zone'ları yükler
sudo rndc reload
# Belirli bir zone'u reload et
sudo rndc reload example.com IN internal
# Status kontrol
sudo rndc status
Bu yapıyı bir kez kurduğunda, iç ve dış DNS yönetiminin ne kadar kolaylaştığını göreceksin. Yeni bir servis eklemek istediğinde sadece iki zone dosyasını güncelleyip reload ediyorsun, gerisini BIND hallediyor.