Unbound ile DNSSEC Doğrulama Yapılandırması

DNS sorgularınızın gerçekten güvenilir kaynaklardan gelip gelmediğini hiç sorguladınız mı? Bir istemci DNS yanıtı aldığında, o yanıtın yolculuk boyunca manipüle edilmediğini nasıl anlarsınız? İşte tam bu noktada DNSSEC devreye giriyor ve Unbound, bu doğrulama sürecini sunucu tarafında en sağlıklı biçimde yönetebilen çözümlerden biri.

Bu yazıda Unbound üzerinde DNSSEC doğrulamasını sıfırdan yapılandıracağız. Teorik bilgiyi bir kenara bırakıp, gerçek üretim ortamlarında karşılaştığım senaryolar üzerinden ilerleyeceğiz.

DNSSEC Nedir ve Neden Unbound?

DNSSEC (DNS Security Extensions), DNS yanıtlarını dijital imzalarla doğrulayan bir güvenlik katmanıdır. Temelde iki şeyi garantiler: yanıtın gerçekten yetkili sunucudan geldiğini ve iletim sırasında değiştirilmediğini.

Peki neden Unbound? BIND veya dnsmasq de DNSSEC yapabilir, ancak Unbound baştan sona güvenli DNS çözümleyici (recursive resolver) olarak tasarlanmıştır. BIND gibi hem yetkili hem de çözümleyici olmaya çalışmaz. Bu tek amaçlılık, yapılandırmasını sadeleştirir ve saldırı yüzeyini küçültür.

Unbound’un DNSSEC ile çalışma mantığı şöyle özetlenebilir: İstemciden bir sorgu gelir, Unbound bu sorguyu kök sunuculara kadar izler, aldığı her yanıtı RRSIG (Resource Record Signature) kayıtlarıyla doğrular ve zincirin bütünlüğünü kontrol eder. Bu zincirin başlangıç noktası ise trust anchor olarak adlandırdığımız kök güven anahtarıdır.

Kurulum

Çoğu modern dağıtımda Unbound paket depolarında hazır gelir. Ama versiyon önemli, özellikle DNSSEC desteği açısından 1.6.x ve üstünü kullanmanızı öneririm.

Debian/Ubuntu:

apt update && apt install unbound unbound-anchor -y

RHEL/CentOS/AlmaLinux:

dnf install unbound -y

FreeBSD:

pkg install unbound

Kurulumdan sonra versiyon kontrolü yapın:

unbound -V

Çıktıda DNSSEC desteğinin etkin olup olmadığını görebilirsiniz. Eğer paket kaynaklı kurulum yaptıysanız genellikle zaten etkindir.

Trust Anchor: Güven Zincirimizin Temeli

DNSSEC doğrulamasının kalbi, kök zone için tanımladığımız trust anchor’dır. Bu anahtar olmadan Unbound, imzaları doğrulayacak referans noktasına sahip olmaz.

unbound-anchor aracı bu anahtarı otomatik olarak çeker ve günceller:

unbound-anchor -a /var/lib/unbound/root.key

Bu komut /var/lib/unbound/root.key dosyasını oluşturur ya da günceller. Dosyanın içeriğine bakalım:

cat /var/lib/unbound/root.key

Çıktıda şuna benzer bir şey görmelisiniz:

. IN DS 20326 8 2 E06D44B980783A31...

Bu DS kaydı, ICANN tarafından yönetilen kök zone’un KSK (Key Signing Key) özetidir. 2017’de yapılan kök zone KSK rollovers’tan bu yana bu değer güncel tutulmalıdır.

Kritik nokta: Bu dosyanın unbound kullanıcısı tarafından okunabilir ve yazılabilir olması gerekir. Unbound, RFC 5011 mekanizmasıyla bu anahtarı otomatik günceller, bu yüzden yazma izni şart.

chown unbound:unbound /var/lib/unbound/root.key
chmod 644 /var/lib/unbound/root.key

Ana Yapılandırma

Şimdi /etc/unbound/unbound.conf dosyasına geçelim. Temel bir DNSSEC yapılandırmasından başlayıp üzerine ekleyeceğiz.

cat > /etc/unbound/unbound.conf << 'EOF'
server:
    # Dinleme adresi ve port
    interface: 0.0.0.0
    port: 53
    
    # IPv4 ve IPv6
    do-ip4: yes
    do-ip6: yes
    do-udp: yes
    do-tcp: yes
    
    # Erişim kontrolü
    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: 0.0.0.0/0 refuse
    
    # DNSSEC yapılandırması
    auto-trust-anchor-file: "/var/lib/unbound/root.key"
    val-log-level: 1
    
    # Güvenlik seçenekleri
    harden-glue: yes
    harden-dnssec-stripped: yes
    harden-below-nxdomain: yes
    harden-referral-path: no
    
    # QNAME minimization (RFC 7816)
    qname-minimisation: yes
    
    # Cache ayarları
    cache-min-ttl: 300
    cache-max-ttl: 86400
    msg-cache-size: 128m
    rrset-cache-size: 256m
    
    # Log
    logfile: "/var/log/unbound/unbound.log"
    log-queries: no
    log-replies: no
    verbosity: 1
    
    # Chroot
    chroot: ""
    username: "unbound"
    
    # Root hints
    root-hints: "/var/lib/unbound/root.hints"

EOF

Birkaç parametre üzerinde duralım:

harden-dnssec-stripped: Bu bence en önemli seçenek. Bir yanıt DNSSEC imzasız gelirse ama zone DNSSEC destekliyorsa, Unbound bu yanıtı reddeder. Ortadaki adam saldırısına karşı temel savunma mekanizmasıdır.

harden-below-nxdomain: NXDOMAIN yanıtlarının alt alanlarını da kapsayacak şekilde sertleştirir. DNS rebinding saldırılarına karşı koruma sağlar.

qname-minimisation: RFC 7816’ya uygun olarak, tam alan adını üst sunuculara göndermek yerine minimum gerekli bilgiyi gönderir. Gizlilik açısından önemli.

val-log-level: 1 değeri başarısız DNSSEC doğrulama girişimlerini loglar. Hata ayıklama için değeri 2’ye çıkarabilirsiniz ama production’da 1 yeterli.

Root Hints Güncelleme

Kök sunucu listesini de güncel tutmak gerekir:

wget -O /var/lib/unbound/root.hints https://www.internic.net/domain/named.root

Bunu bir cron job’a ekleyin, ayda bir yeterli:

echo "0 0 1 * * root wget -q -O /var/lib/unbound/root.hints https://www.internic.net/domain/named.root && systemctl reload unbound" > /etc/cron.d/update-root-hints

Yapılandırmayı Test Etme

Servisi başlatmadan önce yapılandırma dosyasını kontrol edin:

unbound-checkconf /etc/unbound/unbound.conf

Hatasız çıktı aldıysanız servisi başlatın:

systemctl enable --now unbound
systemctl status unbound

Şimdi DNSSEC doğrulamasının çalışıp çalışmadığını test edelim. Bunun için dig komutunun +dnssec ve +cd bayraklarını kullanacağız:

# DNSSEC imzalı bir zone sorgusu (sigok.uri.arpa test zone'u)
dig @127.0.0.1 sigok.uri.arpa A +dnssec

# AD bayrağını kontrol et
dig @127.0.0.1 google.com A +dnssec | grep -E "^;; flags"

Çıktıdaki flags satırında ad (Authenticated Data) bayrağını görmelisiniz:

;; flags: qr rd ra ad; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

ad bayrağı, yanıtın DNSSEC ile başarıyla doğrulandığını gösterir.

Şimdi bir de kasıtlı olarak bozuk DNSSEC’e sahip test domain’ini deneyelim:

dig @127.0.0.1 dnssec-failed.org A

Bu sorgu SERVFAIL döndürmeli. Döndürüyorsa DNSSEC doğrulama düzgün çalışıyor demektir. Bu domain özellikle DNSSEC doğrulama testleri için tasarlanmıştır.

Gerçek Dünya Senaryosu: Split-DNS ile DNSSEC

Üretim ortamlarında sıkça karşılaşılan bir durum: iç ağda özel domain’leriniz var ve bunlar için yetkili sunucunuz ayrı. Bu durumda DNSSEC doğrulamasını iç zone’lar için devre dışı bırakmak gerekebilir ya da iç CA’dan imzalanmış zone’ları yapılandırmanız gerekir.

Diyelim ki internal.sirket.com zone’unuz için ayrı bir yetkili DNS sunucunuz var ve bu zone DNSSEC imzalı değil:

# /etc/unbound/unbound.conf.d/internal.conf

server:
    # internal zone için DNSSEC doğrulamasını atla
    domain-insecure: "internal.sirket.com"

stub-zone:
    name: "internal.sirket.com"
    stub-addr: 10.10.1.53
    stub-prime: no

domain-insecure direktifi, belirtilen zone için DNSSEC doğrulamasını atlar. Bu zone’dan gelen yanıtlar ad bayrağı taşımaz ama SERVFAIL da dönmez.

Daha güvenli bir alternatif: Eğer iç zone’unuzu DNSSEC ile imzalayabildiyseniz, trust anchor’ı Unbound’a tanıtın:

server:
    trust-anchor: "internal.sirket.com. DS 12345 8 2 ABCDEF1234..."

Bu yaklaşım zincirin kopmaması açısından daha sağlıklıdır, ancak iç CA ve zone signing altyapısı gerektirir.

Negative Trust Anchors (NTA)

Bazen bir zone’un DNSSEC yapılandırması bozuk olabilir ama erişmeniz gerekir. Örneğin bir iş ortağınızın domain’i geçici olarak bozuk DNSSEC imzasıyla yayın yapıyor. Bu durumda NTA mekanizmasını kullanabilirsiniz:

# Geçici NTA ekle (runtime, restart'ta sıfırlanır)
unbound-control insecure_add ortakfirma.com

# Kalıcı NTA için yapılandırma dosyasına ekle
# /etc/unbound/unbound.conf.d/nta.conf
server:
    domain-insecure: "ortakfirma.com"

Önemli not: NTA’ları geçici çözüm olarak kullanın. Sorunu çözdükten sonra kaldırın. Bir zone için kalıcı NTA tanımlamak, o zone’daki tüm DNSSEC korumasını ortadan kaldırır.

DNSSEC Doğrulama Loglarını Anlama

val-log-level: 2 ile detaylı loglama açıkken şuna benzer girişler görebilirsiniz:

[1234567890] unbound[1234:0] info: validation failure <example.com. A IN>: signatures expired
[1234567890] unbound[1234:0] info: RRSIG expired

Bu logları takip etmek için basit bir izleme komutu:

tail -f /var/log/unbound/unbound.log | grep -E "(validation failure|BOGUS|SERVFAIL)"

Sık karşılaşılan DNSSEC hata mesajları:

  • signatures expired: Zone’un RRSIG kayıtları süresi dolmuş. Zone operatörünün yeniden imzalaması gerekiyor.
  • no signatures: DNSSEC imzası beklenirken bulunamadı. Ortadaki kişi imzaları silmiş olabilir.
  • DNSKEY missing: İmzayı doğrulamak için gerekli DNSKEY kaydı yok.
  • DS record not found: Üst zone’dan gelen delegation imzası eksik.

unbound-control ile Yönetim

Unbound’un remote control arayüzü production’da hayat kurtarır. Önce etkinleştirin:

# Sertifikaları oluştur
unbound-control-setup

Bu komut /etc/unbound/ altında unbound_control.key, unbound_control.pem, unbound_server.key, unbound_server.pem dosyalarını oluşturur.

Yapılandırmaya ekleyin:

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"

Artık şu komutları kullanabilirsiniz:

# Servis durumu
unbound-control status

# İstatistikler (DNSSEC doğrulama sayıları dahil)
unbound-control stats_noreset | grep dnssec

# Cache'i temizle
unbound-control flush_zone example.com

# Yapılandırmayı yeniden yükle (restart gerekmez)
unbound-control reload

# Belirli bir kaydı sorgula ve cache'e bak
unbound-control lookup example.com

stats_noreset çıktısındaki DNSSEC ile ilgili sayaçlar:

unbound-control stats_noreset | grep -i dnssec

Burada num.dnssec.bogus değeri, başarısız DNSSEC doğrulama sayısını gösterir. Bu değer sıfıra yakın olmalı, eğer anormalce yüksekse araştırmanız gerekir.

Performans Ayarları

DNSSEC doğrulama, ek kriptografik işlem demektir. Cache boyutlarını artırmak bu yükü azaltır:

server:
    # Her RRset için ayrı cache
    rrset-cache-size: 256m
    msg-cache-size: 128m
    
    # Prefetch: TTL dolmadan önce popüler kayıtları yenile
    prefetch: yes
    prefetch-key: yes
    
    # Thread sayısı (CPU core sayısına eşit ayarlayın)
    num-threads: 4
    
    # Her thread için buffer
    outgoing-range: 4096
    num-queries-per-thread: 4096
    
    # So-reuseport kernel desteği varsa etkinleştir
    so-reuseport: yes

prefetch-key: DNSKEY kayıtlarını TTL dolmadan önce yeniler. Bu sayede imza doğrulaması için her seferinde yeniden sorgu yapılmaz ve gecikme azalır.

Monitoring ve Alerting

Production’da DNSSEC doğrulama başarısızlıklarını izlemek için basit bir script:

#!/bin/bash
# /usr/local/bin/check_dnssec.sh

RESOLVER="127.0.0.1"
TEST_DOMAIN="sigok.uri.arpa"
FAIL_DOMAIN="dnssec-failed.org"
ALERT_MAIL="[email protected]"

# Başarılı DNSSEC testi
RESULT=$(dig @$RESOLVER $TEST_DOMAIN A +dnssec +short 2>/dev/null)
if [ -z "$RESULT" ]; then
    echo "KRITIK: DNSSEC dogrulama calismıyor - $TEST_DOMAIN cevaplanmiyor" | 
        mail -s "Unbound DNSSEC Hatasi" $ALERT_MAIL
    exit 2
fi

# AD bayragi kontrolu
FLAGS=$(dig @$RESOLVER $TEST_DOMAIN A +dnssec 2>/dev/null | grep "^;; flags")
if ! echo "$FLAGS" | grep -q " ad"; then
    echo "UYARI: AD bayragi bulunamadi - DNSSEC dogrulama devre disi?" | 
        mail -s "Unbound DNSSEC Uyarisi" $ALERT_MAIL
    exit 1
fi

# Bogus domain SERVFAIL donmeli
BOGUS=$(dig @$RESOLVER $FAIL_DOMAIN A +short 2>&1)
STATUS=$(dig @$RESOLVER $FAIL_DOMAIN A 2>/dev/null | grep "^;; ->>HEADER" | grep -o "SERVFAIL")
if [ "$STATUS" != "SERVFAIL" ]; then
    echo "KRITIK: Bogus DNSSEC dogrulama basarisiz - $FAIL_DOMAIN SERVFAIL donmuyor!" | 
        mail -s "Unbound DNSSEC Kritik Hata" $ALERT_MAIL
    exit 2
fi

echo "OK: DNSSEC dogrulama normal calisiyor"
exit 0

Bu scripti cron’a ekleyin:

*/5 * * * * root /usr/local/bin/check_dnssec.sh >> /var/log/dnssec_check.log 2>&1

Yaygın Sorunlar ve Çözümleri

Sorun: Bazı domain’ler SERVFAIL dönüyor ama DNSSEC bozuk değil.

Sebebi çoğunlukla sistem saatidir. DNSSEC imzaları zaman damgasına dayanır, sunucu saati yanlışsa tüm doğrulama bozulur.

# NTP senkronizasyonunu kontrol et
timedatectl status
chronyc tracking

Sorun: harden-dnssec-stripped aktifken bazı kurumsal uygulamalar çalışmıyor.

Bazı eski veya kötü yapılandırılmış zone’lar DNSSEC desteğini yarım bırakmış. domain-insecure direktifiyle bu zone’ları kapsama dışı bırakın, ancak bunu kalıcı çözüm olarak değil geçici çözüm olarak düşünün.

Sorun: unbound-anchor trust anchor dosyasını güncelleyemiyor.

Dosya izinlerini kontrol edin. Unbound kullanıcısının bu dosyaya yazma izni olmalı:

ls -la /var/lib/unbound/root.key
chown unbound:unbound /var/lib/unbound/root.key
chmod 644 /var/lib/unbound/root.key

Sonuç

Unbound üzerinde DNSSEC doğrulaması, DNS altyapınıza ekleyebileceğiniz en değerli güvenlik katmanlarından biri. Yapılandırması görece basit, bakım maliyeti düşük ve sağladığı koruma ciddi.

Özetle dikkat etmeniz gereken kritik noktalar:

  • Trust anchor dosyasının güncel ve doğru izinlerde olduğundan emin olun
  • harden-dnssec-stripped her zaman açık tutun
  • NTP senkronizasyonunu ihmal etmeyin, DNSSEC için saat doğruluğu şarttır
  • unbound-control stats ile periyodik olarak bogus sayacını takip edin
  • İç zone’lar için domain-insecure kullanmak yerine mümkünse o zone’ları da imzalamayı tercih edin
  • Monitoring scriptini mutlaka kurun ve hem pozitif hem negatif test edin

DNS güvenliğini katmanlı düşünmek gerekiyor: DNSSEC kayıt manipülasyonunu engeller, DoT/DoH ise dinlemeyi engeller. İkisini birlikte kullandığınızda sağlam bir DNS güvenlik mimarisi elde edersiniz. Ama bu başka bir yazının konusu.

Bir yanıt yazın

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