CoreDNS ile DNS over TLS Yapılandırması

DNS trafiğiniz düz metin gidiyor ve siz hâlâ “zaten iç ağdayız, ne olacak” diye düşünüyorsunuz. Bunu yıllarca ben de düşündüm. Ta ki bir müşterinin iç ağında yapılan trafik analizinde DNS sorgularının ne kadar açık seçik göründüğünü görene kadar. O günden sonra DNS şifrelemesini “nice to have” değil, “must have” olarak değerlendiriyorum.

Bu yazıda CoreDNS üzerinde DNS over TLS (DoT) yapılandırmasını ele alacağız. Hem upstream resolver olarak TLS kullanımını, hem de CoreDNS’i kendisi bir DoT sunucusu olarak çalıştırmayı işleyeceğiz. Kubernetes ortamları için özel notlar da ekledim çünkü CoreDNS orada en çok karşılaşılan senaryo.

DNS over TLS Nedir ve Neden Önemli?

Klasik DNS, UDP 53 üzerinden düz metin çalışır. Bu demek oluyor ki ağınızdaki herhangi bir izleme noktası, yönlendiriciniz, ISP’niz veya kötü niyetli biri hangi alan adlarını sorguladığınızı görebilir. DNS over TLS bunu TLS tüneli içine alarak şifreler ve dinlemeyi engeller.

RFC 7858 ile standardize edilen DoT, varsayılan olarak 853 numaralı portu kullanır. DNS over HTTPS (DoH) ile karıştırmamak gerekir; DoH 443 portunu kullanır ve HTTP/2 üzerinden çalışır. Her ikisinin de avantajları var ama altyapı tarafında DoT daha temiz bir çözüm. Port 853 açıkça DNS trafiği için ayrılmış olduğundan ağ yönetimi açısından daha belirgin ve yönetilebilir.

CoreDNS bu noktada güçlü bir tercih çünkü hem tls eklentisi ile TLS terminasyonu yapabiliyor, hem de forward eklentisi ile upstream’e TLS üzerinden bağlanabiliyor.

CoreDNS Kurulumu

Eğer sisteminizde CoreDNS yoksa önce bunu halledelim. Kubernetes dışında bağımsız kurulum için:

# En güncel sürümü GitHub'dan çekin
COREDNS_VERSION="1.11.3"
wget https://github.com/coredns/coredns/releases/download/v${COREDNS_VERSION}/coredns_${COREDNS_VERSION}_linux_amd64.tgz

tar -xzf coredns_${COREDNS_VERSION}_linux_amd64.tgz
sudo mv coredns /usr/local/bin/
sudo chmod +x /usr/local/bin/coredns

# Çalıştığını doğrula
coredns --version

Systemd ile yönetmek için bir servis dosyası oluşturalım:

sudo useradd -r -s /sbin/nologin coredns
sudo mkdir -p /etc/coredns

cat << 'EOF' | sudo tee /etc/systemd/system/coredns.service
[Unit]
Description=CoreDNS DNS Server
Documentation=https://coredns.io
After=network.target

[Service]
User=coredns
ExecStart=/usr/local/bin/coredns -conf /etc/coredns/Corefile
Restart=on-failure
RestartSec=5
LimitNOFILE=1048576

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable coredns

TLS Sertifikası Hazırlamak

CoreDNS’i DoT sunucusu olarak çalıştırmak için bir TLS sertifikasına ihtiyacınız var. Üretim ortamı için Let’s Encrypt veya kurumsal CA’nızdan alınmış sertifika kullanın. Test için self-signed sertifika oluşturalım:

sudo mkdir -p /etc/coredns/tls
cd /etc/coredns/tls

# Self-signed CA ve sunucu sertifikası oluştur
openssl req -x509 -newkey rsa:4096 -keyout ca.key -out ca.crt 
  -days 365 -nodes 
  -subj "/CN=Internal DNS CA/O=MyOrg/C=TR"

openssl req -newkey rsa:4096 -keyout server.key -out server.csr 
  -nodes -subj "/CN=dns.internal.example.com/O=MyOrg/C=TR"

openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key 
  -CAcreateserial -out server.crt -days 365 
  -extfile <(printf "subjectAltName=DNS:dns.internal.example.com,IP:192.168.1.10")

