CentOS ve Rocky Linux’a Unbound DNS Sunucusu Kurulum

Recursive DNS çözümleme konusuna geçmeden önce şunu söyleyeyim: Kurumsal ortamlarda BIND’ı refleks olarak kurmaktan vazgeçmeniz gerekiyor. Unbound, özellikle önbellek ve recursive resolver ihtiyacı olan ortamlar için hem daha güvenli hem de çok daha az konfigürasyon yükü gerektiren bir çözüm. Ben bu yazıda CentOS 7/8 ve Rocky Linux 8/9 üzerinde Unbound kurulumu ve yapılandırmasını, gerçek prodüksiyon senaryolarıyla birlikte anlatacağım.

Unbound Nedir ve Ne Zaman Tercih Edilmeli

Unbound, NLnet Labs tarafından geliştirilen, güvenlik odaklı, validating, recursive ve caching DNS resolver’dır. BIND gibi authoritative DNS sunucusu değildir; yani kendi zone’larınızı dışarıya servis etmek için değil, istemcilerinizin DNS sorgularını recursive olarak çözümlemek ve önbelleğe almak için kullanılır.

Peki ne zaman Unbound tercih edilmeli?

  • Şirket içi DNS önbellek sunucusu ihtiyacınız varsa
  • DNSSEC doğrulaması yapmak istiyorsanız
  • Split-DNS senaryosu kuruyorsanız (iç ağ için ayrı, dış ağ için ayrı çözümleme)
  • Lightweight bir çözüm arıyorsanız, BIND’ın karmaşıklığından kaçmak istiyorsanız
  • Pi-hole gibi bir DNS filtreleme altyapısının altına recursive resolver koymak istiyorsanız

Ben genellikle şirket içi altyapılarda Unbound’u primary DNS resolver olarak konumlandırıp, authoritative ihtiyaçları için arka planda ayrı bir BIND instance’ı çalıştırıyorum. Bu ikili yapı hem güvenlik hem yönetilebilirlik açısından çok daha temiz.

Kurulum Öncesi Sistem Hazırlığı

CentOS veya Rocky Linux üzerinde kuruluma geçmeden önce sistemin temel gereksinimlerini karşıladığından emin olun.

Önce mevcut DNS yapılandırmasına bakın:

cat /etc/resolv.conf
systemctl status systemd-resolved
ss -tulnp | grep :53

Eğer sistemde zaten bir şey 53 portunu dinliyorsa (özellikle systemd-resolved), kurulum sonrası çakışma yaşarsınız. Rocky Linux 9’da bu sorun daha sık karşıma çıkıyor.

Firewall ve SELinux durumunu da not edin:

getenforce
firewall-cmd --state

SELinux’u kapatmanızı önermiyorum, Unbound SELinux ile sorunsuz çalışıyor, sadece farkında olmanız gerekiyor.

Kurulum

CentOS 7

CentOS 7’de Unbound doğrudan base repo’da mevcut:

yum install -y unbound
systemctl enable unbound
systemctl start unbound

CentOS 8 ve Rocky Linux 8/9

Bu sistemlerde dnf kullanıyoruz:

dnf install -y unbound
systemctl enable --now unbound

Rocky Linux 9’da paket versiyonu genellikle daha güncel geliyor. Kurulum sonrası versiyon kontrolü yapın:

unbound -V

Şu anda prodüksiyon makinelerimizde 1.16.x serisini kullanıyoruz, 1.17+ sürümünde bazı konfigürasyon parametreleri değişti, buna dikkat edin.

Temel Konfigürasyon

Unbound’un ana konfigürasyon dosyası /etc/unbound/unbound.conf dosyasıdır. Ayrıca /etc/unbound/conf.d/ dizinine ek konfigürasyon dosyaları koyabilirsiniz; bu yaklaşım yönetimi çok kolaylaştırıyor.

Varsayılan konfigürasyon dosyasını yedekleyin, sonra sıfırdan yazın:

cp /etc/unbound/unbound.conf /etc/unbound/unbound.conf.bak

Şimdi temel bir prodüksiyon konfigürasyonu yazalım. Bu konfigürasyon, iç ağdaki tüm istemcilere hizmet verecek şekilde tasarlanmış:

