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.