# İzinleri düzelt
sudo chown -R coredns:coredns /etc/coredns/tls
sudo chmod 600 /etc/coredns/tls/*.key
sudo chmod 644 /etc/coredns/tls/*.crt

Üretimde Let’s Encrypt kullanıyorsanız Certbot ile:

sudo certbot certonly --standalone 
  -d dns.example.com 
  --agree-tos 
  -m [email protected]

# Sertifikaları CoreDNS dizinine sembolik link ile bağlayın
sudo ln -s /etc/letsencrypt/live/dns.example.com/fullchain.pem 
  /etc/coredns/tls/server.crt
sudo ln -s /etc/letsencrypt/live/dns.example.com/privkey.pem 
  /etc/coredns/tls/server.key

Temel Corefile Yapılandırması

Şimdi işin özüne gelelim. CoreDNS yapılandırması Corefile adlı dosyadan okunur. Temel bir DoT yapılandırması şöyle görünür:

cat << 'EOF' | sudo tee /etc/coredns/Corefile
# Standart DNS (port 53) - geriye dönük uyumluluk için
.:53 {
    errors
    health {
        lameduck 5s
    }
    ready
    log . {
        class error
    }
    cache 300
    forward . tls://9.9.9.9 tls://149.112.112.112 {
        tls_servername dns.quad9.net
        health_check 5s
        policy sequential
    }
    reload
}

# DNS over TLS (port 853)
tls://.:853 {
    tls /etc/coredns/tls/server.crt /etc/coredns/tls/server.key {
        client_auth require_and_verify /etc/coredns/tls/ca.crt
    }
    errors
    log . {
        class all
    }
    cache 300
    forward . tls://9.9.9.9 tls://149.112.112.112 {
        tls_servername dns.quad9.net
        health_check 5s
    }
    reload
}
EOF

Burada dikkat edilmesi gereken birkaç nokta var. client_auth require_and_verify satırı istemci sertifikası doğrulamasını zorunlu kılıyor. Eğer bunu istemiyorsanız, yani herhangi bir istemcinin bağlanabilmesini istiyorsanız bu satırı kaldırın. İç ağda kontrollü bir ortamda çalışıyorsanız mutual TLS tercih edin.

tls_servername parametresi çok önemli. Upstream’e bağlanırken SNI için kullanılıyor ve sertifika doğrulaması bu isimle yapılıyor. Yanlış veya eksik bırakırsanız TLS bağlantısı kurulmaz.

İç Ağ Bölgeleri için Gelişmiş Yapılandırma

Gerçek dünya senaryolarında genellikle iç domain’ler için farklı, dış domain’ler için farklı resolver kullanmanız gerekir. Bir kurumsal ortamda şöyle bir yapı kurabilirsiniz:

cat << 'EOF' | sudo tee /etc/coredns/Corefile
# İç domain: internal.example.com
tls://internal.example.com:853 {
    tls /etc/coredns/tls/server.crt /etc/coredns/tls/server.key
    errors
    log
    cache 600
    # İç DNS sunucusuna yönlendir (TLS olmadan, iç ağ güvenli kabul ediliyor)
    forward . 192.168.1.5 192.168.1.6 {
        policy round_robin
        health_check 10s
        expire 15s
    }
}

# Tüm diğer sorgular için Cloudflare DoT
tls://.:853 {
    tls /etc/coredns/tls/server.crt /etc/coredns/tls/server.key
    errors
    log . {
        class error
    }
    cache 300 {
        success 9984 300 60
        denial 9984 5 60
    }
    forward . tls://1.1.1.1 tls://1.0.0.1 {
        tls_servername cloudflare-dns.com
        health_check 5s
        max_fails 3
        expire 10s
    }
    reload 30s
}

# Standart port 53 de dinle, eski istemciler için
.:53 {
    errors
    health
    ready
    cache 300
    forward . tls://1.1.1.1 tls://1.0.0.1 {
        tls_servername cloudflare-dns.com
        health_check 5s
    }
}
EOF

Bu yapılandırmada cache bloğunun parametrelerine dikkat edin. success 9984 300 60 şu anlama geliyor: başarılı yanıtlar için maksimum 9984 kayıt sakla, TTL’i en fazla 300 saniye tut, minimum 60 saniye sakla. Bu cache tuning’i yoğun ortamlarda ciddi performans farkı yaratır.

Kubernetes’te CoreDNS DoT Yapılandırması

Kubernetes’te CoreDNS zaten cluster DNS sunucusu olarak çalışıyor. Buradaki asıl hedef genellikle upstream resolver’a TLS ile bağlanmak. ConfigMap’i düzenleyelim:

kubectl edit configmap coredns -n kube-system

Açılan editörde Corefile anahtarının değerini şu şekilde güncelleyin:

# Önce mevcut yapılandırmayı yedekleyin
kubectl get configmap coredns -n kube-system -o yaml > coredns-backup.yaml

# Yeni ConfigMap'i uygulayın
cat << 'EOF' | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
  name: coredns
  namespace: kube-system
data:
  Corefile: |
    .:53 {
        errors
        health {
            lameduck 5s
        }
        ready
        kubernetes cluster.local in-addr.arpa ip6.arpa {
            pods insecure
            fallthrough in-addr.arpa ip6.arpa
            ttl 30
        }
        prometheus :9153
        forward . tls://9.9.9.9 tls://149.112.112.112 {
            tls_servername dns.quad9.net
            health_check 5s
            max_fails 3
            expire 10s
        }
        cache 30
        loop
        reload
        loadbalance
    }
EOF

Kubernetes’te bir uyarı: forward bloğunda max_fails ve expire değerlerini mutlaka ayarlayın. Aksi halde bir upstream TLS bağlantısı başarısız olduğunda CoreDNS bir süre o sunucuya yönelmeye devam edebilir ve bu cluster içindeki DNS çözümlemelerini ciddi ölçüde yavaşlatır. Ben bunu canlı ortamda yaşadım, etkisi çok hızlı hissediliyor.

CoreDNS pod’larını yeniden başlatarak yapılandırmayı uygulayın:

kubectl rollout restart deployment/coredns -n kube-system
kubectl rollout status deployment/coredns -n kube-system

Doğrulama ve Test

Yapılandırma sonrası her şeyin düzgün çalıştığını doğrulamak kritik. Birkaç farklı yöntem:

# CoreDNS servisini başlat ve durumunu kontrol et
sudo systemctl start coredns
sudo systemctl status coredns

# Logları izle
sudo journalctl -u coredns -f

# Standart DNS sorgusu testi
dig @localhost -p 53 google.com

# DoT bağlantı testi - kdig (knot-dnsutils paketi) ile
sudo apt install knot-dnsutils  # veya: yum install knot-utils
kdig -d @localhost:853 +tls google.com

# openssl ile TLS bağlantısını test et
openssl s_client -connect localhost:853 -servername dns.internal.example.com

# nmap ile port durumunu kontrol et
nmap -p 853 localhost

DoT bağlantısını test etmek için kdig oldukça değerli bir araç. -d parametresi debug modunu açar ve TLS el sıkışma detaylarını gösterir. Sertifika sorunlarını bu çıktıdan yakalayabilirsiniz.

Upstream TLS bağlantısını doğrulamak için CoreDNS’in detaylı log üretmesini sağlayın:

# Corefile'a geçici olarak debug ekleyin
# forward bloğunun içine şunu ekleyin:
# debug

# Alternatif: CoreDNS'i geçici olarak verbose modda çalıştırın
sudo coredns -conf /etc/coredns/Corefile -dns.port 5353 2>&1 | head -50

Güvenlik Duvarı Kuralları

DoT sunucusu çalıştırıyorsanız 853 portunu açmanız gerekiyor:

# iptables ile
sudo iptables -A INPUT -p tcp --dport 853 -j ACCEPT
sudo iptables -A INPUT -p udp --dport 853 -j ACCEPT
sudo iptables-save | sudo tee /etc/iptables/rules.v4

# firewalld ile
sudo firewall-cmd --permanent --add-port=853/tcp
sudo firewall-cmd --permanent --add-port=853/udp
sudo firewall-cmd --reload

# UFW ile
sudo ufw allow 853/tcp
sudo ufw allow 853/udp
sudo ufw reload

Eğer yalnızca iç ağ istemcilerinin bağlanmasını istiyorsanız kaynak IP kısıtlaması ekleyin:

# Sadece 192.168.0.0/16 ağından gelen bağlantılara izin ver
sudo iptables -A INPUT -p tcp --dport 853 -s 192.168.0.0/16 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 853 -j DROP

Sertifika Yenileme Otomasyonu

Let’s Encrypt kullanıyorsanız sertifika yenilemeyi otomatize etmek gerekiyor. CoreDNS reload eklentisi ile sertifikaları otomatik yükleyebiliyor ama bunu tetiklemek için servis yeniden başlatılmalı veya SIGUSR1 sinyali gönderilmeli:

# /etc/letsencrypt/renewal-hooks/post/ dizinine betik ekle
cat << 'EOF' | sudo tee /etc/letsencrypt/renewal-hooks/post/reload-coredns.sh
#!/bin/bash
# Let's Encrypt yenilemesi sonrası CoreDNS'i yeniden yükle
systemctl reload coredns || systemctl restart coredns
logger "CoreDNS TLS sertifikası yenilendi ve servis yeniden yüklendi"
EOF

sudo chmod +x /etc/letsencrypt/renewal-hooks/post/reload-coredns.sh

# Test et
sudo certbot renew --dry-run

CoreDNS reload eklentisi Corefile değişikliklerini otomatik algılar ama TLS sertifika değişiklikleri için tam bir servis yeniden başlatması gerekebilir. Bunu systemctl reload yerine systemctl restart ile yapmak daha güvenli.

Performans İzleme

Üretim ortamında DNS performansını izlemek için CoreDNS’in Prometheus metriklerini aktifleştirin. Bu metrikleri zaten yapılandırmanın içinde kullandık:

# Prometheus metriklerini kontrol et
curl -s http://localhost:9153/metrics | grep coredns_dns_requests_total

# Önemli metrikler:
# coredns_dns_requests_total - toplam sorgu sayısı
# coredns_dns_responses_total - yanıt sayısı ve RCODE dağılımı
# coredns_forward_requests_total - upstream'e iletilen sorgular
# coredns_cache_hits_total - cache hit sayısı
# coredns_tls_handshake_errors_total - TLS el sıkışma hataları

TLS el sıkışma hatalarını izlemek özellikle önemli. coredns_tls_handshake_errors_total metriği anormal yükseliyorsa ya sertifika sorunu var, ya istemci tarafında uyumsuzluk var, ya da birileri geçersiz bağlantı deniyor demektir.

Grafana dashboard’u için CoreDNS’in resmi olarak sağladığı dashboard ID 5926’yı Grafana.com’dan import edebilirsiniz. Upstream’in nasıl performans gösterdiğini, cache hit oranlarını ve TLS bağlantı durumlarını güzel grafiklerle gösterir.

Yaygın Sorunlar ve Çözümleri

DoT yapılandırmalarında sıkça karşılaşılan sorunlar ve nasıl ele alınacağına dair birkaç not:

TLS bağlantısı kurulamıyor: İlk kontrol noktası tls_servername değeri. Upstream resolver’ın sertifikasındaki CN veya SAN ile eşleşmeli. Quad9 için dns.quad9.net, Cloudflare için cloudflare-dns.com, Google için dns.google kullanın.

Sertifika doğrulama hatası: Self-signed sertifika kullanıyorsanız istemci tarafında CA sertifikasının güvenilir olarak tanınması gerekiyor. Sisteme CA eklemek için update-ca-certificates komutunu kullanın.

Yüksek gecikme: DoT her sorgu için TLS el sıkışması yapmaz; bağlantı yeniden kullanılır. Ama ilk bağlantı kurulumu zaman alır. CoreDNS bunu connection pooling ile yönetiyor. Eğer latency sorunları yaşıyorsanız expire değerini artırın, böylece bağlantılar daha uzun açık kalır.

Port 53 ve 853 çakışması: Sistemde başka bir DNS servisi (systemd-resolved gibi) zaten 53 portunu dinliyorsa CoreDNS başlamaz. ss -tulnp | grep :53 ile kontrol edin ve gerekirse systemd-resolved’ü devre dışı bırakın ya da stub resolver moduna alın.

Sonuç

CoreDNS ile DNS over TLS yapılandırması başta karmaşık görünebilir ama birkaç kez kurulduktan sonra oldukça yönetilebilir bir yapı ortaya çıkıyor. Temel alınması gereken yaklaşım şu: önce upstream’e TLS ile bağlanmayı kur ve doğrula, sonra kendi sunucunu DoT olarak dışa aç.

Kubernetes ortamlarında upstream DoT desteği eklemek neredeyse risk sıfırla yapılabilen bir değişiklik ve DNS trafiğinize ciddi bir güvenlik katmanı ekliyor. Bağımsız kurulumlar için ise tam bir DoT sunucusu işletmek, iç ağınızdaki tüm cihazların şifreli DNS kullanmasını merkezi olarak sağlamanın en temiz yolu.

Son olarak şunu söyleyeyim: DNS şifrelemesi tek başına bir güvenlik çözümü değil. Ağ segmentasyonu, erişim kontrolleri ve izleme mekanizmalarıyla birlikte değer kazanıyor. Ama başlangıç noktası olarak DNS trafiğini şifrelemek, özellikle sıfır maliyetiyle düşünüldüğünde, ertelenmemesi gereken bir adım.

Bir yanıt yazın

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