cat > /etc/unbound/unbound.conf << 'EOF'
server:
    # Dinlenecek interface ve port
    interface: 0.0.0.0
    port: 53

    # Erişime izin verilecek ağlar
    access-control: 127.0.0.0/8 allow
    access-control: 10.0.0.0/8 allow
    access-control: 192.168.0.0/16 allow
    access-control: 172.16.0.0/12 allow
    access-control: 0.0.0.0/0 refuse

    # Güvenlik ayarları
    hide-identity: yes
    hide-version: yes
    harden-glue: yes
    harden-dnssec-stripped: yes
    harden-below-nxdomain: yes
    harden-referral-path: yes
    use-caps-for-id: yes

    # Performans ayarları
    num-threads: 4
    msg-cache-size: 64m
    rrset-cache-size: 128m
    cache-min-ttl: 300
    cache-max-ttl: 86400

    # DNSSEC
    auto-trust-anchor-file: "/var/lib/unbound/root.key"

    # Log
    logfile: "/var/log/unbound/unbound.log"
    log-queries: no
    verbosity: 1

    # Prefetch
    prefetch: yes
    prefetch-key: yes

remote-control:
    control-enable: yes
    control-interface: 127.0.0.1
EOF

Bu konfigürasyonda birkaç kritik noktaya dikkat edin:

hide-identity ve hide-version: Sunucu kimliğini ve sürüm bilgisini gizler. Penetrasyon testlerinde bu bilgilerin açık kalmasından kaynaklanan bulgular görüyorum hâlâ.

use-caps-for-id: 0x20 encoding olarak da bilinir. DNS spoofing saldırılarına karşı ek koruma sağlar, sorgu ID’lerinde büyük/küçük harf karıştırır.

prefetch: TTL’i dolmadan önce popüler kayıtları yeniler, kullanıcılar gecikme yaşamaz.

Log Dizini Oluşturma

Konfigürasyonda log dosyası tanımladık, dizini oluşturmamız gerekiyor:

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

# SELinux context düzeltmesi
semanage fcontext -a -t named_log_t "/var/log/unbound(/.*)?"
restorecon -Rv /var/log/unbound

SELinux bu adımı atlarsanız, Unbound log yazamaz ve sessizce başarısız olabilir. Bu hatayı defalarca yaşadım, özellikle Rocky Linux 9’da.

DNSSEC Root Key Güncelleme

Unbound DNSSEC doğrulaması yapabilmek için root trust anchor’a ihtiyaç duyar. Bu dosyayı otomatik olarak yönetmek için:

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

Bu komut internete bağlanarak güncel root key’i indirir. Kapalı ağlarda bu adım farklı yönetilmeli, o senaryoyu aşağıda ele alacağım.

Konfigürasyonu Doğrulama ve Servisi Başlatma

Her konfigürasyon değişikliğinden sonra syntax kontrolü yapın:

unbound-checkconf /etc/unbound/unbound.conf

Çıktı unbound-checkconf: no errors in /etc/unbound/unbound.conf şeklinde olmalı. Servisi (yeniden) başlatın:

systemctl restart unbound
systemctl status unbound

Test edelim:

dig @127.0.0.1 google.com
dig @127.0.0.1 google.com +dnssec

DNSSEC’in çalıştığını doğrulamak için:

dig @127.0.0.1 sigfail.verteiltesysteme.net

Bu sorgu SERVFAIL döndürmeli, çünkü bu domain kasıtlı olarak hatalı DNSSEC imzasına sahip. Eğer SERVFAIL alıyorsanız DNSSEC doğrulama çalışıyor demektir.

Firewall Yapılandırması

DNS trafiğine izin vermek için:

firewall-cmd --permanent --add-service=dns
firewall-cmd --reload
firewall-cmd --list-services

Sadece belirli bir ağdan gelen DNS trafiğine izin vermek istiyorsanız (daha güvenli yaklaşım):

firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.0.0.0/8" service name="dns" accept'
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.0.0/16" service name="dns" accept'
firewall-cmd --reload

Split-DNS Yapılandırması

