Ders 18 / 21
Kullanıcılar ve İzinler
Köksüz çalışmanın üç ölçüsü: ayrıcalıklı kimliğe on beş girdinin kaçının açık kaldığı, köksüz kimliğe geçişin kaç girdide sahiplik düzeltmesi istediği ve düzeltmeden sonra bağlama takmasında kaç yolun ortamdan gelen sahiplikle kapalı kaldığı.
İçindekiler
Önceki ders ağ ayrımını kurdu: hangi adın hangi adrese çözüldüğü, hangi bağlantı noktasının dışarı açıldığı ve açılmayanın erişilemez kaldığı sayıldı. O ölçümlerin hepsi sürecin ne yaptığını sayıyordu. İçeride kimin çalıştığı hiç sorulmadı ve sorulmadığı için varsayılan yürürlükte kaldı: bir imaj derlenirken içine yazılan her dosyanın sahibi derlemeyi yürüten ayrıcalıklı kimliktir, süreç de aynı kimlikle başlar. Bölgesel ölçüm ağının okuma toplayıcı servisi — sayaç verisini toplayan, doğrulayan ve faturalamaya devreden kurgu yazılım — bu dersin ölçüm nesnesidir.
Uygulama düzeyindeki yetki soruları bu dersin dışındadır. Gizli değerin nerede durduğu, kaç yerde tekrarlandığı ve döndürülürken ne kırdığı Gizli Değer Yönetimi dersinde ölçülmüştü. Buradaki soru bir sınır aşağısındadır: o değerin dosyasına hangi kimliğin ulaşabildiği ve bu ulaşmanın imaj tanımından mı yoksa ortamdan mı geldiği.
CZ25 — servisin imajı on beş girdiden oluşur (dosya ve dizin) ve servis çalışırken bunların
üzerinde on iki işlem yapar: okuma, yazma, çalıştırma. CZ26 — derleme sırasında yazılan her girdi
ayrıcalıklı kimliğin (0:0) sahipliğine geçer. CZ27 — köksüz düzende süreç kimliği
10001:10001’dir. CZ28 — bağlanan konak dizini içeride 501:20 sahipliğiyle görünür; bu
sahiplik imajdan değil, takmanın geldiği yerden okunur. CZ29 — ayrıcalıklı kimlik isteğe bağlı
erişim denetimini atlar, kip bitlerine bakılmaz. CZ30 — kullanıcı ad alanı eşlemesi yoksa
konteyner içindeki sayısal kimlik konaktaki aynı sayıdır.
İzin Matrisi
Aşağıdaki koşum dosyaları gerçekten diske yazar, kip bitlerini chmod ile kurar ve statSync ile
geri okur; tablodaki kip sütunu diskten gelir. Sahiplik ise bir modeldir: sahiplik değiştirmek
ayrıcalık istediğinden sahip ve grup numaraları tabloda tutulur, erişim kararı bu numaralarla
gerçek kip bitleri üzerinden hesaplanır. Bir yola erişimin bütün ata dizinlerinde çalıştırma biti
istemesi de hesaba katılır.
// agac.mjs — kurgu okuma toplayici servisinin dosya agaci. // Dosyalar ve kip bitleri GERCEK: diske yazilir, chmod ile kurulur, statSync ile geri okunur. // Sahiplik bir MODELDIR: chown ayricalik ister, bu yuzden sahip/grup tabloda tutulur. import { mkdirSync, writeFileSync, chmodSync, statSync, rmSync } from 'node:fs'; export const AYRICALIKLI = { ad: 'ayricalikli', uid: 0, gid: 0 }; // CZ26 export const KOKSUZ = { ad: 'koksuz', uid: 10001, gid: 10001 }; // CZ27 export const KONAK = { uid: 501, gid: 20 }; // CZ28 // [yol, tur, kip, sahip, grup] — bagli/ altindakiler konak sahipligiyle gorunur. export const IMAJ = [ ['uygulama', 'd', 0o755, 0, 0], ['uygulama/baslat.mjs', 'f', 0o755, 0, 0], ['uygulama/toplayici.mjs', 'f', 0o644, 0, 0], ['uygulama/dogrulayici.mjs', 'f', 0o644, 0, 0], ['ayar', 'd', 0o755, 0, 0], ['ayar/servis.json', 'f', 0o644, 0, 0], ['ayar/imza-anahtari', 'f', 0o600, 0, 0], ['durum', 'd', 0o755, 0, 0], ['durum/kuyruk.veri', 'f', 0o644, 0, 0], ['durum/kilit', 'f', 0o644, 0, 0], ['gecici', 'd', 0o777, 0, 0], ['bagli', 'd', 0o755, KONAK.uid, KONAK.gid], ['bagli/olcum-01.csv', 'f', 0o644, KONAK.uid, KONAK.gid], ['bagli/olcum-02.csv', 'f', 0o640, KONAK.uid, KONAK.gid], ['bagli/cikti', 'd', 0o755, KONAK.uid, KONAK.gid], ]; export function kur(kok, dugumler) { rmSync(kok, { recursive: true, force: true }); mkdirSync(kok, { recursive: true }); for (const [yol, tur, kip] of dugumler) { const tam = `${kok}/${yol}`; if (tur === 'd') mkdirSync(tam); else writeFileSync(tam, `# ${yol}\n`); chmodSync(tam, kip); } return dugumler.map(([yol, tur, , uid, gid]) => ({ yol, tur, uid, gid, kip: statSync(`${kok}/${yol}`).mode & 0o777, })); } // Istege bagli erisim denetimi: sahip / grup / diger uclusunden biri secilir. // CZ29: ayricalikli kimlik ucluyu atlar; calistirma icin yine de bir x biti arar. export function izin(dugum, kimlik) { const { kip } = dugum; if (kimlik.uid === 0) return { r: true, w: true, x: (kip & 0o111) !== 0 }; const u = kimlik.uid === dugum.uid ? kip >> 6 : kimlik.gid === dugum.gid ? kip >> 3 : kip; return { r: (u & 4) !== 0, w: (u & 2) !== 0, x: (u & 1) !== 0 }; } // Bir yola erisim, butun ata dizinlerinde x bitini de ister. export function erisim(agac, yol, kimlik) { const p = yol.split('/'); for (let i = 1; i < p.length; i += 1) { const ata = agac.find((d) => d.yol === p.slice(0, i).join('/')); if (ata && !izin(ata, kimlik).x) return { r: false, w: false, x: false }; } return izin(agac.find((d) => d.yol === yol), kimlik); } export const bicim = (e) => `${e.r ? 'r' : '-'}${e.w ? 'w' : '-'}${e.x ? 'x' : '-'}`; if (import.meta.url === `file://${process.argv[1]}`) { const agac = kur('./kok', IMAJ); const s = (x, n) => String(x).padStart(n); console.log(`${'yol'.padEnd(24)}${s('kip', 5)}${s('sahip', 8)}${s('ayricalikli', 13)}${s('koksuz', 9)}`); for (const d of agac) { console.log(`${(d.tur === 'd' ? `${d.yol}/` : d.yol).padEnd(24)}${s(d.kip.toString(8), 5)}` + `${s(`${d.uid}:${d.gid}`, 8)}${s(bicim(erisim(agac, d.yol, AYRICALIKLI)), 13)}` + `${s(bicim(erisim(agac, d.yol, KOKSUZ)), 9)}`); } for (const k of [AYRICALIKLI, KOKSUZ]) { const e = agac.map((d) => erisim(agac, d.yol, k)); console.log(`${k.ad.padEnd(13)} okunabilir ${s(e.filter((x) => x.r).length, 2)}/${agac.length}` + ` yazilabilir ${s(e.filter((x) => x.w).length, 2)}/${agac.length}` + ` calistirilabilir ${s(e.filter((x) => x.x).length, 2)}/${agac.length}`); } }
yol kip sahip ayricalikli koksuz uygulama/ 755 0:0 rwx r-x uygulama/baslat.mjs 755 0:0 rwx r-x uygulama/toplayici.mjs 644 0:0 rw- r-- uygulama/dogrulayici.mjs 644 0:0 rw- r-- ayar/ 755 0:0 rwx r-x ayar/servis.json 644 0:0 rw- r-- ayar/imza-anahtari 600 0:0 rw- --- durum/ 755 0:0 rwx r-x durum/kuyruk.veri 644 0:0 rw- r-- durum/kilit 644 0:0 rw- r-- gecici/ 777 0:0 rwx rwx bagli/ 755 501:20 rwx r-x bagli/olcum-01.csv 644 501:20 rw- r-- bagli/olcum-02.csv 640 501:20 rw- --- bagli/cikti/ 755 501:20 rwx r-x ayricalikli okunabilir 15/15 yazilabilir 15/15 calistirilabilir 7/15 koksuz okunabilir 13/15 yazilabilir 1/15 calistirilabilir 7/15
İki sütun arasındaki fark, kip bitlerinin ne kadarının karar taşıdığını gösteriyor. Ayrıcalıklı
kimlik için on beş girdinin on beşi okunabilir ve on beşi yazılabilir; 600 kipiyle korunan imza
anahtarı da, konak sahipliğiyle gelen ölçüm dosyası da açıktır. O sütunda kip bitleri karar
taşımaz, yalnızca kayıt tutar. Köksüz sütununda ise aynı bitler kararı verir: okunabilir on üç,
yazılabilir bir. Yazılabilir tek girdi gecici/ dizinidir ve nedeni kipinin 777 olmasıdır.
Çalıştırma sütununun iki kimlikte de yedi çıkması ayrı bir ayrımdır. Ayrıcalıklı kimlik izin denetimini atlar ama olmayan bir çalıştırma bitini uyduramaz; dosyanın çalıştırılabilir olması yetkiden değil, kipten gelir. Bu dersin bütün sayıları içinde ayrıcalığın değiştirmediği tek sütun budur.
Köksüz Çalışmaya Geçişin Bedeli
Matris tek başına bir karar vermez; hangi erişimin gerektiği bilinmeden “kapalı” bir sayı değildir. İkinci koşum servisin gerçekten yaptığı on iki işlemi listeler, iki kimlikle koşturur ve düşen işlemleri ayırır: imaj içinde düzeltilebilenler ile takmadan gelenler.
// matris.mjs — koksuz calismaya gecisin bedeli ve konak tarafinda birakilan yuzey. import { kur, izin, erisim, bicim, IMAJ, AYRICALIKLI, KOKSUZ } from './agac.mjs'; // Servisin gercekten yaptigi islemler: [yol, islem] const GEREKLI = [ ['uygulama/baslat.mjs', 'x'], ['uygulama/toplayici.mjs', 'r'], ['uygulama/dogrulayici.mjs', 'r'], ['ayar/servis.json', 'r'], ['ayar/imza-anahtari', 'r'], ['durum', 'w'], ['durum/kuyruk.veri', 'w'], ['durum/kilit', 'w'], ['gecici', 'w'], ['bagli/olcum-01.csv', 'r'], ['bagli/olcum-02.csv', 'r'], ['bagli/cikti', 'w'], ]; const dusen = (agac, kimlik) => GEREKLI.filter(([y, i]) => !erisim(agac, y, kimlik)[i]); const yuzey = (agac, kimlik) => agac.filter((d) => izin(d, kimlik).w).length; const agac = kur('./kok', IMAJ); const s = (x, n) => String(x).padStart(n); console.log(`ayricalikli: dusen islem ${dusen(agac, AYRICALIKLI).length}/${GEREKLI.length}` + `, yazilabilir girdi ${yuzey(agac, AYRICALIKLI)}/${agac.length}`); console.log(`koksuz : dusen islem ${dusen(agac, KOKSUZ).length}/${GEREKLI.length}` + `, yazilabilir girdi ${yuzey(agac, KOKSUZ)}/${agac.length}`); for (const [y, i] of dusen(agac, KOKSUZ)) { const d = agac.find((x) => x.yol === y); console.log(` ${y.padEnd(22)} istenen ${i} kip ${d.kip.toString(8)} sahip ${d.uid}:${d.gid}` + ` ${d.yol.startsWith('bagli/') ? 'baglama takmasi — imajda duzeltilemez' : 'imaj icinde'}`); } // Iki duzeltme bicimi: sahipligi tasimak, ya da kip bitlerini gevsetmek. // YABANCI: ayni bitlerden yararlanan ucuncu bir kimlik (baska bir surec ya da esleme yokken konak). const YABANCI = { uid: 20002, gid: 20002 }; function duzelt(bicimAdi) { const t = kur('./kok', IMAJ); let dokunulan = 0; for (const [y] of dusen(t, KOKSUZ)) { const d = t.find((x) => x.yol === y); if (y.startsWith('bagli/')) continue; if (bicimAdi === 'sahiplik') { d.uid = KOKSUZ.uid; d.gid = KOKSUZ.gid; } else d.kip |= d.tur === 'd' ? 0o077 : 0o066; dokunulan += 1; } return { t, dokunulan, kalan: dusen(t, KOKSUZ) }; } console.log(`\n${'duzeltme'.padEnd(16)}${s('dokunulan', 11)}${s('kalan dusen', 13)}` + `${s('koksuz yazar', 14)}${s('yabanci yazar', 15)}${s('yabanci okur', 14)}`); let kalan = []; for (const b of ['sahiplik', 'kip gevsetme']) { const r = duzelt(b); kalan = r.kalan; console.log(`${b.padEnd(16)}${s(r.dokunulan, 11)}${s(r.kalan.length, 13)}` + `${s(`${yuzey(r.t, KOKSUZ)}/${r.t.length}`, 14)}${s(`${yuzey(r.t, YABANCI)}/${r.t.length}`, 15)}` + `${s(`${r.t.filter((d) => izin(d, YABANCI).r).length}/${r.t.length}`, 14)}`); } // Konak yuzeyi: ayni sayisal kimligin konteyner disinda dokunabildigi girdiler (CZ30). const KONAK_AGACI = [ ['veri', 'd', 0o755, 0, 0], ['veri/olcum-arsiv', 'f', 0o644, 10001, 10001], ['veri/gunluk.log', 'f', 0o664, 0, 10001], ['ayar', 'd', 0o755, 0, 0], ['ayar/ag.json', 'f', 0o644, 0, 0], ['ev', 'd', 0o755, 10001, 10001], ['ev/anahtar', 'f', 0o600, 10001, 10001], ['ev/toplayici.mjs', 'f', 0o755, 10001, 10001], ]; const konak = kur('./konak', KONAK_AGACI); const ESLEME = 100000; // kullanici ad alani kaydirmasi console.log(`\n${'konak kimligi'.padEnd(16)}${s('okunabilir', 12)}${s('yazilabilir', 13)}${s('ev/anahtar', 12)}`); for (const k of [{ ad: 'eslemesiz 10001', uid: KOKSUZ.uid, gid: KOKSUZ.gid }, { ad: `esleme +${ESLEME}`, uid: KOKSUZ.uid + ESLEME, gid: KOKSUZ.gid + ESLEME }]) { const e = konak.map((d) => erisim(konak, d.yol, k)); console.log(`${k.ad.padEnd(16)}${s(`${e.filter((x) => x.r).length}/${konak.length}`, 12)}` + `${s(`${e.filter((x) => x.w).length}/${konak.length}`, 13)}` + `${s(bicim(erisim(konak, 'ev/anahtar', k)), 12)}`); } console.log(`baglama takmasindaki ${kalan.length} yol konak tarafinda duzeltilmeli;` + ` esleme acikken hedef kimlik ${KOKSUZ.uid} degil ${KOKSUZ.uid + ESLEME} olur`);
ayricalikli: dusen islem 0/12, yazilabilir girdi 15/15 koksuz : dusen islem 6/12, yazilabilir girdi 1/15 ayar/imza-anahtari istenen r kip 600 sahip 0:0 imaj icinde durum istenen w kip 755 sahip 0:0 imaj icinde durum/kuyruk.veri istenen w kip 644 sahip 0:0 imaj icinde durum/kilit istenen w kip 644 sahip 0:0 imaj icinde bagli/olcum-02.csv istenen r kip 640 sahip 501:20 baglama takmasi — imajda duzeltilemez bagli/cikti istenen w kip 755 sahip 501:20 baglama takmasi — imajda duzeltilemez duzeltme dokunulan kalan dusen koksuz yazar yabanci yazar yabanci okur sahiplik 4 2 5/15 1/15 13/15 kip gevsetme 4 2 5/15 5/15 14/15 konak kimligi okunabilir yazilabilir ev/anahtar eslemesiz 10001 8/8 5/8 rw- esleme +100000 7/8 0/8 --- baglama takmasindaki 2 yol konak tarafinda duzeltilmeli; esleme acikken hedef kimlik 10001 degil 110001 olur
Ayrıcalıklı kimlikle on iki işlemin sıfırı düşer. Bu, ayrıcalıklı çalışmanın neden sürdüğünü tek satırda açıklıyor: hiçbir şey kırılmaz, dolayısıyla hiçbir şey sorulmaz. Köksüz kimliğe geçildiğinde on iki işlemin altısı düşer ve düşenlerin dağılımı asıl bilgidir — dördü imaj içindedir, ikisi takmadan gelir.
İmaj içindeki dört düşük tek bir düzeltmeyle kapanır: o dört girdinin sahipliğinin köksüz kimliğe geçirilmesi. Yalıtımın bu dersteki bedeli bu sayıdır — dört girdide sahiplik düzeltmesi, yani imaj tanımına yazılacak dört yol. Bu yolların kod okunarak bulunamaması bedeli asıl ağırlaştıran şeydir: hangi dosyanın yazıldığı ancak servis köksüz koşup düştüğünde ortaya çıkar, ve düşen her işlem ayrı bir kırılmadır. Düzeltmeden sonra yazılabilir girdi sayısı ondan beşe değil, on beşten beşe iner: ayrıcalıklı kimliğin yazabildiği on girdi kapanır. Kapanan on girdinin içinde imza anahtarının yanındaki üç yapılandırma dosyası ve uygulama kodunun kendisi vardır; ele geçirilmiş bir sürecin kendi kodunu değiştirip kalıcı olması bu on girdinin kapanmasıyla ortadan kalkar.
Aynı dört girdi iki ayrı yolla açılabilir ve tablo ikisini yan yana koyuyor. Sahipliği taşımak da
kip bitlerini gevşetmek de dört girdiye dokunur, ikisi de kalan düşük sayısını ikiye indirir ve
ikisinde de köksüz kimliğin yazabildiği girdi beştir. Servis her iki düzeltmeyle de aynı biçimde
ayağa kalkar. Fark tablonun son iki sütunundadır: sahiplik taşındığında üçüncü bir kimlik on beş
girdinin yalnız birine yazabilir, kip gevşetildiğinde beşine. Okuma sütunu farkı daha keskin
söylüyor — gevşetme imza anahtarının 600 kipini 666 yapar ve yabancı kimliğin okuyabildiği
girdi on üçten on dörde çıkar. İki düzeltme aynı hatayı susturur, imaj tanımına aynı sayıda satır
yazdırır ve farklı bir yüzey bırakır; hangisinin seçildiği ne hata iletisinden ne de servisin
davranışından okunur.
Kalan iki işlem imaj tanımına yazılarak kapanmaz. bagli/olcum-02.csv ve bagli/cikti takmadan
gelir; sahiplikleri imajın değil, takmanın geldiği yerin kaydındadır. Yalıtımın delindiği birinci
yer budur: on beş girdinin on birinin sahipliği çıktının içine alınabilir, dördününki çalışma
anında dışarıdan gelir ve o dördü için doğru sayı ancak iki tarafın anlaşmasıyla belirlenir.
Sayısal Kimliğin Paylaşılması
Çıktının alt yarısı ikinci deliği ölçüyor. Köksüz kimlik 10001 bir addan değil bir sayıdan
ibarettir ve o sayı çekirdeğin tek bir tablosundan okunur. Konak tarafında sekiz girdilik kurgu bir
kümede aynı sayı beş girdiyi yazabilir; ev/anahtar dosyası 600 kipindedir, yani içeriden
erişilemeyen imza anahtarının konak eşdeğeri o kimliğe rw- açıktır. Köksüz çalışmak “yetkisiz
çalışmak” değildir; başka bir sayının yetkisiyle çalışmaktır ve o sayının konakta ne anlama
geldiği imajda yazmaz.
Kullanıcı ad alanı eşlemesi bu deliği kapatır: yüz bin kaydırmayla içerideki 10001 konakta
110001 olur, hiçbir konak girdisinin sahibi ya da grubu değildir, yazılabilir sayısı beşten
sıfıra iner ve ev/anahtar okunamaz hâle gelir. Bedeli son satırdadır. Takmadaki iki yol için
konak tarafında yapılacak sahiplik düzeltmesinin hedefi artık 10001 değil 110001’dir; içeriden
yazılan her yeni dosya da konakta o kimlikle görünür. Eşleme, dışarı taşan yetkiyi kapatırken
takmanın iki ucu arasındaki kimlik uyuşmazlığını büyütür. Yalıtım burada da tam olmaz, yalnızca
bedeli başka bir kaleme yazılır.
Özet
- İzin matrisi ayrıcalıklı kimlik için karar taşımaz: on beş girdinin on beşi okunabilir, on beşi
yazılabilir;
600kipli imza anahtarı da açıktır. Kipin değiştirmediği tek sütun çalıştırmadır — iki kimlikte de yedi. - Köksüz kimlikle on iki işlemin altısı düşer; dördü imaj içinde, ikisi bağlama takmasındadır.
- Geçişin bedeli dört girdide sahiplik düzeltmesidir ve bu dört yol koddan okunarak değil, servis düşerek bulunur; karşılığında yazılabilir girdi sayısı on beşten beşe iner. Aynı dördü kip gevşeterek açmak da servisi yürütür, ama yabancı bir kimliğe yazılabilir girdiyi birden beşe, okunabiliri on üçten on dörde çıkarır.
- On beş girdinin on birinin sahipliği çıktının içine alınabilir; dördü çalışma anında ortamdan gelir ve imaj tanımıyla düzeltilemez.
- Köksüz kimlik konakla aynı sayıdır: kurgu konak kümesinde sekiz girdinin beşini yazabilir ve
600kipli konak anahtarını okuyabilir. - Kullanıcı ad alanı eşlemesi konakta yazılabilir sayısını beşten sıfıra indirir; bedeli takmanın
iki ucundaki kimlik uyuşmazlığının derinleşmesi, düzeltme hedefinin
110001’e kaymasıdır.
Sonraki Adım
Buraya kadarki bütün ölçümler tek bir konteyner üzerindeydi: bir dosya ağacı, bir kimlik, bir izin matrisi. Oysa bölgesel ölçüm ağı tek süreç değildir — okuma toplayıcının yazdığı kuyruğu bir doğrulayıcı okur, doğrulanan kayıt faturalamaya gider ve hepsinin altında bir kayıt deposu durur. Bu servisleri elle başlatmak, her birinin ağını, birimini ve sırasını ayrı kararlarla kurmak demektir; bir tanesi yeniden başladığında hangilerinin etkilendiği de o kararların içinde saklıdır. Sonraki ders bu kurulumu bildirimsel bir bileşim dosyasına taşır ve iki biçimi sayıyla karşılaştırır: dört servisli bir kurguda başlatma sırasının bağımlılık grafiğinden nasıl türetildiğini, hazır olma denetimi olmadan kaç yarışın doğduğunu ve elle başlatmaya göre kaç komut ile kaç kararın ortadan kalktığını ölçer.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.