BIND ile Split-Horizon DNS Yapılandırması

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.

Yorum yapın