Unbound ile Split DNS Yapılandırması

İç ağda bir DNS sunucusu kurup çalıştırmak bir şeydir, ama aynı alan adının içeriden ve dışarıdan farklı IP adresleri döndürmesini sağlamak bambaşka bir iştir. Yıllarca bunu NAT hairpin (ya da NAT loopback) ile çözdük, firewall kuralları yazdık, hosts dosyalarını düzenledik. Ama bunların hepsi geçici çözümdü. Unbound ile split DNS yapılandırması, bu problemi temiz ve sürdürülebilir bir şekilde çözer.

Bu yazıda gerçek bir kurumsal senaryoyu ele alacağız: sirket.com.tr alan adının dışarıdan erişildiğinde 203.0.113.10 (gerçek public IP) döndürmesi, iç ağdan erişildiğinde ise 192.168.10.50 (internal sunucu IP’si) döndürmesi gerekiyor. Bunu Unbound ile nasıl kurarız, nasıl yönetiriz, nelere dikkat etmemiz gerekir, hepsini adım adım geçeceğiz.

Split DNS Nedir ve Neden Gereklidir

Önce kavramı netleştirelim. Split DNS (bölünmüş DNS ya da split-horizon DNS de denir), aynı alan adı sorgusu için farklı ağ konumlarından gelen istemcilere farklı yanıtlar vermek demektir. İki temel kullanım senaryosu vardır:

  • İç/dış ayrımı: mail.sirket.com.tr iç ağdan sorgulandığında 192.168.10.25 dönsün, internetten sorgulandığında 203.0.113.25 dönsün.
  • VPN kullanıcıları için özel çözümleme: VPN’e bağlı kullanıcılar iç kaynaklara doğrudan erişebilsin, dışarıdakiler göremeysin.
  • Ortam ayrımı: api.sirket.com.tr staging ortamında farklı, production ortamında farklı bir sunucuya işaret etsin.

NAT loopback ile bu işi yapmak başta kolay görünür ama ölçeklendiğinde kabus olur. Her yeni sunucu için firewall kuralı eklemek, stateful connection tracking’i gereksiz yere zorlamak, performans kaybı… Unbound ile bunların hiçbirini yaşamazsınız.

Unbound Kurulumu

Debian/Ubuntu tabanlı sistemlerde:

apt update && apt install -y unbound
systemctl enable unbound
systemctl start unbound

RHEL/CentOS/Rocky Linux tabanlı sistemlerde:

dnf install -y unbound
systemctl enable unbound
systemctl start unbound

Kurulum sonrası temel kontrol:

unbound-control status
# veya
systemctl status unbound

Unbound’un varsayılan konfigürasyon dosyası /etc/unbound/unbound.conf altındadır. Ama ben genellikle bu dosyaya dokunmam, onun yerine /etc/unbound/unbound.conf.d/ dizini altında ayrı dosyalar oluştururum. Hem daha düzenli olur hem de bir şeyleri bozduğunuzda neyi bozduğunuzu hemen anlarsınız.

Temel Unbound Yapılandırması

Split DNS’e geçmeden önce sağlam bir temel yapılandırma şart. /etc/unbound/unbound.conf.d/base.conf dosyasını oluşturalım:

cat > /etc/unbound/unbound.conf.d/base.conf << 'EOF'
server:
    # Hangi arayüzlerde dinleyeceğimiz
    interface: 0.0.0.0
    interface: ::0

    # Sadece iç ağdan gelen sorguları kabul et
    access-control: 127.0.0.0/8 allow
    access-control: 192.168.0.0/16 allow
    access-control: 10.0.0.0/8 allow
    access-control: 172.16.0.0/12 allow
    access-control: 0.0.0.0/0 refuse

    # Temel güvenlik ayarları
    hide-identity: yes
    hide-version: yes
    harden-glue: yes
    harden-dnssec-stripped: yes
    use-caps-for-id: yes

    # Önbellek ayarları
    cache-min-ttl: 300
    cache-max-ttl: 86400
    neg-cache-size: 4m

    # Log ayarları
    verbosity: 1
    log-queries: no

    # EDNS buffer size
    edns-buffer-size: 1472

    # Performans
    num-threads: 2
    so-rcvbuf: 1m
