Google Açılıyor, Şirket Sitesi Açılmıyor: Split DNS, Hairpin NAT ve Proxy

İnternet çalışırken tek bir kurumsal adrese ulaşılamıyorsa: istemcinin gerçekte hangi IP'yi aldığını, o IP'ye giden yolun hairpin NAT gerektirip gerektirmediğini, tarayıcının proxy kullanıp kullanmadığını ve TCP, TLS ve HTTP aşamalarını kanıtla ayırmak.

Standartlar RFC 9499 (split DNS tanımı), RFC 5382 (TCP hairpinning), RFC 9110 (CONNECT), RFC 6066 (SNI), RFC 2606, RFC 5737
Resmi dokümana göre
Microsoft Windows Server 2016–2025, Use DNS Policy for Split-Brain DNS Deployment; nslookup; Resolve-DnsName ve Get-DnsClientNrptPolicy (DnsClient modülü)
Resmi dokümana göre
Fortinet FortiOS 7.6.5 Administration Guide (Hairpin NAT); 7.6.2 CLI Reference (config web-proxy explicit)
Resmi dokümana göre
Tarayıcılar ve curl Chrome Enterprise ilkesi DnsOverHttpsMode; Firefox canary domain (use-application-dns.net); MDN PAC dosyası; curl man page (--connect-to, --noproxy, --proxy)
Resmi dokümana göre

Kullanıcı şikâyeti tanıdıktır: “İnternet çalışıyor, Google açılıyor, ama şirketin kendi sitesi açılmıyor.” Genel erişim sağlam olduğu için sorun, o tek adrese giden yolun bir yerindedir. Bu yazı o yolu dört aşamada ayırıyor: istemcinin gerçekte hangi IP’yi aldığı, o IP’ye nasıl gidildiği, tarayıcının araya proxy koyup koymadığı ve son olarak TCP, TLS ve HTTP.

Genel DNS ve bağlantı testleri Wi-Fi bağlı ama internet yok ve Ağ Sorun Giderme Komutları yazılarında anlatıldı; burada tekrar edilmiyor. “IP ile erişim var, isim çözülmüyor” belirtisinin kurum içi adlarla ilgili kısmı da bu yazıda ele alınıyor. Komutlar resmî belgelerden alındı, laboratuvarda denenmedi. Çıktılar temsilîdir.

Senaryo

Sitenin kampüs topolojisine bir şirket uygulaması ekleniyor:

Öğe Değer
İstemci 10.0.20.25 (VLAN 20), gateway core SVI 10.0.20.1
İç DNS 10.0.30.11, Windows Server DNS
Uygulama sunucusu 10.0.30.40 (VLAN 30), gateway core SVI 10.0.30.1
Alan adı portal.example.com
Genel adres (VIP) 203.0.113.10; ISP’nin firewall’a yönlendirdiği 203.0.113.8/29 bloğundan
Firewall FortiGate; iç tarafı 10.0.254.2, core ile arası 10.0.254.0/30
Açık proxy FortiGate üzerinde, 10.0.254.2:8080; tarayıcılar PAC dosyasıyla yönlendiriliyor

Dışarıdan gelen bir kullanıcı için genel DNS, portal.example.com’u 203.0.113.10 olarak döndürür. FortiGate bu adresi bir VIP ile 10.0.30.40’a çevirir (DNAT). Alan adı RFC 2606, adresler RFC 5737 kapsamında belgeleme içindir. VLAN’lar arası yönlendirme core switch’te yapılır; bu yüzden VLAN 20 ile VLAN 30 arasındaki trafik firewall’dan geçmez.

1 · İÇ YANIT · portal.example.com → 10.0.30.40 İstemci 10.0.20.25 · VLAN 20 Core SVI .20.1 / .30.1 FortiGate 10.0.254.2 · VIP 203.0.113.10 Sunucu 10.0.30.40 · VLAN 30 bu yolda değil DNS: iç kapsamdan yanıt (10.0.30.11) Trafik core'da yönlenir, FW yolda değil. Sunucu gerçek kaynağı görür: 10.0.20.25 2 · GENEL YANIT · portal.example.com → 203.0.113.10 · SNAT YOK İstemci 10.0.20.25 · VLAN 20 Core SVI .20.1 / .30.1 FortiGate 10.0.254.2 · VIP 203.0.113.10 Sunucu 10.0.30.40 · VLAN 30 1 1 2 3 4 yanıt 10.0.30.40'tan beklenen 203.0.113.10 eşleşmez → zaman aşımı 1 SYN, hedef 203.0.113.10 (default route) 2 FW: DNAT → 10.0.30.40, kaynak aynı 3 SYN-ACK 10.0.30.40 → 10.0.20.25 4 core doğrudan iletir, FW atlanır SNAT açık olsaydı sunucu SYN'i 10.0.254.2'den görür, yanıt FortiGate'e döner ve oturum tamamlanır.
İç yanıtta trafik firewall'a uğramaz. Genel yanıtta hairpin yalnızca hedefi çevirirse sunucunun yanıtı core'dan doğrudan istemciye döner ve el sıkışma tamamlanmaz.

