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.
