Unbound DNS Sunucusunda Rate Limiting ile DDoS Koruması
DNS altyapısına yönelik saldırılar giderek daha sofistike hale geliyor. Geçen yıl bir müşterimizin DNS sunucusu sabahın 3’ünde dakikada 50.000 sorgu alan bir amplifikasyon saldırısıyla çöktü. Unbound çalışıyordu ama hiçbir rate limiting yapılandırılmamıştı. O gece birlikte oturup konfigürasyonu sıfırdan yazdık ve o tarihten bu yana aynı sunucu hiç sallanmadı. Bu yazıda o deneyimden öğrendiklerimi paylaşacağım.
Unbound’da Rate Limiting Neden Gereklidir?
DNS sunucuları doğası gereği UDP tabanlı çalışır ve kaynak doğrulama mekanizması zayıftır. Bu iki özellik bir araya gelince DNS amplifikasyon saldırıları için mükemmel bir zemin oluşur. Saldırgan kaynak IP’yi sahteleyerek (spoofing) sunucunuza küçük sorgular yollar, sunucunuz ise büyük yanıtlar üretir ve bu yanıtları hedef sisteme gönderir. Hem bant genişliğinizi tüketir hem de sunucunuzu felç eder.
Unbound bu sorunla başa çıkmak için üç farklı mekanizma sunar:
- ratelimit: Belirli bir alan adı için saniyede işlenecek maksimum sorgu sayısını sınırlar
- ip-ratelimit: Tek bir IP adresinden gelen sorguları sınırlar
- Sorgulama hedefi bazlı sınırlama: Hangi alan adlarına ne kadar sorgu gönderileceğini kontrol eder
Bu mekanizmaları doğru yapılandırmak hem meşru trafiği korumak hem de saldırıları engellemek arasında hassas bir denge kurmayı gerektirir.
Temel Rate Limiting Yapılandırması
Unbound’un ana konfigürasyon dosyası genellikle /etc/unbound/unbound.conf yolunda bulunur. Rate limiting direktiflerini server: bloğu içine eklemeniz gerekir.
Temel bir yapılandırmayla başlayalım:
server:
# Genel rate limiting - saniyede maksimum sorgu sayısı
ratelimit: 1000
# Rate limit aşıldığında loglama
ratelimit-log-factor: 5
# Slip değeri - sınır aşıldığında kaç sorguda bir SLIP yanıtı
ratelimit-slip: 2
# IP başına rate limit
ip-ratelimit: 100
# IP rate limit slip değeri
ip-ratelimit-slip: 2
# Cache boyutu
ratelimit-size: 4m
ip-ratelimit-size: 4m
Buradaki ratelimit: 1000 değeri şu anlama gelir: Unbound, aynı hedef alan adı için saniyede 1000’den fazla sorguyu reddeder. ratelimit-slip: 2 ise her 2 sorguda bir SERVFAIL yerine anlık yanıt döner, bu da meşru istemcilerin bir kısmının yine de yanıt almasını sağlar.
SLIP Mekanizması ve Neden Önemlidir?
SLIP değeri çoğu zaman yanlış anlaşılan bir parametredir. Değeri 0 olarak ayarlarsanız sınırı aşan tüm sorgular sessizce düşürülür. Bu agresif ama kaynakları korur. Değeri 1 yaparsanız her sorguya SERVFAIL döner, bu ise saldırganın saldırıyı ölçmesini kolaylaştırır. 2 ile 10 arasındaki değerler ise en iyi dengeyi sağlar.
# Saldırı anında davranışı test etmek için
# Önce slip değerini 1 yapıp logları izleyin
server:
ratelimit: 500
ratelimit-slip: 1
ratelimit-log-factor: 1 # Her aşımı logla
Logları izlemek için:
tail -f /var/log/unbound/unbound.log | grep "ratelimit"
Çıktıda şuna benzer satırlar görürsünüz:
[1234567890] unbound[1234:0] info: ratelimit exceeded example.com. 500 1000
Bu satır, example.com için saniyede 500 sorgu sınırının aşıldığını ve gerçek hızın 1000 sorgu/saniye olduğunu gösterir.
Alan Adı Bazlı Özel Sınırlamalar
Bazı alan adları için farklı sınırlar koymak isteyebilirsiniz. Örneğin kendi yönettiğiniz bir alan adına gelen sorgular için daha yüksek limit, bilinmeyen alan adları için daha düşük limit mantıklı olabilir.
# /etc/unbound/unbound.conf
server:
ratelimit: 500
ratelimit-slip: 2
# Belirli alan adları için özel limitler
ratelimit-for-domain: example.com 5000
ratelimit-for-domain: trusted-partner.net 3000
ratelimit-for-domain: known-cdn.com 10000
# Belirli alan adları için rate limiti devre dışı bırak
ratelimit-for-domain: internal.company.com 0
ratelimit-for-domain direktifinde 0 değeri o alan adı için rate limitin tamamen devre dışı bırakıldığı anlamına gelir. Kendi iç ağınızdaki kritik servisler için bu tercih edilebilir, ama dikkatli olun.
IP Tabanlı Rate Limiting ile Kaynak Kontrolü
Saldırılar genellikle bot ağlarından gelir ve birden fazla IP’den koordineli sorgular gönderilir. IP bazlı rate limiting bu durumda devreye girer:
server:
# Her IP'den saniyede maksimum sorgu
ip-ratelimit: 100
ip-ratelimit-slip: 2
ip-ratelimit-size: 8m
ip-ratelimit-factor: 0
# Belirli IP'leri veya ağları muaf tut
ip-ratelimit-whitelist: 192.168.1.0/24
ip-ratelimit-whitelist: 10.0.0.0/8
ip-ratelimit-whitelist: 172.16.0.0/12
ip-ratelimit-factor parametresi ilginç bir özellik sunar. 0 değeri verirseniz sınırı aşan IP’lerden gelen sorgular tamamen düşürülür. 10 gibi bir değer verirseniz her 10 sorguda bir yanıt döner, diğerleri sessizce yutulur.
Whitelist için dikkat edilmesi gereken nokta: Bu listede sadece gerçekten güvendiğiniz ağları bulundurun. İç ağınız, yönetim ağınız ve güvenilir monitoring sistemleriniz gibi.
ACL ile Rate Limiting’i Birleştirmek
Rate limiting tek başına yeterli değildir. Access Control List (ACL) kurallarıyla birleştirince çok daha güçlü bir yapı elde edersiniz:
server:
# Varsayılan olarak herkesi reddet
access-control: 0.0.0.0/0 refuse
access-control: ::0/0 refuse
# İç ağa izin ver
access-control: 192.168.0.0/16 allow
access-control: 10.0.0.0/8 allow
access-control: 172.16.0.0/12 allow
# Eğer public resolver ise dikkatlice açın
# access-control: 0.0.0.0/0 allow
# Rate limiting
ratelimit: 1000
ip-ratelimit: 150
ip-ratelimit-whitelist: 127.0.0.1
ip-ratelimit-whitelist: 192.168.0.0/16
Eğer Unbound’u public recursive resolver olarak çalıştırmıyorsanız kesinlikle erişimi iç ağlarla sınırlandırın. Bu hem rate limiting yükünü azaltır hem de saldırı yüzeyini küçültür.
Monitoring ve Alerting Yapısı
Konfigürasyonu yapıp bırakmak olmaz. Neyin normal neyin anormal olduğunu anlayabilmek için sürekli izleme şarttır. Unbound’un istatistik modülünü aktif etmek buradan başlar:
server:
statistics-interval: 60
statistics-cumulative: no
extended-statistics: yes
remote-control:
control-enable: yes
control-interface: 127.0.0.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"
Sertifikaları oluşturmak için:
unbound-control-setup
Sonra istatistikleri çekmek için:
unbound-control stats_noreset | grep -E "ratelimit|total"
Bu komut size şuna benzer çıktı verir:
total.num.queries=15234
total.num.cachehits=12891
total.num.cachemiss=2343
total.num.recursivereplies=2343
ratelimit.ratelimit.exceeded=47
ratelimit.ipaddress.ratelimit.exceeded=12
Bu verileri Prometheus’a göndermek için basit bir script yazabilirsiniz:
#!/bin/bash
# /usr/local/bin/unbound-metrics.sh
STATS=$(unbound-control stats_noreset 2>/dev/null)
if [ $? -ne 0 ]; then
echo "ERROR: unbound-control failed"
exit 1
fi
# Metrikleri /var/lib/node_exporter/textfile_collector/ klasörüne yaz
OUTPUT_FILE="/var/lib/node_exporter/textfile_collector/unbound.prom"
echo "# HELP unbound_ratelimit_exceeded Total ratelimit exceeded count" > $OUTPUT_FILE
echo "# TYPE unbound_ratelimit_exceeded counter" >> $OUTPUT_FILE
RATELIMIT=$(echo "$STATS" | grep "ratelimit.ratelimit.exceeded" | cut -d= -f2)
IP_RATELIMIT=$(echo "$STATS" | grep "ratelimit.ipaddress.ratelimit.exceeded" | cut -d= -f2)
TOTAL_QUERIES=$(echo "$STATS" | grep "^total.num.queries=" | cut -d= -f2)
echo "unbound_ratelimit_exceeded{type="domain"} ${RATELIMIT:-0}" >> $OUTPUT_FILE
echo "unbound_ratelimit_exceeded{type="ip"} ${IP_RATELIMIT:-0}" >> $OUTPUT_FILE
echo "unbound_total_queries ${TOTAL_QUERIES:-0}" >> $OUTPUT_FILE
Bu scripti cron’a ekleyin:
* * * * * /usr/local/bin/unbound-metrics.sh
Gerçek Dünya Senaryosu: Amplifikasyon Saldırısı Tespiti ve Müdahale
Bir üretim ortamında rate limit ihlallerini canlı olarak izlemek için şu yaklaşımı kullanıyorum. Önce loglama seviyesini doğru ayarlayın:
server:
verbosity: 1
log-queries: no # Normalde kapalı tutun, disk yazar
log-replies: no
log-local-actions: yes
log-servfail: yes
ratelimit: 1000
ratelimit-log-factor: 5 # Her 5 ihlalde bir log yaz (performans için)
ip-ratelimit: 100
log-queries: yes yapmayın, yüksek yük altında disk I/O’nuzu öldürür. Zaten rate limit ihlallerini görmek için buna ihtiyacınız yok.
Aktif saldırı sırasında hangi IP’lerin en fazla sorgu ürettiğini görmek için:
# Son 5 dakikadaki rate limit ihlallerini analiz et
journalctl -u unbound --since "5 minutes ago" |
grep "ratelimit" |
awk '{print $NF}' |
sort | uniq -c | sort -rn | head -20
Eğer belirli bir IP sürekli ihlal ediyorsa iptables ile geçici blok atabilirsiniz:
# Saldırgan IP'yi geçici olarak engelle
ATTACKER_IP="1.2.3.4"
iptables -I INPUT -s $ATTACKER_IP -p udp --dport 53 -j DROP
iptables -I INPUT -s $ATTACKER_IP -p tcp --dport 53 -j DROP
# 1 saat sonra otomatik kaldır
echo "iptables -D INPUT -s $ATTACKER_IP -p udp --dport 53 -j DROP" | at now + 1 hour
echo "iptables -D INPUT -s $ATTACKER_IP -p tcp --dport 53 -j DROP" | at now + 1 hour
Yapılandırmayı Test Etmek
Değişiklikleri canlıya almadan önce test ortamında doğrulamak kritik önem taşır. Rate limiting’i test etmek için queryperf veya dnsperf araçlarını kullanabilirsiniz:
# dnsperf kurulumu
apt-get install dnsperf # Debian/Ubuntu
# veya
yum install dnsperf # RHEL/CentOS
# Test sorgu dosyası oluştur
cat > /tmp/queryfile.txt << 'EOF'
google.com A
cloudflare.com A
github.com A
stackoverflow.com A
example.com A
EOF
# Rate limiting'i test et - saniyede 200 sorgu gönder
dnsperf -s 127.0.0.1 -d /tmp/queryfile.txt -Q 200 -l 30
Test sonucunda düşen sorgu oranına bakın. Eğer ratelimit: 1000 ayarladıysanız ve 200 sorgu/saniye gönderiyorsanız neredeyse hiç düşme olmamalı. Limiti aşan değerlerde ise SERVFAIL yanıtlarının arttığını göreceksiniz.
Yapılandırmayı syntax açısından kontrol etmek için:
unbound-checkconf /etc/unbound/unbound.conf
Değişiklikleri yeniden başlatmadan uygulamak için:
systemctl reload unbound
# veya
unbound-control reload
Performans Tuning
Rate limiting bellek ve CPU kullanır. Özellikle ratelimit-size ve ip-ratelimit-size parametreleri hash tablosunun ne kadar bellek kullanacağını belirler. Sunucunuzun bellek durumuna göre bu değerleri ayarlayın:
- ratelimit-size: 4m: 4MB hash tablosu, orta yoğunluklu resolver için yeterli
- ratelimit-size: 16m: Yüksek trafikli public resolver için
- ip-ratelimit-size: 8m: IP bazlı tracking için, bot ağı saldırılarında arttırın
Bellek kullanımını izlemek için:
unbound-control stats | grep "mem.cache"
Çok sayıda benzersiz IP’den gelen saldırılarda (dağıtık botnet) ip-ratelimit-size yetersiz kalabilir. Bu durumda değeri yükseltmek yerine upstream’de BGP blackholing veya DDoS mitigation servisi düşünmek daha mantıklı olabilir. Unbound’un rate limiting’i makul saldırılar için tasarlanmıştır, büyük ölçekli botnet saldırıları için dedicated hardware çözümler gerekir.
Ayrıca num-threads değerinin CPU çekirdek sayısıyla örtüşmesine dikkat edin:
server:
num-threads: 4 # CPU çekirdek sayısına eşit ayarlayın
msg-cache-slabs: 4 # num-threads ile aynı veya 2 katı
rrset-cache-slabs: 4
infra-cache-slabs: 4
key-cache-slabs: 4
ratelimit-size: 8m
ip-ratelimit-size: 8m
Tam Üretim Konfigürasyonu
Yukarıda anlattıklarımı bir araya getiren, gerçek üretim ortamında kullandığım bir konfigürasyon taslağı:
server:
# Genel ayarlar
verbosity: 1
num-threads: 4
interface: 0.0.0.0
port: 53
# Cache ayarları
msg-cache-size: 256m
rrset-cache-size: 512m
msg-cache-slabs: 4
rrset-cache-slabs: 4
# Güvenlik
hide-identity: yes
hide-version: yes
qname-minimisation: yes
# Access control
access-control: 0.0.0.0/0 refuse
access-control: 127.0.0.0/8 allow
access-control: 192.168.0.0/16 allow
access-control: 10.0.0.0/8 allow
# Rate limiting
ratelimit: 1000
ratelimit-slip: 2
ratelimit-size: 8m
ratelimit-log-factor: 5
# IP rate limiting
ip-ratelimit: 100
ip-ratelimit-slip: 2
ip-ratelimit-size: 8m
ip-ratelimit-factor: 0
# İç ağ muafiyeti
ip-ratelimit-whitelist: 127.0.0.0/8
ip-ratelimit-whitelist: 192.168.0.0/16
ip-ratelimit-whitelist: 10.0.0.0/8
# Özel domain limitleri
ratelimit-for-domain: internal.example.com 0
# Loglama
log-servfail: yes
log-local-actions: yes
# İstatistikler
statistics-interval: 60
extended-statistics: yes
remote-control:
control-enable: yes
control-interface: 127.0.0.1
Sonuç
Unbound’da rate limiting doğru yapılandırıldığında ciddi bir koruma katmanı sağlar, ama tek başına yeterli değildir. Bunu bir güvenlik katmanı olarak görün: iptables/nftables kuralları, upstream BGP filtering ve uygulama katmanı korumaları birlikte çalışmalıdır.
Benim önerim şu: Önce ratelimit-log-factor: 1 ile birkaç gün izleyin ve gerçek trafiğinizin profilini çıkarın. Hangi alan adlarına ne kadar sorgu geliyor, hangi IP’ler en aktif, normalize değerler neler? Bu verilere bakarak limit değerlerinizi belirleyin. Körü körüne 1000 veya 100 gibi değerler koymak bazen meşru trafiği de etkiler.
Son olarak, Unbound sürümünüzü güncel tutmayı ihmal etmeyin. Rate limiting algoritmaları ve bypass açıkları zaman içinde keşfedilir ve yamalar çıkar. apt-get upgrade unbound basit bir komut gibi görünür ama geciktirildiğinde ciddi güvenlik açıklarına zemin hazırlar.