1. DNS: İstemci gerçekte hangi IP’yi alıyor?

Split DNS iki sunucu demek değildir. RFC 9499’a göre split DNS, aynı alan adına sorgunun kaynağına göre kısmen ya da tamamen farklı yanıt verilmesidir. Bu iki ayrı sunucuyla da, tek bir sunucuyla da yapılabilir. Windows Server 2016 ve sonrasında DNS politikaları, sorgunun geldiği arayüze ya da istemci alt ağına göre aynı bölgenin farklı kapsamlarından (zone scope) yanıt verebilir. Bu senaryoda iç yanıt 10.0.30.40, dış yanıt 203.0.113.10 olmalıdır.

İlk karşılaştırma, aynı adın iki görünüşünü yan yana koymaktır:

nslookup portal.example.com 10.0.30.11
nslookup portal.example.com <dış çözümleyici>
Resolve-DnsName portal.example.com

İlk iki komut belirli bir DNS sunucusunu sorgular. Üçüncüsü sunucu belirtmeden, işletim sisteminin ayarlarıyla çözümler; -NoHostsFile ve -CacheOnly seçenekleri hosts dosyasının ve önbelleğin etkisini ayrıca sınamaya izin verir.

Komutun yanıtı, uygulamanın yanıtı olmayabilir. nslookup ya da dig’in döndürdüğü adres, tarayıcının ya da VPN istemcisinin gerçekte kullandığı adres olmak zorunda değildir:

  • Tarayıcının kendi DNS’i: Tarayıcı DNS over HTTPS (DoH) ile işletim sisteminin DNS’ini atlayabilir. Chrome’un DnsOverHttpsMode ilkesi bunu kurumsal olarak off, automatic ya da secure yapar. İlke ayarlanmamışsa yönetilen cihazlarda DoH sorgusu gönderilmez. Firefox, varsayılan DoH’u ağın canary alan adına (use-application-dns.net) negatif yanıt vermesiyle kapatır; ama bu sinyal kullanıcının DoH’u kendi açtığı durumda uygulanmaz.
  • VPN istemcisi: VPN istemcisi belirli alan adlarının sorgularını kendi DNS sunucusuna yönlendirebilir. Windows’ta bu kurallar Ad Çözümleme İlke Tablosu’nda (NRPT) durur ve Get-DnsClientNrptPolicy ile görülür.
  • hosts dosyası ve önbellek: Eski bir hosts girdisi ya da önbellekteki eski bir kayıt, sunucudaki doğru kaydı gölgeleyebilir.

Bu yüzden en güçlü kanıt, uygulamanın gerçekte hangi adrese bağlandığıdır. curl -v bunu Trying satırında yazar; firewall loglarındaki hedef adres de aynı soruya yanıt verir.

2. Ağ yolu: Çözülen adrese nasıl gidiliyor?

İç yanıt alınmışsa (10.0.30.40): İstemci sunucuya core üzerinden doğrudan gider; firewall yolda değildir. Bağlantı kurulamıyorsa bakılacak yer core’daki ACL’ler, sunucunun kendi firewall’u ve VLAN’lar arası yoldur. Hairpin NAT bu durumda konu dışıdır.

Genel yanıt alınmışsa (203.0.113.10): İstemci paketi default route ile firewall’a gönderir. FortiGate VIP ile hedefi 10.0.30.40’a çevirir ve paketi aynı iç arayüzden core’a geri yollar. Bu U dönüşüne hairpin denir. RFC 5382’ye göre hairpinning, iç uç noktaların birbirine yalnızca dış adresleriyle tanındığı durumda iletişimi mümkün kılar.

