Unbound DNS Sunucusunda IPv6 Desteği ve Yapılandırması
IPv6 artık “yakında geliyor” aşamasını çoktan geçti. Özellikle son birkaç yılda ISP’lerin dual-stack dağıtımlarını yaygınlaştırmasıyla birlikte, DNS altyapısını IPv6’ya hazır hale getirmek artık opsiyonel değil, zorunlu. Peki ya Unbound? Recursive resolver dünyasının bu sağlam ve güvenilir oyuncusu IPv6 konusunda ne kadar olgun? Kısa cevap: oldukça olgun. Ama doğru yapılandırılmadığında da başınızı oldukça ağrıtabiliyor.
Bu yazıda Unbound’un IPv6 desteğini tüm detaylarıyla ele alacağım. Hem sunucunun IPv6 üzerinden dinlemesi, hem IPv6 upstream sorgular yapması, hem de AAAA kayıt yönetimi konularında gerçek dünya senaryolarıyla ilerleyeceğiz.
Unbound ve IPv6: Temel Mimari
Unbound’da IPv6 desteği iki farklı boyutta düşünülmeli:
- Gelen bağlantılar (inbound): Unbound’un IPv6 adreslerinde DNS sorgularını dinlemesi
- Giden bağlantılar (outbound): Unbound’un upstream resolver veya root sunuculara IPv6 üzerinden sorgu yapması
Bu iki boyutu birbirinden bağımsız olarak yapılandırabilirsiniz. Örneğin, iç ağınızda henüz IPv6 yoksa ama upstream sorgularını IPv6 üzerinden yapmak istiyorsanız bunu ayrı ayrı kontrol edebilirsiniz. Ya da tam tersi de mümkün.
Ayrıca bir de AAAA kayıtlarının nasıl işlendiği konusu var. Bu, istemcilerinizin davranışını doğrudan etkileyen kritik bir nokta.
Kurulum ve Temel Doğrulama
Önce Unbound’un IPv6 desteğiyle derlendiğini doğrulayalım:
unbound -V
Çıktıda IPv6 satırını görüyor olmalısınız. Debian/Ubuntu tabanlı sistemlerde paket yöneticisinden kurulan Unbound genellikle IPv6 desteğiyle gelir. RHEL/CentOS tarafında da aynı durum geçerli.
Mevcut Unbound sürecinizin hangi adresleri dinlediğini görmek için:
ss -tlnp | grep unbound
# veya
netstat -tlnp | grep unbound
Eğer burada sadece 0.0.0.0:53 görüyorsanız, IPv6 dinleme henüz aktif değildir. :::53 veya belirli bir IPv6 adresi görmeniz gerekiyor.
Ana Yapılandırma: interface Direktifi
Unbound’un unbound.conf dosyasında IPv6 dinlemeyi aktifleştirmek için interface direktifini kullanıyoruz:
# /etc/unbound/unbound.conf
server:
# Tüm IPv4 arayüzlerinde dinle
interface: 0.0.0.0
# Tüm IPv6 arayüzlerinde dinle
interface: ::0
# Veya belirli bir IPv6 adresi üzerinde dinle
# interface: 2001:db8::1
# IPv6 destekli access control
access-control: ::1 allow
access-control: ::0/0 refuse
Burada dikkat edilmesi gereken bir nokta var: interface: ::0 ile interface: :: aynı şeyi ifade ediyor. Bazı Unbound versiyonlarında ::0 daha açık bir yazım olarak tercih ediliyor.
Yapılandırma değişikliğinin ardından syntax kontrolü yapın:
unbound-checkconf /etc/unbound/unbound.conf
Sorun yoksa servisi yeniden başlatın:
systemctl restart unbound
do-ip6 Direktifi: Giden IPv6 Sorgular
Unbound’un upstream sunuculara IPv6 üzerinden sorgu yapıp yapmamasını do-ip6 direktifi kontrol ediyor:
server:
# IPv6 ile upstream sorgu yap (varsayılan: yes)
do-ip6: yes
# IPv4 ile upstream sorgu yap (varsayılan: yes)
do-ip4: yes
do-ip6: yes ayarlandığında Unbound, root sunuculara ve diğer upstream resolver’lara IPv6 adresleri üzerinden sorgu yapabilir. Root sunucuların büyük çoğunluğunun artık IPv6 adresi olduğunu düşündüğümüzde, bu özelliği aktifleştirmek genellikle mantıklı.
Ancak şunu da belirteyim: Bazı ortamlarda, özellikle IPv6 bağlantısı unstable olan sistemlerde do-ip6: yes ayarı sorgu gecikmelerini artırabiliyor. Unbound önce IPv6 ile deneyip başarısız olduktan sonra IPv4’e düşüyor. Bunu log’larda şöyle görebilirsiniz:
journalctl -u unbound -f | grep -i "ip6|ipv6|AAAA"
prefer-ip6 Direktifi
Unbound’un IPv6 adresini tercih etmesini istiyorsanız:
server:
do-ip6: yes
prefer-ip6: yes
prefer-ip6: yes ile Unbound, bir host’un hem IPv4 hem IPv6 adresi olduğunda IPv6’yı tercih eder. Bu özellikle dual-stack root server’larla iletişimde etkili oluyor. Genel olarak modern bir altyapıda bu ayarı aktifleştirmenizi öneririm. Hem RFC önerileriyle uyumlu, hem de IPv6’nın daha geniş kullanımını teşvik etmek açısından doğru bir adım.
Access Control ve IPv6 Güvenliği
IPv6 ağ aralıkları için access control kuralları biraz farklı ele alınmalı:
server:
# Loopback
access-control: ::1 allow
# ULA (Unique Local Address) aralığı - RFC 4193
access-control: fc00::/7 allow
# Belirli bir /64 ağı
access-control: 2001:db8:cafe::/48 allow
# Link-local adresler (genellikle DNS için uygun değil ama...)
access-control: fe80::/10 allow
# Geri kalan her şeyi reddet
access-control: ::0/0 refuse
Burada ULA (fc00::/7) aralığına dikkat edin. İç ağınızda ULA adresleri kullanıyorsanız, yani fd::/8 ile başlayan adresler, bu aralığı izin listesine eklemeniz gerekiyor. Aksi takdirde iç ağdaki IPv6 istemcileri sorgu yapamaz.
Bir de şunu ekleyeyim: deny ile refuse arasındaki farka dikkat edin. refuse sorguyu reddedip REFUSED yanıtı döner, deny ise hiç yanıt vermez. Dışarıdan gelen sorgular için deny daha güvenli bir tercih olabilir; saldırgan sunucunuzun varlığından bile haberdar olmaz.
IPv6 ile Stub Zone Yapılandırması
Iç ağınızda bir authoritative DNS sunucunuz varsa ve o sunucunun IPv6 adresi üzerinden erişmek istiyorsanız:
stub-zone:
name: "iç.example.com"
stub-addr: 2001:db8::53
stub-addr: 192.168.1.53 # Fallback için IPv4 de ekleyebilirsiniz
Bu yapılandırmayla Unbound, iç.example.com altındaki sorgular için önce IPv6 adresini dener. Eğer IPv6 bağlantısı başarısız olursa (ve IPv4 de tanımlıysa) IPv4’e düşer.
Forward Zone ve IPv6 Upstream
Google, Cloudflare veya kurumsal bir forwarder kullanıyorsanız, IPv6 adreslerini de eklemenizi öneririm:
forward-zone:
name: "."
# Cloudflare
forward-addr: 1.1.1.1
forward-addr: 1.0.0.1
forward-addr: 2606:4700:4700::1111
forward-addr: 2606:4700:4700::1001
# Google
# forward-addr: 8.8.8.8
# forward-addr: 8.8.4.4
# forward-addr: 2001:4860:4860::8888
# forward-addr: 2001:4860:4860::8844
forward-tls-upstream: yes # DNS-over-TLS için
forward-tls-upstream kullanıyorsanız, IPv6 adresleri üzerinden TLS bağlantısı da sorunsuz çalışıyor. Cloudflare ve Google her ikisini de destekliyor.
PTR Sorguları ve ip6.arpa
IPv6 reverse DNS konusu, IPv4’e kıyasla biraz daha karmaşık. IPv6 PTR kayıtları ip6.arpa altında tutuluyor ve her hex digit ayrı bir label oluşturuyor. Yani 2001:db8::1 adresi için PTR kaydı şöyle:
1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa
Unbound’da iç IPv6 ağınız için PTR yanıtı vermek istiyorsanız, local-data ile tanımlayabilirsiniz:
server:
# ULA ağınız için PTR kaydı
local-data-ptr: "fd12:3456:789a::1 server1.internal.example.com"
local-data-ptr: "fd12:3456:789a::2 server2.internal.example.com"
# Veya AAAA kaydı
local-data: "server1.internal.example.com. AAAA fd12:3456:789a::1"
Daha büyük ölçekte, belirli bir ip6.arpa bloğunu local zone olarak tanımlamak daha yönetilebilir:
local-zone: "9.a.7.8.6.5.4.3.2.1.d.f.ip6.arpa." static
local-data: "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.9.a.7.8.6.5.4.3.2.1.d.f.ip6.arpa. PTR server1.internal.example.com."
Bu yazımın uzayacağını biliyorum ama PTR konusu gerçekten ihmal edilmemelidir. Özellikle mail sunucuları ve bazı uygulamalar reverse DNS doğrulaması yapıyor.
AAAA Kayıtlarını Filtreleme: dns64 ve Diğer Teknikler
Bazı ortamlarda, özellikle sadece IPv4 istemcilerinin olduğu legacy sistemlerde, AAAA kayıtlarını filtrelemek ya da sadece IPv4 yanıtlar döndürmek isteyebilirsiniz. Bunun için Unbound’da iki farklı yaklaşım var.
Birinci yaklaşım, belirli bir domain için AAAA yanıtlarını bastırmak:
server:
# Sadece belirli bir alan adı için AAAA bastır
local-zone: "sadece-ipv4-olan-site.example.com." typetransparent
local-data: "sadece-ipv4-olan-site.example.com. AAAA 0::0"
Ama dürüst olmak gerekirse, bu yaklaşım pek temiz değil. Daha doğru yöntem şu:
server:
# Belirli bir type için "nodata" döndür
local-zone: "sadece-ipv4-olan-site.example.com." static
local-data: "sadece-ipv4-olan-site.example.com. A 203.0.113.1"
# AAAA eklenmediğinde NODATA döner
İkinci yaklaşım ise DNS64 modülünü kullanmak. Bu, sadece IPv6 ağı olan istemcilerin IPv4-only servislere erişmesini sağlayan NAT64 altyapısının DNS ayağı. Unbound’da DNS64 desteği var:
server:
module-config: "dns64 iterator"
dns64:
# NAT64 prefix'iniz
prefix: 64:ff9b::/96
DNS64 yapılandırması daha karmaşık bir konu ve genellikle NAT64 gateway kurulumunu da gerektiriyor. Şu an için bu kadar değinmek yeterli, ilerleyen bir yazıda ayrıca ele alabiliriz.
Monitoring ve Hata Ayıklama
IPv6 ile ilgili sorunları debug ederken kullanabileceğiniz araçlar:
# Unbound'un IPv6 istatistiklerini görüntüle
unbound-control stats | grep -i "ip6|num.queries"
# Belirli bir AAAA sorgusu için test
dig AAAA google.com @::1
# IPv6 upstream bağlantısını test et
dig AAAA google.com @2606:4700:4700::1111
# Unbound log seviyesini artır
unbound-control verbosity 3
# Log'ları canlı takip et
journalctl -u unbound -f
Bir de şu senaryo çok sık karşılaşılan bir sorun: Unbound IPv6 dinliyor ama istemcilerden sorgu gelmiyor. Bu durumda ilk kontrol edilecek şey firewall:
# iptables ile IPv6 için 53. portu aç
ip6tables -A INPUT -p udp --dport 53 -j ACCEPT
ip6tables -A INPUT -p tcp --dport 53 -j ACCEPT
# nftables kullanıyorsanız
nft add rule ip6 filter input udp dport 53 accept
nft add rule ip6 filter input tcp dport 53 accept
# firewalld kullanıyorsanız (Red Hat ekosistemi)
firewall-cmd --add-service=dns --permanent
firewall-cmd --reload
firewalld’nin DNS servisinin hem IPv4 hem IPv6’yı kapsadığını belirteyim; ayrı bir kural eklemenize gerek yok.
Gerçek Dünya Senaryosu: Dual-Stack Kurumsal Ortam
Elimde şöyle bir senaryo var: Bir kurumsal ağda hem IPv4 hem IPv6 kullanılıyor, iç DNS sunucusu Unbound, tüm istemciler DHCP/DHCPv6 ile yapılandırılıyor. Unbound’u bu ortam için nasıl ayarlarsınız?
server:
# Tüm arayüzlerde dinle
interface: 0.0.0.0
interface: ::0
do-ip4: yes
do-ip6: yes
prefer-ip6: yes
# IPv4 iç ağı
access-control: 192.168.0.0/16 allow
access-control: 10.0.0.0/8 allow
# IPv6 iç ağı (ULA)
access-control: fd00::/8 allow
# Loopback
access-control: 127.0.0.0/8 allow
access-control: ::1 allow
# Her şeyi reddet
access-control: 0.0.0.0/0 refuse
access-control: ::0/0 refuse
# DNSSEC
auto-trust-anchor-file: "/var/lib/unbound/root.key"
# Cache
cache-max-ttl: 86400
# Minimal responses
minimal-responses: yes
# Gizlilik
hide-identity: yes
hide-version: yes
# İç alan adları
local-zone: "internal.example.com." static
local-data: "gw.internal.example.com. A 10.0.0.1"
local-data: "gw.internal.example.com. AAAA fd12:3456::1"
local-data-ptr: "10.0.0.1 gw.internal.example.com"
local-data-ptr: "fd12:3456::1 gw.internal.example.com"
forward-zone:
name: "."
forward-addr: 9.9.9.9
forward-addr: 149.112.112.112
forward-addr: 2620:fe::fe
forward-addr: 2620:fe::9
Bu yapılandırmada dikkat çeken birkaç nokta var. Hem A hem AAAA kayıtlarını local-data ile tanımladık. PTR kayıtlarını da ekledik çünkü bazı iç uygulamalar reverse lookup yapıyor. Upstream olarak Quad9’un hem IPv4 hem IPv6 adreslerini ekledik.
Performans Notları
IPv6 ile ilgili bir performans konusuna da değinmek istiyorum. Bazı ortamlarda, özellikle IPv6 bağlantısının kalitesinin IPv4’ten düşük olduğu durumlarda, Unbound’un IPv6 upstream’e erişmeye çalışırken timeout beklemesi genel sorgu sürelerini artırabiliyor. Bunu hafifletmek için:
server:
# Outgoing sorgu timeout değeri (ms cinsinden)
# Varsayılan oldukça yüksek, düşürülebilir
infra-cache-min-rtt: 50
# IPv6 bağlantısı sorunluysa geçici olarak kapat
# do-ip6: no
Eğer IPv6 bağlantınız tutarsızsa ve bunu çözmeniz zaman alacaksa, geçici olarak do-ip6: no yapabilirsiniz. Ama bu kalıcı bir çözüm değil, IPv6 altyapısını düzeltmek asıl hedef olmalı.
Bir diğer konu da outgoing arayüzü. Birden fazla IPv6 adresiniz varsa, Unbound’un hangi adresi kullanarak dışarıya sorgu yapacağını belirleyebilirsiniz:
server:
outgoing-interface: 2001:db8::1
outgoing-interface: 192.168.1.100 # IPv4 fallback
Yaygın Hatalar ve Çözümleri
Unbound IPv6 yapılandırmasında karşılaşılan bazı yaygın hatalar ve bunların çözümleri:
Hata: “bind: Cannot assign requested address”
Bu hata genellikle yapılandırmada belirtilen IPv6 adresinin henüz sisteme atanmamış olmasından kaynaklanır. Unbound başladığında adres yoksa bind edemez. Çözüm: ip -6 addr show ile adresi doğrulayın, adres gecikmeli geliyorsa Unbound servisini ağ bağlantısı sağlandıktan sonra başlayacak şekilde ayarlayın.
# systemd ile ağa bağımlı başlatma
systemctl edit unbound
# [Unit] altına ekleyin:
# After=network-online.target
# Wants=network-online.target
Hata: AAAA sorgularına SERVFAIL dönüyor ama A sorgularına dönmüyor
Bu genellikle Unbound’un IPv6 root sunuculara ulaşamadığı anlamına gelir. Kontrol listesi:
do-ip6: yesvar mı?- Firewall IPv6 çıkış trafiğine izin veriyor mu?
ping6 2001:500:2::croot sunucu yanıt veriyor mu?
Hata: Local-data ile tanımladığım AAAA kaydı dönmüyor
Local-zone tipine dikkat edin. Eğer zone tipi transparent ise ve upstream’de farklı bir kayıt varsa, upstream yanıtı override edebilir. İç kayıtlar için static tipi daha güvenli:
local-zone: "internal.example.com." static
Sonuç
Unbound’da IPv6 desteği olgun ve kapsamlı. Doğru yapılandırıldığında hem inbound hem outbound IPv6 sorgular sorunsuz çalışıyor. Özetlemek gerekirse:
- interface: ::0 ile tüm IPv6 arayüzlerinde dinlemeye başlayın
- do-ip6: yes ile upstream IPv6 sorgularını aktifleştirin
- prefer-ip6: yes ile modern ağ prensiplerini destekleyin
- Access control kurallarınıza IPv6 ağ bloklarını mutlaka ekleyin
- PTR kayıtlarını ihmal etmeyin, ip6.arpa yönetimine zaman ayırın
- Firewall kurallarınızı hem iptables hem ip6tables (veya nftables) tarafında kontrol edin
- Monitoring’de IPv6’ya özel metrikler ekleyin
IPv6 geçişi hala devam ediyor ve DNS altyapısının bu geçişte oynadığı rol kritik. Unbound’u bugün doğru yapılandırmak, yarın sorun yaşamak zorunda kalmamak anlamına geliyor.