En çok kullandığım senaryo bu. Şirket içi domain’ler için local authoritative sunucuya yönlendirme, diğer tüm sorgular için recursive çözümleme. Mesela iç.sirket.local için kendi DNS sunucunuza, geri kalan her şey için upstream’e gitmesini istiyorsunuz.

/etc/unbound/conf.d/local-zones.conf dosyası oluşturun:

cat > /etc/unbound/conf.d/local-zones.conf << 'EOF'
# İç domain forward
forward-zone:
    name: "sirket.local"
    forward-addr: 10.0.1.10
    forward-addr: 10.0.1.11

# Reverse lookup için de aynısını yapın
forward-zone:
    name: "0.0.10.in-addr.arpa"
    forward-addr: 10.0.1.10
    forward-addr: 10.0.1.11

# Tüm diğer sorgular için upstream (isteğe bağlı, Unbound zaten recursive çalışır)
# forward-zone:
#     name: "."
#     forward-addr: 8.8.8.8
#     forward-addr: 1.1.1.1
EOF

Burada önemli bir nokta: Eğer forward-zone: name: "." tanımlarsanız Unbound recursive çalışmayı bırakır ve tüm sorguları o upstream’e iletir. DNSSEC doğrulaması yapmak istiyorsanız ya upstream da DNSSEC desteklemeli ya da bu satırı kaldırıp Unbound’u tam recursive modda çalıştırmalısınız. Ben üretim ortamlarında genellikle tam recursive modda çalıştırıyorum.

Local Data Tanımlama

Bazı kayıtları doğrudan Unbound’da tanımlamak isteyebilirsiniz, ayrı bir authoritative sunucuya gerek kalmadan. Küçük ofis ortamları için ideal:

cat > /etc/unbound/conf.d/local-data.conf << 'EOF'
server:
    local-zone: "ofis.local." static

    local-data: "router.ofis.local. IN A 192.168.1.1"
    local-data: "printer.ofis.local. IN A 192.168.1.50"
    local-data: "nas.ofis.local. IN A 192.168.1.100"
    local-data: "gitlab.ofis.local. IN A 192.168.1.200"

    # PTR kayıtları
    local-data-ptr: "192.168.1.1 router.ofis.local"
    local-data-ptr: "192.168.1.50 printer.ofis.local"
    local-data-ptr: "192.168.1.100 nas.ofis.local"
EOF

Kapalı Ağ (Air-Gapped) Ortamlar İçin Özel Yapılandırma

İnternete erişimi olmayan ortamlarda Unbound’u doğrudan recursive çalıştıramazsınız çünkü root server’lara erişemez. Bu durumda ya bir upstream forwarder tanımlamanız gerekir (şirketin proxy üzerinden internete çıkan DNS sunucusu gibi) ya da root hints’i lokal bir kaynaktan beslemeniz gerekir.

Root hints dosyasını manuel olarak almak için (internete erişimi olan başka bir makineden transfer edin):

# İnternete erişimi olan makinede:
curl -o /tmp/named.cache https://www.internic.net/domain/named.cache

# Dosyayı hedef sunucuya aktarın, sonra:
cp /tmp/named.cache /etc/unbound/root.hints

# unbound.conf içine ekleyin:
# root-hints: "/etc/unbound/root.hints"

Kapalı ağ ortamlarında genellikle şirketin merkezi DNS sunucusuna forward-zone ile yönlendirme yapıyorum ve o sunucu internete çıkıyor. Bu daha temiz bir çözüm.

Unbound-Control ile Yönetim

unbound-control aracı servis yeniden başlatmadan önbellek temizleme, istatistik alma gibi işlemler yapmanızı sağlar:

# Servis durumu
unbound-control status

# İstatistikler
unbound-control stats_noreset

# Önbelleği temizle
unbound-control flush_zone google.com

# Tüm önbelleği temizle
unbound-control flush_all

# Belirli bir domain'i engelle (runtime'da)
unbound-control local_zone "kötüdomain.com." always_nxdomain

# Zone yeniden yükle (config değişikliği sonrası restart yerine)
unbound-control reload

unbound-control stats_noreset çıktısını monitoring sistemine beslemek için güzel bir yol var, ilerleyen bir yazıda Prometheus entegrasyonunu anlatmayı düşünüyorum.