Sorun dönüş yolundadır. Firewall yalnızca hedefi çevirirse sunucuya ulaşan SYN’in kaynağı istemcinin gerçek adresi olur: 10.0.20.25. Sunucu yanıtı kendi gateway’ine, yani core’a verir; core da onu doğrudan VLAN 20’ye iletir. Yanıt firewall’a hiç uğramaz ve kaynağı 10.0.30.40 olarak istemciye ulaşır. İstemci ise 203.0.113.10 ile konuştuğunu sanmaktadır; bu yanıtı kendi bağlantısıyla eşleştirmez ve el sıkışma tamamlanmaz. Firewall’da da yanıtı hiç görmemiş bir oturum kalır. Bu, stateful firewall yazısında anlatılan asimetrinin bir türüdür.

Çözüm kaynağı da çevirmektir. Fortinet’in FortiOS 7.6.5 hairpin NAT örneğinde VIP iç arayüzden gelen trafiği de eşleyecek şekilde extintf any ile tanımlanır, VIP’i kullanan politikada NAT açıktır ve trafik log’unda trandisp="snat+dnat" görünür: hedef sunucuya, kaynak FortiGate’in iç arayüz adresine çevrilmiştir. Bu senaryoda sunucu SYN’i 10.0.254.2’den gelmiş görür; yanıtı da firewall’a döner ve oturum tamamlanır. Kanıt için:

execute log filter category 0
execute log display
get system session list | grep 203.0.113.10

Log satırında tranip hedefin, transip kaynağın çevrildiği adresi gösterir. transip alanının olmaması tek başına kanıt değildir; trandisp değeri, oturum listesindeki SOURCE-NAT sütunu ve özellikle sunucu tarafında yakalanan SYN’in kaynağının 10.0.20.25 olması birlikte hairpin’in SNAT’sız çalıştığını doğrular.

Split DNS mi, hairpin mi? İkisi de çalışır, ama farklı sonuç verir:

  • Split DNS: Bu topolojide iç trafik firewall’a uğramaz; yük oluşturmaz, ama firewall politikası ve incelemesi de uygulanmaz. Sunucu istemcinin gerçek adresini görür. İç ve dış kayıtların ayrı ayrı güncel tutulması gerekir.
  • Hairpin NAT ile SNAT: İç kullanıcı da dış kullanıcıyla aynı adresi kullanır ve trafik firewall politikasından geçer. Buna karşılık firewall’a ek yük biner ve sunucu loglarında bütün iç kullanıcılar firewall’un adresiyle görünür.

3. Proxy: Tarayıcı bağlantıyı kime açıyor?

Tarayıcı bir PAC dosyası kullanıyorsa her adres için FindProxyForURL fonksiyonunu çalıştırır. Fonksiyon DIRECT döndürürse tarayıcı sunucuya kendisi bağlanır; PROXY host:port döndürürse isteği proxy’ye verir. PAC dosyasının konumu elle, grup ilkesiyle ya da WPAD ile otomatik bulunarak verilebilir.

Proxy kullanılıyorsa istemcinin DNS yanıtı belirleyici olmayabilir. HTTPS için tarayıcı proxy’ye CONNECT portal.example.com:443 gönderir. RFC 9110’a göre CONNECT, proxy’den hedefe bir tünel kurmasını ister; hedef adı çözen ve bağlantıyı açan proxy’dir. Proxy adı kendi DNS ayarlarıyla çözer. Genel adresi alırsa ya da politikası bu iç hedefe izin vermiyorsa, istemci doğru iç adresi alsa bile tarayıcıda site açılmaz.

Her uygulama PAC okumaz. curl’ün belgesine göre curl proxy’yi --proxy seçeneğinden ya da HTTPS_PROXY gibi ortam değişkenlerinden alır; PAC dosyası kullanmaz. “curl çalışıyor, tarayıcı çalışmıyor” gözlemi bu yüzden sunucunun sağlam olduğunu değil, iki uygulamanın farklı yollardan gittiğini gösterebilir.

Hata türünü ayırmak: Proxy’nin döndürdüğü hata sayfası proxy’nin adını ya da kendi hata metnini taşır. Ad çözülemezse tarayıcı bunu DNS hatası olarak bildirir. Sertifika sorunlarında ise tarayıcı bir sertifika uyarısı gösterir. Üçü farklı aşamalardır ve aynı düzeltmeyle çözülmez.

4. Son doğrulama: TCP, TLS ve HTTP