EOF

Yapılandırmayı test etmek için:

unbound-checkconf /etc/unbound/unbound.conf

Bu komut herhangi bir syntax hatası varsa size söyler. Üretime almadan önce mutlaka çalıştırın.

Split DNS Yapılandırması: Local Zone Yaklaşımı

Şimdi işin özüne geliyoruz. Unbound’da split DNS’in en temiz yolu local-zone ve local-data direktiflerini kullanmaktır. /etc/unbound/unbound.conf.d/split-dns.conf dosyasını oluşturalım:

cat > /etc/unbound/unbound.conf.d/split-dns.conf << 'EOF'
server:
    # sirket.com.tr için iç zone tanımı
    # "static" tipi: sadece bizim tanımladığımız kayıtları döndür,
    # upstream'e sorma
    local-zone: "sirket.com.tr." static

    # A kayıtları
    local-data: "sirket.com.tr. 3600 IN A 192.168.10.50"
    local-data: "www.sirket.com.tr. 3600 IN A 192.168.10.50"
    local-data: "mail.sirket.com.tr. 3600 IN A 192.168.10.25"
    local-data: "vpn.sirket.com.tr. 3600 IN A 192.168.10.100"
    local-data: "gitlab.sirket.com.tr. 3600 IN A 192.168.10.75"

    # MX kaydı
    local-data: "sirket.com.tr. 3600 IN MX 10 mail.sirket.com.tr."

    # PTR kayıtları (reverse DNS)
    local-data-ptr: "192.168.10.50 sirket.com.tr."
    local-data-ptr: "192.168.10.25 mail.sirket.com.tr."

    # Sadece iç ağda olan servisler
    local-zone: "internal.sirket.com.tr." static
    local-data: "jira.internal.sirket.com.tr. 3600 IN A 192.168.20.10"
    local-data: "confluence.internal.sirket.com.tr. 3600 IN A 192.168.20.11"
    local-data: "monitoring.internal.sirket.com.tr. 3600 IN A 192.168.20.20"
EOF

local-zone tiplerini anlamak önemli:

  • static: Sadece tanımladığınız kayıtlar döner, başka hiçbir şey. Tanımlamadığınız alt alan adları NXDOMAIN döner.
  • transparent: Tanımladığınız kayıtlar döner, tanımlamadıklarınız upstream’e sorulur. Kısmi override için kullanışlı.
  • redirect: Zone altındaki tüm sorgular belirttiğiniz hedefe yönlendirilir.
  • refuse: Zone için gelen sorguları reddeder.
  • deny: Zone için gelen sorgulara yanıt vermez (drop).

Stub Zone ile Yetkilendirme

Bazen iç DNS sunucunuzun bir zone için yetkili olmayan bir DNS sunucusuna (mesela Windows Active Directory DNS’i) yönlendirmesi gerekir. Bunun için stub-zone kullanırız:

cat > /etc/unbound/unbound.conf.d/stub-zones.conf << 'EOF'
# Active Directory domain'i için iç DNS'e yönlendir
stub-zone:
    name: "ad.sirket.com.tr."
    stub-addr: 192.168.1.10
    stub-addr: 192.168.1.11
    stub-first: no

# Ayrı bir datacenter'daki DNS'e yönlendir
stub-zone:
    name: "dc2.sirket.com.tr."
    stub-addr: 10.10.1.53
    stub-first: no
EOF

stub-first: no diyerek Unbound’un önce upstream’e sormadan direkt stub adresine gitmesini sağlıyoruz. Bu önemli, aksi takdirde beklenmedik davranışlar görebilirsiniz.

Forward Zone ile Seçici Yönlendirme

Belirli domain’ler için farklı upstream DNS kullanmak isteyebilirsiniz. Örneğin .local domain’leri için iç DNS’iniz varsa ya da belirli cloud provider domain’leri için özel DNS kullanmanız gerekiyorsa:

