Unbound DNS Güvenlik Sertleştirme: En İyi Pratikler

Production ortamında Unbound çalıştırıyorsanız ve güvenlik sertleştirmesini ertelediyseniz, bu yazı tam size göre. Kurulum belgelerinde genellikle “çalışıyor mu, iyi o zaman geç” yaklaşımı hakimdir. Ama açık bir recursive resolver, yanlış yapılandırılmış bir cache, ya da DNSSEC doğrulaması olmayan bir sunucu, kurumunuz için ciddi bir saldırı yüzeyi demektir. Hadi işin teknik kısmına girelim.

Unbound’un Varsayılan Durumu Neden Yetersiz

Unbound, kurulum sonrası makul bir başlangıç noktası sunar. Ama “makul başlangıç noktası” ile “production güvenliği” arasındaki mesafe oldukça büyüktür. Varsayılan konfigürasyon şunları yapmaz:

  • Dış dünyadan gelen sorguları engellemez
  • Query rate limiting uygulamaz
  • DNSSEC validasyonunu tam anlamıyla zorlamaz
  • Gereksiz protokol özelliklerini devre dışı bırakmaz
  • Erişim loglarını ayrıntılı tutmaz

Bir DNS amplification saldırısı düşünün: sunucunuz açık recursive resolver olarak internete bakıyorsa, saldırganlar UDP tabanlı sorgular aracılığıyla trafik hacmini 50-70 katına çıkarabilir. Bant genişliğinizi tüketirler, siz de farkında bile olmayabilirsiniz.

Temel Yapılandırma Dosyası Organizasyonu

Güvenlik odaklı bir Unbound kurulumunda her şeyi tek bir dosyaya koymak yerine modüler yapı kullanmak yönetimi kolaylaştırır. /etc/unbound/ altında aşağıdaki yapıyı oluşturalım:

mkdir -p /etc/unbound/conf.d
mkdir -p /etc/unbound/zones
chmod 750 /etc/unbound/conf.d
chown -R unbound:unbound /etc/unbound/

Ana unbound.conf dosyasına sadece include direktifleri koyun:

cat > /etc/unbound/unbound.conf << 'EOF'
include: "/etc/unbound/conf.d/server.conf"
include: "/etc/unbound/conf.d/access-control.conf"
include: "/etc/unbound/conf.d/hardening.conf"
include: "/etc/unbound/conf.d/dnssec.conf"
include: "/etc/unbound/conf.d/logging.conf"
EOF

Bu yapı sayesinde bir junior’a erişim kontrolünü güncellemesini söylediğinizde, yanlışlıkla DNSSEC ayarlarını bozma riski minimuma iner.

Erişim Kontrolü: Kimin Sorgu Yapabileceğini Belirleyin

Bu adım güvenlik sertleştirmenin belkemiğidir. /etc/unbound/conf.d/access-control.conf dosyasını oluşturun:

cat > /etc/unbound/conf.d/access-control.conf << 'EOF'
server:
    # Varsayılan: herkesi reddet
    access-control: 0.0.0.0/0 refuse
    access-control: ::/0 refuse

    # Localhost her zaman izinli
    access-control: 127.0.0.1/32 allow
    access-control: ::1/128 allow

    # İç ağ segmentleri
    access-control: 10.0.0.0/8 allow
    access-control: 172.16.0.0/12 allow
    access-control: 192.168.0.0/16 allow

    # Sadece belirli VLAN'lar için recursive sorgu izni
    access-control: 10.10.5.0/24 allow_snoop

    # Yönetim ağı için tam erişim
    access-control: 10.255.0.0/24 allow
EOF

Burada dikkat edilmesi gereken nokta: allow_snoop yalnızca güvendiğiniz yönetim makinelerine verilmeli. Bu seçenek, dig +trace gibi debugging araçlarının çalışmasını sağlar ama aynı zamanda cache poisoning vektörü de olabilir.

refuse ile deny arasındaki fark önemlidir:

  • refuse: REFUSED cevabı döner, istemci anlar
  • deny: Hiç cevap vermez, istemci timeout bekler
  • allow: Normal recursive çözümleme yapar
  • allow_snoop: iterative sorgulara da izin verir

Dış dünyadan gelen sorguları deny ile düşürmenizi öneririm. refuse yanıtı bile saldırgana sunucunuzun var olduğunu söyler.

Sertleştirme Parametreleri: Asıl İş Burada Başlıyor

/etc/unbound/conf.d/hardening.conf dosyasını hazırlayalım. Bu dosya, DNS protokolünün bilinen zayıflıklarına karşı savunma katmanlarını içerir:

cat > /etc/unbound/conf.d/hardening.conf << 'EOF'
server:
    # Cache Poisoning Koruması
    # Randomize kaynak port - Kaminsky saldırısına karşı kritik
    use-caps-for-id: yes
    harden-glue: yes
    harden-dnssec-stripped: yes
    harden-below-nxdomain: yes
    harden-referral-path: yes
    harden-algo-downgrade: yes
    harden-short-bufsize: yes
    harden-large-queries: yes

    # Unwanted Reply Koruması
    unwanted-reply-threshold: 10000000

    # Amplification Saldırılarına Karşı
    minimal-responses: yes

    # DNS Rebinding Koruması
    private-address: 10.0.0.0/8
    private-address: 172.16.0.0/12
    private-address: 192.168.0.0/16
    private-address: 169.254.0.0/16
    private-address: fd00::/8
    private-address: fe80::/10

    # Zehirleme denemelerini bastır
    val-clean-additional: yes

    # Priv Separation
    username: "unbound"
    chroot: "/var/lib/unbound"
    directory: "/var/lib/unbound"

    # Gereksiz servisleri kapat
    hide-identity: yes
    hide-version: yes
    hide-trustanchor: yes

    # Rate Limiting
    ratelimit: 1000
    ratelimit-slip: 2
    ip-ratelimit: 200
EOF

harden-glue ve harden-dnssec-stripped ikisi birlikte çok önemli. Biri NS kayıtlarıyla gelen ek bilgilerin doğrulanmasını zorlarken, diğeri DNSSEC imzası olmayan yanıtları reddeder. Bir araştırma projemde bu ikisi olmadan test ettiğimizde, manipüle edilmiş glue kayıtlarıyla cache’i kirletmek sadece birkaç dakika aldı.

hide-identity ve hide-version parametreleri basit ama etkili. Bir saldırgan dig chaos txt id.server @ns.example.com komutuyla sunucunuz hakkında bilgi toplamaya çalıştığında boş elle döner.

Rate Limiting: Hem Kendinizi Hem Başkasını Koruyun

Rate limiting hem DDoS kurbanı olmaktan sizi korur, hem de sunucunuzun saldırı aracı olarak kullanılmasını engeller. Biraz daha granüler bir yapı için:

cat >> /etc/unbound/conf.d/hardening.conf << 'EOF'

    # IP bazlı rate limiting (saniye başına sorgu)
    ip-ratelimit: 100
    ip-ratelimit-factor: 10
    ip-ratelimit-size: 4m

    # Domain bazlı rate limiting
    ratelimit: 1000
    ratelimit-size: 4m
    ratelimit-factor: 10

    # Yavaş istemcilere yönelik
    ratelimit-for-domain: malware-domain.example. 0
EOF

Gerçek bir olaydan bahsedeyim: Bir müşteri ortamında, çalışanların bilgisayarlarına bulaşan zararlı yazılım her 5 saniyede bir belirli bir domain’e sorgu gönderiyordu. Rate limiting olmadan bu durum DNS sunucusunu kilitledi. ratelimit-for-domain ile o domain için limit 0 yapılınca, zararlı yazılım iletişimi kesildi ve endpoint’leri tespit etmek kolaylaştı.

DNSSEC Yapılandırması

DNSSEC, DNS cevaplarının bütünlüğünü kriptografik olarak doğrular. Unbound zaten DNSSEC capable gelir ama doğru yapılandırılması gerekir:

cat > /etc/unbound/conf.d/dnssec.conf << 'EOF'
server:
    # Trust anchor otomatik güncelleme
    auto-trust-anchor-file: "/var/lib/unbound/root.key"

    # DNSSEC doğrulama aktif
    val-permissive-mode: no

    # Bogus (doğrulanamamış) yanıtları reddet
    val-log-level: 1

    # NTA (Negative Trust Anchor) - geçici istisnalar için
    # val-override-date: -1  # Test için, productionda kullanmayın

    # DNSSEC olmayan domainler için davranış
    harden-dnssec-stripped: yes

    # Algoritma güvenlik eşiği
    harden-algo-downgrade: yes
EOF

# Trust anchor dosyasını oluştur
unbound-anchor -a /var/lib/unbound/root.key
chown unbound:unbound /var/lib/unbound/root.key

val-permissive-mode: no kritik. Bu parametre yes olduğunda DNSSEC hataları sadece loglanır ama sorgular yine de yanıtlanır. Yani koruma devre dışıdır. Hayret edecek kadar fazla kurulumda bunu yes olarak görmüşümdür.

Trust anchor dosyasını cron ile güncel tutmayı unutmayın:

echo "0 2 * * * unbound /usr/sbin/unbound-anchor -a /var/lib/unbound/root.key && /usr/sbin/unbound-control reload" | crontab -u root -

Logging Yapılandırması: Göremediğinizi Yönetemezsiniz

Güvenlik logları olmadan bir saldırıyı ne tespit edebilirsiniz ne de forensics yapabilirsiniz:

cat > /etc/unbound/conf.d/logging.conf << 'EOF'
server:
    # Log hedefi
    logfile: "/var/log/unbound/unbound.log"

    # Log seviyesi: 0=hata, 1=bilgi, 2=detaylı, 3=debug
    verbosity: 1

    # Sorgu loglaması (dikkat: yoğun ortamlarda disk doldurabilir)
    log-queries: yes
    log-replies: yes
    log-tag-queryreply: yes

    # Zaman damgası formatı
    log-time-ascii: yes

    # DNSSEC başarısızlıklarını logla
    val-log-level: 2

    # Rate limit aşımlarını logla
    ratelimit-log-only: no
EOF

mkdir -p /var/log/unbound
chown unbound:unbound /var/log/unbound

Log rotation için /etc/logrotate.d/unbound dosyası:

cat > /etc/logrotate.d/unbound << 'EOF'
/var/log/unbound/unbound.log {
    daily
    rotate 30
    compress
    delaycompress
    missingok
    notifempty
    postrotate
        /usr/sbin/unbound-control log_reopen 2>/dev/null || true
    endscript
}
EOF

log-queries: yes üretim ortamında çok ciddi I/O yükü yaratabilir. Günlük 10 milyon sorgu yapan bir ortamda bu seçeneği açtığınızda log dosyaları hızla gigabaytlara ulaşır. SIEM’e göndereceğiniz kritik olaylar için filtreli loglama tercih edin ya da örnekleme yapın.

Remote Control Güvenliği

unbound-control aracı çok kullanışlı ama güvenli yapılandırılmaz ise risk oluşturur:

# Control interface için sertifika oluştur
unbound-control-setup

cat >> /etc/unbound/conf.d/server.conf << 'EOF'
remote-control:
    control-enable: yes
    # Sadece localhost dinle, asla 0.0.0.0 yapma
    control-interface: 127.0.0.1
    control-interface: ::1
    control-port: 8953
    control-use-cert: yes
    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

control-interface değerini asla 0.0.0.0 yapmayın. Kulağa saçma geliyor ama bunu yapan sistemler gördüm. Sonuç: herkes cache’i temizleyebilir, blacklist ekleyebilir, servisi yeniden başlatabilir.

Servis Düzeyinde İzolasyon

Systemd ile Unbound’u sandbox içinde çalıştırmak ek bir güvenlik katmanı sağlar:

mkdir -p /etc/systemd/system/unbound.service.d/

cat > /etc/systemd/system/unbound.service.d/hardening.conf << 'EOF'
[Service]
# Dosya sistemi kısıtlamaları
PrivateTmp=yes
ProtectSystem=strict
ProtectHome=yes
ReadWritePaths=/var/lib/unbound /var/log/unbound /run/unbound

# Ağ kısıtlamaları
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
IPAddressDeny=any
IPAddressAllow=localhost
IPAddressAllow=10.0.0.0/8
IPAddressAllow=172.16.0.0/12
IPAddressAllow=192.168.0.0/16

# Sistem çağrısı filtresi
SystemCallFilter=@system-service
SystemCallFilter=~@privileged @resources

# Capability sınırlaması
CapabilityBoundingSet=CAP_NET_BIND_SERVICE CAP_SETUID CAP_SETGID
AmbientCapabilities=CAP_NET_BIND_SERVICE

# Diğer kısıtlamalar
NoNewPrivileges=yes
PrivateDevices=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes
RestrictNamespaces=yes
LockPersonality=yes
MemoryDenyWriteExecute=yes
EOF

systemctl daemon-reload
systemctl restart unbound

Firewall Kuralları ile Tamamlama

Uygulama katmanı güvenliğini network katmanıyla destekleyin. UFW veya iptables kullanıyorsanız:

# Sadece iç ağdan DNS sorgusuna izin ver
iptables -A INPUT -p udp --dport 53 -s 10.0.0.0/8 -j ACCEPT
iptables -A INPUT -p tcp --dport 53 -s 10.0.0.0/8 -j ACCEPT
iptables -A INPUT -p udp --dport 53 -j DROP
iptables -A INPUT -p tcp --dport 53 -j DROP

# Rate limiting - IP başına saniyede max 20 DNS paketi
iptables -A INPUT -p udp --dport 53 -m hashlimit 
    --hashlimit-above 20/sec 
    --hashlimit-burst 50 
    --hashlimit-mode srcip 
    --hashlimit-name dns_limit 
    -j DROP

# Control interface sadece localhost
iptables -A INPUT -p tcp --dport 8953 ! -s 127.0.0.1 -j DROP

Firewall kurallarını kalıcı hale getirmeyi unutmayın. iptables-save > /etc/iptables/rules.v4 veya kullandığınız dağıtımın yöntemiyle.

Yapılandırmayı Doğrulama

Her değişiklikten sonra:

# Sözdizimi kontrolü
unbound-checkconf /etc/unbound/unbound.conf

# Servisi yeniden başlat
systemctl restart unbound

# DNSSEC çalışıyor mu?
dig +dnssec sigok.verteiltesysteme.net @127.0.0.1 | grep -E "RRSIG|ad"

# Bogus domain reddediliyor mu? (SERVFAIL bekliyoruz)
dig sigfail.verteiltesysteme.net @127.0.0.1

# Dışarıdan erişim engelleniyor mu?
nmap -sU -p 53 --script dns-recursion <SUNUCU_IP>

# Rate limiting çalışıyor mu?
for i in $(seq 1 200); do dig google.com @127.0.0.1 +short; done | tail -20

sigok.verteiltesysteme.net için RRSIG kaydı ve ad (Authenticated Data) flag’i görmelisiniz. sigfail.verteiltesysteme.net için ise SERVFAIL dönmeli. Bu iki test, DNSSEC’in gerçekten çalıştığını doğrular.

Düzenli Güvenlik Denetimleri

Sertleştirme tek seferlik bir iş değil. Şu kontrolleri periyodik yapın:

  • unbound-control stats ile query rate trendlerini takip edin
  • Log dosyalarında yüksek frekanslı SERVFAIL veya REFUSED yanıtlarına bakın
  • Trust anchor dosyasının güncel olduğunu doğrulayın
  • unbound-control dump_cache | wc -l ile cache boyutunu izleyin
  • Yeni Unbound sürümlerini takip edin, güvenlik yamaları kritik olabilir

Anomali tespiti için basit bir bash scripti:

#!/bin/bash
# /usr/local/bin/unbound-audit.sh

LOGFILE="/var/log/unbound/unbound.log"
THRESHOLD=1000

# Son 1 saatteki SERVFAIL sayısını say
SERVFAIL_COUNT=$(grep -c "SERVFAIL" "$LOGFILE" 2>/dev/null || echo 0)

if [ "$SERVFAIL_COUNT" -gt "$THRESHOLD" ]; then
    echo "UYARI: Son 1 saatte $SERVFAIL_COUNT SERVFAIL tespit edildi!" | 
        mail -s "Unbound Güvenlik Uyarısı" [email protected]
fi

# Cache hitrate kontrolü
STATS=$(unbound-control stats_noreset)
TOTAL=$(echo "$STATS" | grep "total.num.queries=" | cut -d= -f2)
CACHE_HITS=$(echo "$STATS" | grep "total.num.cachehits=" | cut -d= -f2)

echo "$(date): Toplam sorgu: $TOTAL, Cache hit: $CACHE_HITS"

Sonuç

Unbound güvenlik sertleştirmesi katmanlı bir yaklaşım gerektirir. Erişim kontrolü ile başladınız, rate limiting eklediniz, DNSSEC’i doğru yapılandırdınız, servisi izole ettiniz ve ağ katmanında da kapıları kapattınız. Bunların hepsi birlikte çalıştığında, open resolver açığı, cache poisoning girişimi veya amplification saldırısı gibi DNS’e yönelik yaygın tehditlere karşı güçlü bir savunma hattı oluşturmuş olursunuz.

Şunu da eklemeliyim: bu ayarları uyguladıktan sonra kendi iç kullanıcılarınızdan şikayet gelmesi olasıdır. Özellikle harden-dnssec-stripped açık olduğunda, kötü imzalı bazı domainlere erişimde sorun yaşanabilir. Bu şikayetleri “sorun” değil “sistem doğru çalışıyor” olarak değerlendirin. DNSSEC problemi olan domain sahipleri bunu düzeltmeli, siz güvenliği gevşetmemelisiniz. Geçici istisna için val-override-date veya NTA mekanizmasını kullanabilirsiniz ama bu istisnaları belgeleyip düzenli gözden geçirin.

Bir yanıt yazın

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