HTTPS’i doğrudan IP adresiyle denemek, örneğin https://10.0.30.40/, yanıltıcıdır. RFC 6066’ya göre SNI alanına IP adresi yazılamaz; IP adresiyle bağlanan istemci sunucuya hangi siteyi istediğini SNI ile bildiremez, Host başlığında da IP adresi gider. Sunucu varsayılan sertifikasını ya da başka bir siteyi döndürebilir; istemci de sertifikayı alan adına değil IP adresine göre doğrular. Doğru test, adı korurken bağlantının gideceği adresi seçmektir. curl’ün --connect-to seçeneği yalnızca ağ bağlantısının adresini değiştirir; TLS’te kullanılan adı (SNI ve sertifika doğrulaması) ve HTTP isteğini etkilemez:

curl -v --noproxy "*" --connect-to portal.example.com:443:10.0.30.40:443 https://portal.example.com/
curl -v --noproxy "*" --connect-to portal.example.com:443:203.0.113.10:443 https://portal.example.com/
curl -v --noproxy "" --proxy http://10.0.254.2:8080 https://portal.example.com/

İlk komut iç yolu, ikincisi hairpin yolunu proxy olmadan dener. Üçüncüsü aynı isteği tarayıcının PAC ile kullandığı proxy üzerinden gönderir; --noproxy "", ortamda tanımlı NO_PROXY değişkeninin proxy’yi devre dışı bırakmasını engeller. -v çıktısı aşamaları sırayla gösterir. Hairpin yolunda SNAT yoksa beklenen tablo şuna benzer; çıktı temsilîdir:

* Connecting to hostname: 203.0.113.10
*   Trying 203.0.113.10:443...
* connect to 203.0.113.10 port 443 from 10.0.20.25 port 50432 failed: Connection timed out
* Failed to connect to portal.example.com port 443: Couldn't connect to server

Satırların tam metni curl sürümüne göre değişir.

Çıktının nerede durduğu sorunun aşamasını gösterir: Trying satırından sonra bağlantı kurulmazsa sorun yol ya da NAT’tadır; sertifika hatası TLS’tedir; bir HTTP durum kodu dönüyorsa ağ ve TLS tamamdır ve yanıt uygulamadan ya da proxy’den gelir.

Tarayıcı proxy kullanıyor mu? PAC, WPAD ya da elle ayar Proxy yolu CONNECT ile adı proxy çözer curl --proxy ile aynı yolu dene evet hayır: DIRECT 1 · DNS uygulama hangi IP'ye bağlanıyor? Ad çözülmüyor hosts, önbellek, NRPT, DoH uygulamanın çözümleyicisi yok IP var 2 · TCP el sıkışma tamamlanıyor mu? Yol ya da NAT genel IP: hairpin, SNAT, asimetri iç IP: ACL, sunucu firewall'u hayır evet 3 · TLS sertifika ya da SNI hatası var mı? Sertifika ve ad adı koru: curl --connect-to IP ile HTTPS testi yanıltır evet hayır 4 · HTTP durum kodu ya da hata sayfası kimden? Uygulama ya da proxy sunucu yanıtı: uygulama tarafı proxy sayfası: proxy politikası
Proxy sorusu en başta sorulur, çünkü proxy kullanılıyorsa adı da bağlantıyı da proxy kurar. Sonra DNS, TCP, TLS ve HTTP sırayla elenir.

Kendinizi deneyin

Alıştırma 1: Bir kullanıcıda tarayıcı portal.example.com’da zaman aşımına düşüyor. Kanıtlar: (1) nslookup portal.example.com 10.0.30.11 ve Resolve-DnsName portal.example.com 10.0.30.40 döndürüyor. (2) curl -v --noproxy "*" https://portal.example.com/ 10.0.30.40’a bağlanıyor ve HTTP 200 alıyor. (3) FortiGate’in trafik log’unda bu istemciden 203.0.113.10:443’e oturumlar var; log’da tranip=10.0.30.40 var, transip alanı yok. (4) Kullanıcının tarayıcısında DoH, genel bir sağlayıcıyla elle açılmış. Bu kullanıcı grubu proxy kullanmıyor. Neler oluyor ve hangi iki düzeltme yolu var?