Performans Tuning

Prodüksiyon ortamı için thread ve cache ayarlarını sisteminize göre optimize edin:

# CPU sayısına göre thread ayarı
nproc

# RAM durumuna göre cache boyutu
free -m

Genel kural olarak:

  • num-threads: Fiziksel CPU çekirdek sayısına eşit veya yarısı kadar ayarlayın
  • msg-cache-size: Toplam RAM’in %5-10’u
  • rrset-cache-size: msg-cache-size’ın 2 katı
  • cache-min-ttl: 300 saniye makul bir başlangıç noktası, çok düşük TTL’li kayıtlar için performansı artırır
  • so-rcvbuf ve so-sndbuf: Yüksek trafikli ortamlarda 4m veya 8m olarak ayarlayın

Yüksek trafikli bir ortam için örnek:

# /etc/unbound/conf.d/performance.conf
server:
    num-threads: 8
    msg-cache-size: 256m
    rrset-cache-size: 512m
    key-cache-size: 64m
    neg-cache-size: 32m
    so-rcvbuf: 4m
    so-sndbuf: 4m
    outgoing-range: 8192
    num-queries-per-thread: 4096
    infra-cache-numhosts: 100000

Bu ayarları uyguladıktan sonra unbound-control stats_noreset | grep cache ile cache hit oranını izleyin. İyi yapılandırılmış bir ortamda %70-80 cache hit oranı görmelisiniz.

Sorun Giderme

En sık karşılaşılan sorunlar ve çözümleri:

Servis başlamıyor ve port çakışması var:

# 53 portunu kim kullanıyor?
ss -tulnp | grep :53
# systemd-resolved çakışması varsa:
systemctl disable --now systemd-resolved
# veya resolved'ı stub moddan çıkarın:
# /etc/systemd/resolved.conf içinde DNSStubListener=no

SELinux kaynaklı sorunlar için:

ausearch -c 'unbound' --raw | audit2allow -M unbound-local
semodule -i unbound-local.pp

DNSSEC doğrulama hataları için:

unbound-control dump_cache | grep -i "servfail"
# Geçici olarak DNSSEC'i devre dışı bırakmak için:
# val-permissive-mode: yes  (sadece test için!)

Log seviyesini artırarak daha detaylı debug:

unbound-control verbosity 3
# İşiniz bitince eski seviyeye döndürün:
unbound-control verbosity 1

Systemd Entegrasyonu ve Otomatik Başlatma

Unbound’un sistem başlangıcında network hazır olduktan sonra başlamasını sağlamak için unit dosyasını kontrol edin:

systemctl cat unbound
# After=network.target satırının olduğundan emin olun

# Servis durumunu izlemek için:
journalctl -u unbound -f
journalctl -u unbound --since "1 hour ago"

Rocky Linux 9’da unbound unit dosyası genellikle doğru yazılmış geliyor, CentOS 7’de bazen After=network-online.target eklemeniz gerekebiliyor, özellikle ağ yapılandırmasının yavaş tamamlandığı ortamlarda.

Sonuç

Unbound, doğru yapılandırıldığında son derece güvenilir ve performanslı bir DNS resolver. BIND’a kıyasla saldırı yüzeyi daha küçük, konfigürasyonu daha anlaşılır, ve modern güvenlik özelliklerini (DNSSEC, qname minimization, 0x20 encoding) kutudan çıkar çıkmaz destekliyor.

Anlattığım kurulum akışını özetlersek: Paket kurulumu, temel güvenlik odaklı konfigürasyon, SELinux uyumluluğu, DNSSEC root key ayarı, split-DNS ihtiyaçları için forward zone tanımlaması ve performans tuning. Bu adımları takip ederek hem güvenli hem de yüksek performanslı bir DNS altyapısı kurabilirsiniz.

Bir sonraki adım olarak Unbound’u Prometheus + Grafana ile izlemeyi ve DNS over TLS yapılandırmasını anlatmayı planlıyorum. Yorum veya sorularınız için blogun iletişim bölümünü kullanabilirsiniz.

Bir yanıt yazın

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