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. 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,automaticya dasecureyapar. İ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-DnsClientNrptPolicyile 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.
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
transipalanının olmaması tek başına yeterli değildir,trandispdeğ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-toile 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.comkoşuluna uymuyor; PAC onu proxy’ye gönderiyor. Tarayıcı proxy’yeCONNECT portal.example.com:443gö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 ""ileNO_PROXYetkisini 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
DIRECTlistesine eklenir, örneğindnsDomainIs(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
- RFC 9499: DNS Terminology (split DNS)
- RFC 5382: NAT Behavioral Requirements for TCP (hairpinning)
- RFC 9110: HTTP Semantics (CONNECT)
- RFC 6066: TLS Extensions (Server Name Indication)
- RFC 2606: Reserved Top Level DNS Names
- RFC 5737: IPv4 Address Blocks Reserved for Documentation
- Microsoft: Use DNS Policy for Split-Brain DNS Deployment
- Microsoft: nslookup
- Microsoft: Resolve-DnsName
- Microsoft: Get-DnsClientNrptPolicy
- Google: Chrome Enterprise ilkeleri, DnsOverHttpsMode
- Mozilla: Canary domain, use-application-dns.net
- MDN: Proxy Auto-Configuration (PAC) file
- curl: man page
- Fortinet: Hairpin NAT (FortiOS 7.6.5)
- Fortinet: config web-proxy explicit (FortiOS 7.6.2 CLI Reference)