Cevap ve açıklama
  • İki farklı çözümleme yolu var. İşletim sistemi iç DNS’i kullanıyor ve iç yanıtı alıyor (1); curl da bu yolu kullandığı için çalışıyor (2). Tarayıcı ise elle açılmış DoH ile genel bir çözümleyiciye gidiyor ve genel adresi, 203.0.113.10’u alıyor (3, 4). Firefox’un canary alan adı burada işe yaramaz, çünkü kullanıcı DoH’u kendisi açmış.
  • Genel adrese giden yol SNAT’sız bir hairpin. Log’da yalnızca hedef çevirisi görünüyor (3); ancak transip alanının olmaması tek başına yeterli değildir, trandisp değeri ve oturum listesindeki SOURCE-NAT sütunuyla birlikte değerlendirilir. Sunucu SYN’i 10.0.20.25’ten görüyor ve yanıtı core üzerinden doğrudan istemciye gönderiyor. İstemci bu yanıtı 203.0.113.10 ile kurduğu bağlantıyla eşleştirmediği için tarayıcı zaman aşımına düşüyor.
  • Doğrulama: Son doğrulama bölümündeki ikinci curl komutu, yani hairpin yolunu --connect-to ile adı koruyarak deneyen komut, aynı zaman aşımını üretmeli; kesin kanıt, sunucu tarafında yakalanan SYN’lerin kaynağının 10.0.20.25 olmasıdır.
  • Düzeltme 1, DNS yolunu yönetmek: Tarayıcıların DoH ayarı kurumsal ilkeyle yönetilir; örneğin Chrome’da DnsOverHttpsMode. Tarayıcı iç DNS’i kullandığında iç yanıtı alır ve firewall’a hiç gitmez.
  • Düzeltme 2, hairpin’i tamamlamak: VIP’i kullanan politikada kaynak NAT açılır. O zaman genel adresi alan her istemci de bağlanabilir; bu, DoH’u yönetilemeyen cihazlar için de güvenlik ağıdır. İki düzeltme birbirini dışlamaz.

Alıştırma 2: Başka bir kullanıcı grubu PAC dosyası kullanıyor. Tarayıcıda portal.example.com açılmıyor ve FortiGate proxy’sinin hata sayfası görünüyor; sertifika uyarısı yok. curl -v https://portal.example.com/ ise 10.0.30.40’a bağlanıp HTTP 200 alıyor. PAC dosyasının ilgili kısmı:

function FindProxyForURL(url, host) {
  if (dnsDomainIs(host, ".corp.example.com")) return "DIRECT";
  return "PROXY 10.0.254.2:8080";
}

Sorun DNS’te mi, proxy’de mi, TLS’te mi? Nasıl doğrularsınız?

Cevap ve açıklama
  • DNS değil. curl aynı makinede adı çözüp iç adrese bağlanıyor. Tarayıcı da ad çözme hatası değil, proxy’nin hata sayfasını gösteriyor.
  • TLS kesin olarak dışlanmış değil. Sertifika uyarısı olmaması TLS’i tek başına dışlamaz. Görünen sayfa proxy’nin hata sayfası olduğu için hata büyük olasılıkla proxy tarafında üretiliyor: CONNECT aşamasında ya da proxy hedefe bağlanmaya çalışırken.
  • Proxy yolu. portal.example.com, .corp.example.com koşuluna uymuyor; PAC onu proxy’ye gönderiyor. Tarayıcı proxy’ye CONNECT portal.example.com:443 gönderiyor; adı proxy kendi DNS ayarlarıyla çözüyor ve bağlantıyı kendisi açmaya çalışıyor. curl ise PAC okumadığı için doğrudan gidiyor ve bu yüzden çalışıyor.
  • Doğrulama: Son doğrulama bölümündeki üçüncü komut, --noproxy "" ile NO_PROXY etkisini kaldırarak tarayıcının proxy yolunu tekrarlar ve aynı hatayı üretmelidir. CONNECT isteğine dönen yanıt (-v çıktısındaki durum satırı) ve proxy’nin log’u, hatanın tünel kurulurken mi yoksa proxy hedefe bağlanırken mi oluştuğunu ayırır.
  • Düzeltme: İç uygulama PAC’te DIRECT listesine eklenir, örneğin dnsDomainIs(host, "portal.example.com") koşuluyla. Uygulamanın proxy üzerinden kullanılması isteniyorsa proxy’nin bu adı iç adrese çözmesi ve politikasının bu hedefe izin vermesi sağlanır. Hangisinin seçileceği güvenlik politikasına bağlıdır.

Kaynaklar

#dns#split-dns#hairpin-nat#proxy#sorun-giderme

Kemal Keleş

Network & Security Engineer · CCNA · HCSA · NSE 3

Switching, routing, firewall ve kampüs ağlarında presales ve saha deneyimimi, teknik araştırmalarımı ve üretici dokümantasyonlarını bir araya getirerek pratik, üreticiden bağımsız içerikler hazırlıyorum.

Aynı konudan