Ders 02 / 16
Ters Vekil Yapılandırması
İki katlı bir vekil zincirinden gerçek istek geçirilerek kaybolan dört bilginin sayılması: istemci adresi, protokol, sunucu adı ve isteğin özgün yolu; her kaybın uygulamada ürettiği yanlış karar (üç üyenin tek kimlikle kaydedilmesi, iç adrese giden mutlak bağlantı, önek soyulunca 404 veren bağ), ikinci atlamanın ilk atlamanın aktarımını silmesi ve aktarılan başlığa güvenmenin açtığı iddia edilen kimlik yüzeyi.
İçindekiler
Önceki ders yüzeyi sunucunun dışından saydı: kaç kapı dinliyor, kaç yol sunuluyor, kaç uç açık. Sayımın tamamı isteğin varmasıyla ilgiliydi. Bu ders istek vardıktan sonrasını ölçer.
Ters vekil isteği alır ve arkaya iletir. “İletir” sözcüğü olup biteni gizler: vekil isteği taşımaz, yeniden kurar. İstemcinin açtığı bağlantı vekilde biter; vekil arkaya kendi adına yeni bir bağlantı açar. Yeni bağlantının öteki ucunda istemci yoktur, vekil vardır. Bağlantının kendisinde taşınan ne varsa o noktada silinir. Ters vekilin ne olduğu ve uygulamayı üretime alırken nasıl yapılandırıldığı daha önce ölçülmüştü; burada ölçülen kaybolan bilgi kalemi ve o kaybın açtığı yüzeydir.
Kurgu bölgesel ölçüm ağının önünde iki vekil vardır: dışa bakan bir kenar vekil ve ortamı seçen
bir iç vekil. Saha el terminalleri /api önekiyle kenar vekile bağlanır, kenar vekil iç vekile,
iç vekil ölçüm uygulamasına. İki atlama, üç bağlantı.
- WS7. Uygulama aktarım başlığındaki soldaki ilk girdiyi okur; aktarım zinciri virgülle uzadığında okunan değer değişmez. Bu, aktarılan kimliğin yaygın okunma biçimidir.
- WS8. Tek makinede bütün istemciler aynı geri döngü adresini paylaşır. Kurgu üye adresi kenar vekilde kaynak bağlantı noktası bloğundan türetilir; gerçek bir dağıtımda bu değer doğrudan bağlantıdan gelir.
- WS9. Kenar vekil ile iç vekil aynı dosyadır; aralarındaki bütün fark ayar dizgesindedir.
- WS10. Uygulamanın iç özet ucu yalnız
10.9.önekli adreslerden çağrılabilir; erişim kararı çözülen adrese dayanır. - WS11. Ölçüm süre ölçmez; istek sayısı, kaynak bağlantı noktası blokları ve beklenen değerler koşumdan bağımsız sabitlerdir.
// zincir/uygulama.mjs — olcum uygulamasi. Istegin dort bilgisini once "x-ileti-*" basligindan, // yoksa baglantinin kendisinden cozer ve her cozumden bir karar uretir. import { createServer } from "node:http"; const nokta = Number(process.argv[2]); const IC_AG = "10.9."; // ic ag oneki const kayit = { adres: new Set(), giren: 0, reddedilen: 0 }; const coz = (i) => ({ // Aktarim zinciri virgulle uzar; uygulama soldaki ilk girdiyi okur. adres: String(i.headers["x-ileti-adres"] ?? i.socket.remoteAddress).split(",")[0].trim(), sema: i.headers["x-ileti-sema"] ?? "sifresiz", ad: i.headers["x-ileti-ad"] ?? i.headers.host, onek: i.headers["x-ileti-onek"] ?? "", }); if (Number.isInteger(nokta) === false) console.log("kullanim: node zincir/uygulama.mjs <nokta>"); else createServer((istek, yanit) => { yanit.sendDate = false; const yol = istek.url; const c = coz(istek); if (yol === "/dokum") { yanit.writeHead(200).end(JSON.stringify({ ...kayit, kimlik: kayit.adres.size, adres: [...kayit.adres] })); return; } if (yol === "/ic/ozet") { // yalniz ic agdan cagrilmasi gereken uc if (c.adres.startsWith(IC_AG)) { kayit.giren += 1; yanit.writeHead(200).end("ic ozet"); } else { kayit.reddedilen += 1; yanit.writeHead(403).end("disaridan"); } return; } const parca = yol.split("/"); if (parca[1] !== "olcum" || parca.length > 4) { yanit.writeHead(404).end("yol yok"); return; } if (parca.length === 4 && parca[3] !== "ayrinti") { yanit.writeHead(404).end("yol yok"); return; } kayit.adres.add(c.adres); // denetim kaydina yazilan kimlik yanit.writeHead(200, { "x-adres": c.adres, "x-sema": c.sema, "x-ad": c.ad, "x-onek": c.onek || "yok", "x-karar": c.sema === "sifreli" ? "sun" : "yonlendir", "x-bag": `${c.sema}://${c.ad}${c.onek}${yol}/ayrinti`, }).end("olcum"); }).listen(nokta, "127.0.0.1");
Vekil altı ayarı bir dizgeden alır. Bir üründe bunların her biri bir yapılandırma satırıdır; burada bir dizge alanıdır.
// zincir/vekil.mjs — ters vekil. Ayni dosya hem kenar hem ic vekil olarak kosar; davranisi ayar // dizgesi belirler. Ayar: adres,sema,ad,onekb = acik|kapali; onek=<yol>; temizle=acik; // zincir=yaz|ekle|koru; harita=acik (yalniz kenar vekilde: baglanti -> kurgu istemci adresi). import { createServer, request } from "node:http"; const [nokta, hedef, ayarMetni] = [Number(process.argv[2]), Number(process.argv[3]), process.argv[4] ?? ""]; const A = Object.fromEntries(ayarMetni.split(",").filter(Boolean).map((p) => p.split("="))); // WS8: tek makinede butun istemciler ayni geri dongu adresini paylasir. Kurgu uye adresi kaynak // baglanti noktasi blogundan turetilir; gercek dagitimda deger dogrudan baglantidan gelir. const AG = { 512: "203.0.113.11", 513: "203.0.113.12", 514: "203.0.113.13" }; const kaynak = (s) => (A.harita === "acik" ? AG[Math.floor(s.remotePort / 100)] : undefined) ?? s.remoteAddress; if (Number.isInteger(nokta) === false || Number.isInteger(hedef) === false) { console.log("kullanim: node zincir/vekil.mjs <nokta> <hedef> <ayar>"); } else createServer((disIstek, disYanit) => { disYanit.sendDate = false; const b = { ...disIstek.headers }; if (A.temizle === "acik") for (const k of Object.keys(b)) if (k.startsWith("x-ileti-")) delete b[k]; const gelen = b["x-ileti-adres"]; if (A.adres === "acik") { const kendi = kaynak(disIstek.socket); if (A.zincir === "koru" && gelen) b["x-ileti-adres"] = gelen; else if (A.zincir === "ekle" && gelen) b["x-ileti-adres"] = `${gelen}, ${kendi}`; else b["x-ileti-adres"] = kendi; } if (A.sema === "acik") b["x-ileti-sema"] = "sifreli"; if (A.ad === "acik") b["x-ileti-ad"] = b["x-ileti-ad"] ?? disIstek.headers.host; let yol = disIstek.url; if (A.onek) { if (yol.startsWith(A.onek) === false) { disYanit.writeHead(404).end("yol yok"); return; } yol = yol.slice(A.onek.length) || "/"; // onek soyulur; arka uc onu bilmez if (A.onekb === "acik") b["x-ileti-onek"] = A.onek; } b.host = `127.0.0.1:${hedef}`; // istenen ad yukari akisin adiyla degistirilir const icIstek = request({ port: hedef, path: yol, method: disIstek.method, headers: b }, (icYanit) => { disYanit.writeHead(icYanit.statusCode, icYanit.headers); icYanit.pipe(disYanit); }); icIstek.on("error", () => { if (disYanit.headersSent === false) disYanit.writeHead(502).end("kesildi"); }); disIstek.pipe(icIstek); }).listen(nokta, "127.0.0.1");
İstemci üç üyeyi ayrı kaynak bağlantı noktası bloklarından bağlar, çözülen dört değeri beklenenle karşılaştırır, uygulamanın ürettiği bağlantıyı izler ve denetim kaydındaki kimlik sayısını okur.
// zincir/istemci.mjs — uc uye ayri kaynak baglanti noktasi bloklarindan zincire baglanir. // Kullanim: node zincir/istemci.mjs <kenar> <uygulama> <senaryo> <etiket> <kayma> import { request } from "node:http"; const [kenar, uyg, senaryo, etiket, kayma] = [Number(process.argv[2]), Number(process.argv[3]), process.argv[4], process.argv[5] ?? "-", Number(process.argv[6] ?? 0)]; const AD = "olcum.ornek.gecersiz"; // kurgu ad const BEKLENEN = { adres: "203.0.113.11", sema: "sifreli", ad: AD, onek: "/api" }; const cagir = ({ yol, kaynak, port = kenar, basliklar = {} }) => new Promise((coz) => { const r = request({ port, path: yol, agent: false, localPort: kaynak, headers: { host: AD, ...basliklar } }, (y) => { let g = ""; y.on("data", (d) => { g += d; }); y.on("end", () => coz({ kod: y.statusCode, b: y.headers, govde: g })); }); r.on("error", (e) => coz({ kod: 0, b: {}, govde: e.code })); r.end(); }); if (senaryo === "kayip") { let ilk = null; for (let i = 0; i < 3; i += 1) { const y = await cagir({ yol: "/api/olcum/7", kaynak: 51200 + i * 100 + kayma * 5 }); ilk = ilk ?? y; } const dokum = JSON.parse((await cagir({ yol: "/dokum", port: uyg, kaynak: 51700 + kayma })).govde); const bag = ilk.b["x-bag"] ?? "-"; const yolu = bag.slice(bag.indexOf("://") + 3).replace(/^[^/]*/, ""); const izlenen = await cagir({ yol: yolu, kaynak: 51800 + kayma }); const dogru = ["adres", "sema", "ad", "onek"].filter((k) => ilk.b[`x-${k}`] === BEKLENEN[k]).length; console.log(`${etiket.padEnd(18)}cozulen ${dogru}/4 kayitta ${dokum.kimlik} kimlik ` + `karar ${String(ilk.b["x-karar"]).padEnd(10)}bag izlendi ${izlenen.kod}`); console.log(`${" ".repeat(18)}adres ${String(ilk.b["x-adres"]).padEnd(15)} ad ${String(ilk.b["x-ad"]).padEnd(22)} onek ${ilk.b["x-onek"]}`); } else if (senaryo === "sahte") { let giren = 0; for (let i = 0; i < 4; i += 1) { const y = await cagir({ yol: "/api/ic/ozet", kaynak: 51500 + i + kayma * 10, basliklar: { "x-ileti-adres": "10.9.0.5" } }); if (y.kod === 200) giren += 1; } const dokum = JSON.parse((await cagir({ yol: "/dokum", port: uyg, kaynak: 51900 + kayma })).govde); console.log(`${etiket.padEnd(18)}ic uca giren ${giren}/4 uygulamanin reddettigi ${dokum.reddedilen}`); } else console.log("kullanim: node zincir/istemci.mjs <kenar> <uygulama> <senaryo> <etiket> <kayma>");
# olc.sh — ayni istek zinciri farkli aktarim ayarlariyla. Her kosumda ucu de yeniden baslatilir. K0="onek=/api,harita=acik" # her kosumda ayni: onek soyulur, kaynak haritasi kenarda kos() { # kos <kenar-ek> <ic-ayar> <senaryo> <etiket> <kayma> node zincir/uygulama.mjs 8942 & U=$! node zincir/vekil.mjs 8941 8942 "$2" & I=$! node zincir/vekil.mjs 8940 8941 "$K0$1" & K=$! sleep 0.5 node zincir/istemci.mjs 8940 8942 "$3" "$4" "$5" kill $K $I $U 2>/dev/null; wait $K $I $U 2>/dev/null; sleep 0.2 } D=",temizle=acik,adres=acik,sema=acik,ad=acik,onekb=acik" # dort basligin hepsi aktarilir kos ",temizle=acik" "" kayip "aktarim yok" 0 kos ",temizle=acik,adres=acik,sema=acik" "" kayip "adres+sema" 1 kos "$D" "" kayip "dort baslik" 2 kos "$D" "adres=acik,zincir=yaz" kayip "ikinci atlama" 3 kos ",adres=acik,sema=acik,ad=acik,onekb=acik,zincir=ekle" "" sahte "temizleme yok" 4 kos "$D" "" sahte "temizleme var" 5
aktarim yok cozulen 0/4 kayitta 1 kimlik karar yonlendir bag izlendi 404
adres 127.0.0.1 ad 127.0.0.1:8942 onek yok
adres+sema cozulen 2/4 kayitta 3 kimlik karar sun bag izlendi 404
adres 203.0.113.11 ad 127.0.0.1:8942 onek yok
dort baslik cozulen 4/4 kayitta 3 kimlik karar sun bag izlendi 200
adres 203.0.113.11 ad olcum.ornek.gecersiz onek /api
ikinci atlama cozulen 3/4 kayitta 1 kimlik karar sun bag izlendi 200
adres 127.0.0.1 ad olcum.ornek.gecersiz onek /api
temizleme yok ic uca giren 4/4 uygulamanin reddettigi 0
temizleme var ic uca giren 0/4 uygulamanin reddettigi 4
Sıfırdan Dörde
İlk satır varsayılan durumdur: iki vekil de isteği iletiyor, hiçbiri hiçbir şey aktarmıyor. Uygulama dört bilginin dördünü de kaybediyor ve dört ayrı yanlış karar üretiyor.
İstemci adresi. Üç ayrı üye üç ayrı istek gönderdi; denetim kaydında bir kimlik var. Kayıt eksik değil, dolu — yalnız yazdığı adres iç vekilin adresi. Bir olayın hangi el terminalinden geldiği sorulduğunda kayıt yanıt verir ve yanıtı yanlıştır.
Protokol. Uygulamaya gelen bağlantı şifresizdir, çünkü şifreleme kenar vekilde bitmiştir.
Uygulama şifresiz bir istek gördüğüne karar verip yonlendir diyor. Kararın kendisi doğru
yazılmış bir kuraldır; girdisi yanlıştır.
Sunucu adı. İstemci olcum.ornek.gecersiz adını istedi. Vekil arkaya bağlanırken istenen adı
yukarı akışın adıyla değiştirdi — bu bir hata değil, bağlantının kurulma biçimidir. Uygulama
kendi ürettiği mutlak bağlantıya 127.0.0.1:8942 yazıyor. Bu bağlantı bir sayfada gösterilse ya
da bir bildirim e-postasına konsa, iç adres dışarı çıkmış olur.
Özgün yol. Kenar vekil /api önekini soydu. Uygulama önekten habersiz olduğu için ürettiği
bağlantıya öneki koymuyor. Bağlantı izlendiğinde kenar vekil onu kendi yönlendirme kuralına
uydurmuyor ve 404 dönüyor. Saha ekibine giden bildirim e-postasındaki bağlantı, açıldığı gün
çalışmayan bir bağlantıdır.
İkinci satır iki başlığı açıyor. Denetim kaydındaki kimlik sayısı bire değil üçe çıkıyor ve
karar sun oluyor. İki kalem kapandı; ad ile önek hâlâ kayıp ve bağ hâlâ 404. Bu satır kısmi
aktarımın en yanıltıcı yanını gösteriyor: kayıt düzeldiği için yapılandırmanın tamam olduğu
düşünülür, oysa kapanan iki kalemin ikisi de kaydın içinde görünen kalemlerdi. Kayıtta
görünmeyen iki kalem, kayda bakarak bulunamaz.
Üçüncü satır dört başlığı da açıyor: dört bilginin dördü çözülüyor, kayıtta üç kimlik var, üretilen bağ izlendiğinde 200 dönüyor.
İkinci Atlamanın Sildiği
Dördüncü satırda kenar vekilin ayarı üçüncü satırla birebir aynı. Değişen tek şey iç vekilin de adres aktarımını açması.
Sonuç: çözülen bilgi dörtten üçe düşüyor ve denetim kaydındaki kimlik sayısı üçten bire iniyor. İç vekil, kenar vekilin yazdığı başlığın üstüne kendi gördüğü adresi yazdı; onun gördüğü adres kenar vekilin adresidir. Aktarım iki atlamada da açık olmasına karşın uygulamaya varan değer istemcinin adresi değil.
Bu, ilk satırdan daha zor bulunan bir arızadır. İlk satırda hiçbir aktarım yoktu ve durum bir yapılandırma incelemesinde görülür. Dördüncü satırda her iki vekilde de aktarım açıktır; inceleme “aktarım açık” der ve geçer. Aradaki fark tek bir kelimede — ikinci atlama gelen değeri korumak yerine üzerine yazıyor.
Zincir uzunluğu burada bir çarpan olur: aktarımın doğru çalışması için zincirdeki her atlamanın gelen değeri koruması ya da ona eklemesi gerekir. İki atlamada iki karar, dört atlamada dört karar vardır ve hepsinin doğru olması gerekir; birinin yanlış olması yeter.
Aktarılan Başlığa Güvenmek
Son iki satır ölçünün öteki yüzüdür. Uygulama artık aktarılan adrese karar bağlıyor: iç özet ucu yalnız iç ağ adreslerinden çağrılabiliyor. Kaybı kapatan başlık, kapattığı anda bir yetki girdisine dönüştü.
Beşinci satırda kenar vekil gelen x-ileti-* başlıklarını temizlemiyor ve zincire ekleme
yapıyor. İstemci kendi isteğine 10.9.0.5 yazıyor. Kenar vekil bunun üstüne kendi gördüğü
adresi ekliyor, başlık iki girdili oluyor ve uygulama soldaki ilk girdiyi okuyor: istemcinin
yazdığını. Dört isteğin dördü de iç uca giriyor, uygulama hiçbirini reddetmiyor ve dört
istek kayıtlarda başarılı iç çağrı olarak duruyor.
Altıncı satırda kenar vekil temizliyor. Dört isteğin sıfırı giriyor, uygulama dördünü de reddediyor.
Açılan yüzeyin adı iddia edilen kimlik yüzeyidir: uygulamanın güvendiği her aktarım başlığı, önündeki her atlama onu silmediği sürece istemcinin denetimindeki bir girdidir. Bu yüzey iki biçimde açılır. Birincisi temizlemenin unutulmasıdır — beşinci satır. İkincisi, önceki dersin saydığı yukarı akış kapısıdır: iç vekil ile uygulamanın kapısı makinenin bütün arayüzlerinde dinliyorsa, kenar vekile hiç uğramayan bir istek başlığını kendisi yazar ve temizleme hiç çalışmaz. Kenar vekildeki tek satırlık temizleme kuralı, ancak zincirin tek girişi olduğu sürece bir kural sayılır.
Dört Kalem, İki Yön
Dört bilginin her biri iki ayrı kalem üretir: kapalıyken bir yanlış karar, açıkken bir güven noktası.
| Bilgi | Kapalıyken üretilen yanlış karar | Açıkken doğan yüzey |
|---|---|---|
| İstemci adresi | üç üye kayıtta tek kimliğe düşer | adrese bağlı erişim kararı istemcinin yazdığı değere düşer |
| Protokol | şifreli istek şifresiz sayılır, karar yonlendir olur |
uygulama şifrelemeyi kendisi doğrulamaz, başlığa güvenir |
| Sunucu adı | üretilen bağlantı iç adrese işaret eder | ad başına ayrım yapan kural sahte adla atlatılabilir |
| Özgün yol | önek düşer, üretilen bağ 404 alır | önek başına yetkilendirme kuralı sahte önekle atlatılabilir |
Tablonun sağ sütunu dersin asıl sonucudur. Kaybı kapatan tek yol bilgiyi bir başlıkta taşımaktır ve başlık bağlantının kendisi gibi kanıtlanabilir değildir. Aktarım kapalıyken uygulama yanlış bilgiyle doğru davranır; açıkken doğru bilgiyle davranır ve bilginin doğruluğu artık zincirdeki her atlamanın disiplinine bağlıdır. Kaybolan dört kalem kapatıldı, yerine denetlenmesi gereken dört güven noktası geldi.
Özet
- Vekil zincirinden geçerken dört bilgi kaybolur: istemci adresi, protokol, istenen sunucu adı ve isteğin özgün yolu; aktarım kapalıyken uygulama dördünü de yanlış çözdü.
- Dört kaybın dördü de görünür bir karar üretti: üç üye kayıtta tek kimliğe düştü, karar
yonlendiroldu, üretilen bağlantı iç adrese işaret etti ve önek eksikliği yüzünden izlendiğinde 404 döndü. - İki başlığın aktarılması kaydı düzeltti ama bağı düzeltmedi: kayıtta üç kimlik göründü, bağ yine 404 aldı; kayda bakarak kalan iki kalem bulunamaz.
- Her iki vekilde de aktarım açıkken bile ikinci atlama gelen değeri koruyacak yerde üzerine yazdığında çözülen bilgi dörtten üçe, kayıttaki kimlik üçten bire indi.
- Aktarılan başlığa karar bağlandığında iddia edilen kimlik yüzeyi açıldı: temizleme kapalıyken sahte başlıklı dört isteğin dördü de iç uca girdi, temizleme açıkken sıfırı girdi.
Sonraki Adım
Bu dersin bütün ölçümleri şifrelemenin bir yerde bittiğini varsaydı: kenar vekil şifreyi çözüyor, arkası şifresiz taşıyor. Sonlandırmanın kenar vekilde olduğu hiç sorgulanmadı, oysa bu bir karardır ve üç yerde verilebilir.
Sonlandırma noktası değiştiğinde üç sayı birden değişir: zincirde kaç bacak şifresiz kalır, sertifika kaç ayrı yerde durur ve yenileme sırasında kaç noktaya dokunulur. Üçü aynı yöne gitmez; şifresiz bacağı sıfıra indiren seçenek sertifikanın bulunduğu yer sayısını ve yenileme maliyetini artırır. Sonraki ders üç sonlandırma seçeneğini bu üç sayıyla karşılaştırır ve kabul edilen protokol sürümleriyle şifre takımlarını bir kalem kümesi olarak modelleyip varsayılanın kaç eski seçeneği açık bıraktığını, kısıtlamanın kaç istemciyi düşürdüğünü ölçer.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.