cat > /etc/unbound/unbound.conf.d/forward-zones.conf << 'EOF'
# Genel sorgular için upstream DNS (DoT ile)
forward-zone:
    name: "."
    forward-tls-upstream: yes
    forward-addr: 9.9.9.9@853#dns.quad9.net
    forward-addr: 149.112.112.112@853#dns.quad9.net

# Şirket VPN'inin DNS'i için özel yönlendirme
forward-zone:
    name: "vpn-partner.com."
    forward-addr: 10.200.1.53
    forward-first: no
EOF

Burada dikkat edin: sirket.com.tr için hem local-zone tanımı hem de forward-zone tanımı yaparsanız, local-zone öncelikli olur. Unbound önce local tanımlamalara bakar.

Ters Arama (Reverse DNS) Split Konfigürasyonu

İç ağ için PTR kayıtları da önemlidir. Özellikle monitoring araçları, log analizleri için. /etc/unbound/unbound.conf.d/reverse-zones.conf:

cat > /etc/unbound/unbound.conf.d/reverse-zones.conf << 'EOF'
server:
    # 192.168.10.0/24 bloğu için PTR kayıtları
    local-zone: "10.168.192.in-addr.arpa." static

    local-data: "50.10.168.192.in-addr.arpa. 3600 IN PTR sirket.com.tr."
    local-data: "25.10.168.192.in-addr.arpa. 3600 IN PTR mail.sirket.com.tr."
    local-data: "100.10.168.192.in-addr.arpa. 3600 IN PTR vpn.sirket.com.tr."
    local-data: "75.10.168.192.in-addr.arpa. 3600 IN PTR gitlab.sirket.com.tr."

    # 192.168.20.0/24 bloğu
    local-zone: "20.168.192.in-addr.arpa." static
    local-data: "10.20.168.192.in-addr.arpa. 3600 IN PTR jira.internal.sirket.com.tr."
    local-data: "11.20.168.192.in-addr.arpa. 3600 IN PTR confluence.internal.sirket.com.tr."
EOF

Yapılandırmayı Test Etme

Yapılandırmayı reload etmeden önce mutlaka test edin:

# Syntax kontrolü
unbound-checkconf /etc/unbound/unbound.conf

# Eğer her şey yolundaysa reload et
systemctl reload unbound
# veya
unbound-control reload

Test sorguları atmak için dig kullanın:

# İç IP'yi döndürüyor mu?
dig @127.0.0.1 sirket.com.tr A

# Yok dediğimiz kayıt gerçekten yok mu?
dig @127.0.0.1 yokboylesibir.sirket.com.tr A

# PTR sorgusu
dig @127.0.0.1 -x 192.168.10.50

# Mail sunucusu
dig @127.0.0.1 sirket.com.tr MX

Beklenen çıktı kontrolü için basit bir test scripti yazabilirsiniz:

#!/bin/bash
# /usr/local/bin/test-split-dns.sh

DNS_SERVER="127.0.0.1"
PASS=0
FAIL=0

check_dns() {
    local host=$1
    local expected=$2
    local result
    result=$(dig @${DNS_SERVER} +short "${host}" A 2>/dev/null | head -1)

    if [ "${result}" = "${expected}" ]; then
        echo "[PASS] ${host} -> ${result}"
        ((PASS++))
    else
        echo "[FAIL] ${host} -> beklenen: ${expected}, gelen: ${result}"
        ((FAIL++))
    fi
}

check_dns "sirket.com.tr" "192.168.10.50"
check_dns "www.sirket.com.tr" "192.168.10.50"
check_dns "mail.sirket.com.tr" "192.168.10.25"
check_dns "jira.internal.sirket.com.tr" "192.168.20.10"

echo ""
echo "Sonuc: ${PASS} basarili, ${FAIL} basarisiz"
chmod +x /usr/local/bin/test-split-dns.sh
/usr/local/bin/test-split-dns.sh

DNSSEC Dikkat Noktası

Local zone’larınız için DNSSEC imzalaması yapmıyorsanız (ki çoğu iç ağ kurulumunda yapılmaz), bunu Unbound’a söylemeniz gerekir. Aksi takdirde DNSSEC-aware istemciler sorun yaşayabilir:

cat >> /etc/unbound/unbound.conf.d/split-dns.conf << 'EOF'

    # DNSSEC validasyonu iç zone'larda hata vermesin
    domain-insecure: "sirket.com.tr."
    domain-insecure: "internal.sirket.com.tr."
    domain-insecure: "168.192.in-addr.arpa."
EOF

Unbound-control ile Canlı Yönetim

Unbound çalışırken unbound-control aracıyla çok şey yapabilirsiniz. Önce kontrol arayüzünü aktif edin:

# unbound-control için anahtar ve sertifika üret
unbound-control-setup

# base.conf'a ekleyin
cat >> /etc/unbound/unbound.conf.d/base.conf << 'EOF'

remote-control:
    control-enable: yes
    control-interface: 127.0.0.1
    control-port: 8953
    server-key-file: "/etc/unbound/unbound_server.key"
    server-cert-file: "/etc/unbound/unbound_server.pem"
    control-key-file: "/etc/unbound/unbound_control.key"
    control-cert-file: "/etc/unbound/unbound_control.pem"
EOF

systemctl restart unbound

Kullanışlı unbound-control komutları:

# Önbelleği temizle (bir domain'i test ederken çok işe yarar)
unbound-control flush sirket.com.tr
unbound-control flush_zone sirket.com.tr

# İstatistikleri gör
unbound-control stats_noreset

# Çalışan local zone'ları listele (debug için)
unbound-control list_local_zones

# Canlı olarak local data ekle (kalıcı değil, restart'ta gider)
unbound-control local_data "test.sirket.com.tr. 60 IN A 192.168.99.99"

# Eklediğini test et
dig @127.0.0.1 test.sirket.com.tr

# Sonra kaldır
unbound-control local_data_remove "test.sirket.com.tr."

Yaygın Sorunlar ve Çözümleri

Deneyimlerimden derlediğim, sık karşılaşılan sorunlar:

Sorun: Local zone tanımlıyken bazı kayıtlar çözümlenemiyor

static tip yerine transparent kullandığınızda tanımlamadığınız kayıtlar upstream’e sorulur. Ama static ile tüm kayıtları kendiniz tanımlamanız gerekir. Hangi tipte ne beklediğinizi net bilin.

Sorun: DNSSEC validation hatası

domain-insecure direktifini eklediğinizden emin olun. Özellikle harden-dnssec-stripped: yes açıkken iç zone’lar sorun çıkarabilir.

Sorun: Reload sonrası eski kayıtlar hala geliyor

İstemci tarafında önbellekleme olabilir. unbound-control flush_zone sirket.com.tr ile Unbound önbelleğini temizleyin, istemcide de ipconfig /flushdns (Windows) veya resolvectl flush-caches (Linux systemd-resolved) çalıştırın.

Sorun: Stub zone çalışmıyor, hala public DNS’e soruyor

stub-first: no ayarını kontrol edin. Ayrıca stub adresinin Unbound’dan erişilebilir olduğunu nc -u 192.168.1.10 53 ile doğrulayın.

Sonuç

Unbound ile split DNS, şirket içi DNS altyapısının olmazsa olmazlarından biri. Burada anlattıklarımı özetleyeyim:

  • local-zone ve local-data ile temel split DNS kurulumu düşündüğünüzden çok daha basit.
  • Konfigürasyonları ayrı dosyalara bölün, hem okunabilirlik hem de hata ayıklama açısından işinizi kolaylaştırır.
  • unbound-checkconf alışkanlığı edinin, production’a syntax hatasıyla gitmeyin.
  • unbound-control flush ve stats_noreset günlük operasyonların vazgeçilmezi.
  • DNSSEC ve domain-insecure ayarını gözden kaçırmayın.
  • Test scriptini CI/CD pipeline’ına ya da monitoring sisteminize entegre edin, yapılandırma değişikliklerinin regresyon testini otomatikleştirin.

Split DNS bir kez düzgün kurulduğunda artık “içeriden neden açılmıyor?” sorusunu duymayı bırakırsınız. Ve NAT loopback konfigürasyonlarına bir daha dönüp bakmak zorunda kalmazsınız.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir