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 -lile 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.
