---
title: 'Sır Yönetimi'
source: 'https://academia.sh/tr/kurslar/ci-cd/sir-yonetimi'
course: 'Sürekli Tümleştirme ve Teslimat'
language: tr
updated: '2026-08-17T18:09:03+00:00'
license: 'CC BY-SA 4.0'
---

# Sır Yönetimi

Sırlar depoda şifreli tutulduğunda anahtarın nerede durduğu, kaç öznenin kaç kaydı çözebildiği ve anahtar döndürüldüğünde kaç kaydın yeniden şifrelenip kaç dosyaya dokunulduğu gerçek şifrelemeyle sayılır. Şifreli kaydın geçmişte kalması ölçülür: eski anahtar sızarsa geçmişteki 35 sürüm çözülüyor ve 11'i bugünkü değerdir.

Önceki iki ders istenen durumun depoda durmasını ve oradan kümeye taşınmasını ölçtü. İkisinde de
sessizce dışarıda bırakılan bir kalem vardı: servislerin ihtiyaç duyduğu **sırlar**. Toplayıcının
ölçüm ağı uçlarına bağlanmak için taşıdığı kimlik bilgisi, faturalamanın veritabanı parolası, iş
emri servisinin arşiv anahtarı — bunlar istenen durumun parçasıdır, ama depoya düz metin
konamazlar. Depoya konmazlarsa istenen durum eksiktir ve uzlaştırıcı ajan uygulayamaz.

Çözüm biçimi bellidir: sır depoda **şifreli** durur, çözme yetkisi ayrı bir eksende yönetilir. Bu
dersin ölçtüğü şey o eksendir — anahtar nerede duruyor, kaç özne kaç kaydı çözebiliyor, anahtar
döndürüldüğünde ne oluyor ve depoda kalan şifreli kaydın **geçmişi** ne anlama geliyor. Sır
sızıntısının hangi yollardan gerçekleştiği daha önce taranmıştı — kayıt satırları, ortam
değişkenleri, yapı çıktısı katmanları — ve o tarama burada tekrarlanmaz.

Ölçüm **bölgesel ölçüm ağı** üzerindedir ve **kurgudur**. Şifreleme `node:crypto` ile gerçekten
koşar; **sır değerlerinin tamamı uydurmadır** ve `kurgu-` önekiyle yazılır.

**DD13.** Envanter 33 sır kaydıdır: dört servis, üç ortam, servis başına iki-üç sır türü.
**DD14.** Şifreleme AES-256-GCM'dir, anahtar `scrypt` ile türetilir. Başlangıç vektörü kayıt
kimliğinden türetilir ki çıktı yeniden üretilebilsin; gerçek bir dağıtımda rastgele üretilir.
**DD15.** Üç anahtar yerleşimi karşılaştırılır: tek anahtar, ortam başına anahtar, ortam-servis
başına anahtar.
**DD16.** Depoyu okuyabilen 29 özne vardır; 14'ü hiçbir anahtar taşımaz. Her öznenin ihtiyaç
duyduğu (ortam, servis) çiftleri yazılıdır.
**DD17.** 180 günlük geçmişte her kayıt bir ile dört kez değişmiştir; her değişiklik ayrı bir
işlemede durur.
**DD18.** Üretim nöbeti iki servisin sırlarına ihtiyaç duyar; 180 günde 24 gece olayı olur ve
anahtarsız kalınan her olay 35 dakika eskalasyon ekler.

## Anahtar Nerede Duruyor, Kim Çözebiliyor

```js
// sir.mjs — depoda sifreli sir kaydi: envanter, anahtar yerlesimi, cozme yetkisi.
// SIR DEGERLERI KURGUDUR, gercek bir sir yazilmaz. Sifreleme node:crypto ile gercekten kosar.
// IV kayit kimligi ve anahtar surumunden turetilir ki cikti yeniden uretilebilsin.
import { scryptSync, createCipheriv, createDecipheriv, createHash } from "node:crypto";

export const ORTAM = ["gelistirme", "hazirlik", "uretim"];
export const SERVIS = { toplayici: ["veritabani", "olcum-agi-jetonu", "onbellek"],
  dogrulayici: ["veritabani", "imzalama"], faturalama: ["veritabani", "odeme-jetonu", "imzalama"],
  isemri: ["veritabani", "bildirim-jetonu", "arsiv"] };

export const KAYIT = ORTAM.flatMap((o) => Object.entries(SERVIS).flatMap(([s, tl]) =>
  tl.map((t) => ({ id: `${o}/${s}/${t}`, ortam: o, servis: s,
    deger: `kurgu-${t}-${s}-${o}-0001` }))));             // kurgu deger; gercek sir degil

export const sayac = { turetme: 0, sifreleme: 0, cozme: 0 };
const kasa = new Map();
export const anahtar = (ad, surum = 1) => {               // parola da kurgudur
  const k = `${ad}#${surum}`;
  if (!kasa.has(k)) { sayac.turetme++;
    kasa.set(k, scryptSync(`kurgu-parola-${k}`, `tuz-${ad}`, 32, { N: 16384 })); }
  return kasa.get(k);
};

export const YERLESIM = { tek: () => "hepsi", ortam: (k) => k.ortam,
  "ortam-servis": (k) => `${k.ortam}/${k.servis}` };

export const sifrele = (kayit, ad, surum, deger = kayit.deger) => {
  const iv = createHash("sha256").update(`${kayit.id}#${ad}#${surum}#${deger}`).digest().subarray(0, 12);
  const c = createCipheriv("aes-256-gcm", anahtar(ad, surum), iv);
  const g = Buffer.concat([c.update(deger, "utf8"), c.final()]);
  sayac.sifreleme++;
  return { iv, govde: g, etiket: c.getAuthTag(), ad, surum };
};

export const coz = (k) => {
  const d = createDecipheriv("aes-256-gcm", anahtar(k.ad, k.surum), k.iv);
  d.setAuthTag(k.etiket); sayac.cozme++;
  return Buffer.concat([d.update(k.govde), d.final()]).toString("utf8");
};

// ozneler: her biri belirli (ortam, servis) ciftlerine ihtiyac duyar. depo okuru anahtarsizdir.
export const OZNE = [
  { ad: "ajan/gelistirme", n: 1, ihtiyac: [["gelistirme", "*"]] },
  { ad: "ajan/hazirlik", n: 1, ihtiyac: [["hazirlik", "*"]] },
  { ad: "ajan/uretim", n: 1, ihtiyac: [["uretim", "*"]] },
  { ad: "olcum ekibi", n: 6, ihtiyac: [["gelistirme", "toplayici"], ["gelistirme", "dogrulayici"],
    ["hazirlik", "toplayici"]] },
  { ad: "fatura ekibi", n: 4, ihtiyac: [["gelistirme", "faturalama"], ["hazirlik", "faturalama"]] },
  { ad: "uretim nobeti", n: 2, ihtiyac: [["uretim", "faturalama"], ["uretim", "toplayici"]] },
  { ad: "depo okuru", n: 14, ihtiyac: [] },
];
const uyar = (k, [o, s]) => k.ortam === o && (s === "*" || k.servis === s);
export const gerekli = (z) => KAYIT.filter((k) => z.ihtiyac.some((c) => uyar(k, c)));

export function yetki(yer) {                              // yerlesime gore cozulebilen kayitlar
  const f = YERLESIM[yer];
  return OZNE.map((z) => {
    const tasidigi = new Set(gerekli(z).map(f));          // ozneye verilen anahtar adlari
    return { z, cozer: KAYIT.filter((k) => tasidigi.has(f(k))), gerek: gerekli(z).length };
  });
}

if (import.meta.url === `file://${process.argv[1]}`) {
  const ozneSayisi = OZNE.reduce((a, z) => a + z.n, 0);
  console.log(`envanter: ${KAYIT.length} sir kaydi (kurgu deger), ${ozneSayisi} depo okuru ozne`);
  const ornek = sifrele(KAYIT[0], "gelistirme", 1);
  console.log(`ornek kayit ${KAYIT[0].id} -> ${ornek.govde.toString("base64")} ` +
    `| cozulen: ${coz(ornek)}`);
  const B = [14, 9, 13, 15, 12, 12, 15];
  const yz = (h) => h.map((v, j) => j ? String(v).padStart(B[j]) : String(v).padEnd(B[0])).join("");
  console.log("\n" + yz(["yerlesim", "anahtar", "cozme cifti", "ihtiyac cifti", "fazlalik",
    "engellenen", "en genis ozne"]));
  for (const yer of Object.keys(YERLESIM)) {
    const y = yetki(yer);
    const anahtarlar = new Set(KAYIT.map(YERLESIM[yer])).size;
    const cift = y.reduce((a, x) => a + x.z.n * x.cozer.length, 0);
    const gerek = y.reduce((a, x) => a + x.z.n * x.gerek, 0);
    for (const x of y) if (x.cozer.length) coz(sifrele(x.cozer[0], YERLESIM[yer](x.cozer[0]), 1));
    console.log(yz([yer, anahtarlar, cift, gerek, cift - gerek,
      ozneSayisi * KAYIT.length - cift, Math.max(...y.map((x) => x.cozer.length))]));
  }
  console.log(`\nislem sayaci: ${sayac.turetme} anahtar turetme, ` +
    `${sayac.sifreleme} sifreleme, ${sayac.cozme} cozme`);
}
```

```
envanter: 33 sir kaydi (kurgu deger), 29 depo okuru ozne
ornek kayit gelistirme/toplayici/veritabani -> qvyyakRtf8xnLCnzTl5E7FgDkdh/5gAmv4t9dwQTT9kMOGXo4eVrlZUW | cozulen: kurgu-veritabani-toplayici-gelistirme-0001

yerlesim        anahtar  cozme cifti  ihtiyac cifti    fazlalik  engellenen  en genis ozne
tek                   1          495            117         378         462             33
ortam                 3          275            117         158         682             22
ortam-servis         12          117            117           0         840             11

islem sayaci: 8 anahtar turetme, 19 sifreleme, 19 cozme
```

Şifreleme gerçekten koştu: örnek kayıt şifrelendi, geri çözüldü ve kurgu değer aynen döndü. Asıl
tablo aşağıdadır ve tek bir soruyu üç kez yanıtlar — **kaç özne kaç kaydı çözebiliyor.** Ölçü
birimi (özne, kayıt) çiftidir; 29 özne ve 33 kayıtla olası en büyük değer 957'dir.

Anahtar deponun içinde durmaz — durursa şifrelemenin anlamı kalmaz. Anahtar öznelerin elindedir ve
tablo o dağıtımın sonucunu sayar. Tek anahtarlı yerleşimde anahtarı taşıyan 15 özne 33 kaydın
hepsini çözebilir: 495 çift. Bu öznelerin gerçekten ihtiyaç duyduğu çift sayısı **117**'dir ve bu
sayı üç yerleşimde de aynıdır, çünkü ihtiyaç anahtar yerleşiminden bağımsızdır. Aradaki 378 çift
saf fazlalıktır — kimsenin istemediği, yalnız anahtar kaba olduğu için verilmiş yetki.

Ortam başına anahtara geçildiğinde fazlalık 378'den 158'e iner; ortam-servis başına anahtarda
**sıfırlanır** ve çözebilen özne tam olarak ihtiyacı kadarını çözer. En geniş öznenin yetkisi de
33'ten 11'e düşer: bir kişinin elindeki anahtarın açtığı kayıt sayısı üç kat azalır. Bunun bedeli
anahtar sayısıdır — 1'den 12'ye — ve bu sayı bir sonraki bölümün konusudur.

## Anahtar Döndürmenin Maliyeti ve Geçmişte Kalan Kayıt

```js
// dondur.mjs — anahtar dondurmenin maliyeti ve sifreli kaydin gecmiste kalmasi.
// Gecmisteki sifreli degerler gercekten cozulur; degerler kurgudur.
import { createHash } from "node:crypto";
import { KAYIT, YERLESIM, OZNE, yetki, sifrele, coz, sayac } from "./sir.mjs";

const DOSYA = (k) => `${k.ortam}/${k.servis}/sirlar`;      // kayitlar dosyalara boyle dagilir
const kez = (k) => 1 + (createHash("sha256").update(k.id).digest()[0] % 4);
const deger = (k, i) => `kurgu-${k.id.split("/")[2]}-${k.servis}-${k.ortam}-` +
  String(i).padStart(4, "0");

// 180 gunluk gecmis: her kayit kez(k) kez degisti; her degisiklik ayri bir islemede duruyor.
const gecmis = KAYIT.flatMap((k) => Array.from({ length: kez(k) }, (_, i) =>
  ({ kayit: k, ac: deger(k, i + 1) })));
const guncel = new Map(KAYIT.map((k) => [k.id, deger(k, kez(k))]));

const Y = [16, 10, 14, 10, 16];
const yz = (h) => h.map((v, j) => j ? String(v).padStart(Y[j]) : String(v).padEnd(Y[0])).join("");
console.log(`gecmis: ${gecmis.length} sifreli kayit surumu / ${KAYIT.length} kayit, ` +
  `${new Set(KAYIT.map(DOSYA)).size} dosya`);
console.log("\n" + yz(["yerlesim", "anahtar", "tek dondurme", "dosya", "hepsi dondurme"]));
for (const [yer, f] of Object.entries(YERLESIM)) {
  const hedef = f(KAYIT.find((k) => k.ortam === "uretim" && k.servis === "faturalama"));
  const etkilenen = KAYIT.filter((k) => f(k) === hedef);
  console.log(yz([yer, new Set(KAYIT.map(f)).size, etkilenen.length,
    new Set(etkilenen.map(DOSYA)).size, `${KAYIT.length} / ${new Set(KAYIT.map(DOSYA)).size} dosya`]));
}

// ortam yerlesiminde uretim anahtari 1 -> 2 dondurulur. Guncel kayitlar yeniden sifrelenir.
const f = YERLESIM.ortam;
const uretim = KAYIT.filter((k) => k.ortam === "uretim");
const gecmisSifreli = gecmis.map((g) => ({ ...g, kutu: sifrele(g.kayit, f(g.kayit), 1, g.ac) }));
const yeni = uretim.map((k) => sifrele(k, f(k), 2, guncel.get(k.id)));
console.log(`\nuretim anahtari 1 -> 2: ${yeni.length} kayit yeniden sifrelendi, ` +
  `${new Set(uretim.map(DOSYA)).size} dosyaya dokunuldu, ${new Set(uretim.map(DOSYA)).size} yeni isleme`);

// eski anahtar sizarsa gecmis ne veriyor
const eski = gecmisSifreli.filter((g) => g.kutu.ad === "uretim" && g.kutu.surum === 1);
const acilan = eski.map((g) => coz(g.kutu));
const dogru = acilan.every((a, i) => a === eski[i].ac);
const hala = eski.filter((g, i) => acilan[i] === guncel.get(g.kayit.id)).length;
const idx = eski.findIndex((g, i) => acilan[i] === guncel.get(g.kayit.id));
console.log(`eski anahtar (surum 1) sizarsa: gecmisteki ${eski.length} surum cozuluyor ` +
  `(hepsi dogru: ${dogru}); bunlarin ${hala} tanesi ${uretim.length} kaydin BUGUNKU degeri`);
console.log(`ornek: ${eski[idx].kayit.id} eski islemeden -> ${acilan[idx]}`);

// cozme yetkisi kapisinin yanlis tuttugu: gece nobetinde anahtarsiz kalinan olaylar
const uretec = (t) => { let s = t >>> 0;
  return () => (s = (s * 1664525 + 1013904223) >>> 0) / 4294967296; };
const r = uretec(90210), SRV = ["toplayici", "dogrulayici", "faturalama", "isemri"];
const olay = Array.from({ length: 24 }, () => SRV[Math.floor(r() * 4)]);
const NOBET = ["faturalama", "toplayici"];                 // uretim nobetinin ihtiyac duydugu
console.log("\n" + yz(["yerlesim", "gece olayi", "anahtarsiz", "ek dakika", "engellenen cift"]));
for (const yer of Object.keys(YERLESIM)) {
  const tasir = (s) => yer === "ortam-servis" ? NOBET.includes(s) : true;
  const yok = olay.filter((s) => !tasir(s)).length;
  const cift = yetki(yer).reduce((a, x) => a + x.z.n * x.cozer.length, 0);
  console.log(yz([yer, olay.length, yok, yok * 35,
    OZNE.reduce((a, z) => a + z.n, 0) * KAYIT.length - cift]));
}
console.log(`\nislem sayaci: ${sayac.turetme} anahtar turetme, ${sayac.sifreleme} sifreleme, ` +
  `${sayac.cozme} cozme`);
```

```
gecmis: 93 sifreli kayit surumu / 33 kayit, 12 dosya

yerlesim           anahtar  tek dondurme     dosya  hepsi dondurme
tek                      1            33        12   33 / 12 dosya
ortam                    3            11         4   33 / 12 dosya
ortam-servis            12             3         1   33 / 12 dosya

uretim anahtari 1 -> 2: 11 kayit yeniden sifrelendi, 4 dosyaya dokunuldu, 4 yeni isleme
eski anahtar (surum 1) sizarsa: gecmisteki 35 surum cozuluyor (hepsi dogru: true); bunlarin 11 tanesi 11 kaydin BUGUNKU degeri
ornek: uretim/toplayici/veritabani eski islemeden -> kurgu-veritabani-toplayici-uretim-0004

yerlesim        gece olayi    anahtarsiz ek dakika engellenen cift
tek                     24             0         0             462
ortam                   24             0         0             682
ortam-servis            24            11       385             840

islem sayaci: 4 anahtar turetme, 104 sifreleme, 35 cozme
```

Döndürme tablosu anahtar sayısının ikinci yüzünü verir. Tek anahtarlı yerleşimde bir anahtarı
döndürmek 33 kaydın hepsini yeniden şifreler ve 12 dosyaya dokunur; ortam-servis yerleşiminde aynı
işlem 3 kayıt ve 1 dosyadır. Ama son sütun takası dürüst gösteriyor: **bütün** anahtarları döndürmek
üç yerleşimde de 33 kayıt ve 12 dosyadır. İnce anahtar bölmesi toplam işi azaltmaz, tek bir
sızıntının yankısını daraltır.

Üretim anahtarı 1'den 2'ye döndürüldüğünde 11 kayıt yeniden şifrelendi, 4 dosyaya dokunuldu ve 4
yeni işleme üretildi. Buraya kadar her şey beklenendir. Dersin çekirdeği bir sonraki satırdadır ve
tahmin değil, **çalıştırılmış bir çözmedir**: eski anahtar sızarsa geçmişteki 35 şifreli sürümün
hepsi çözülüyor — `hepsi dogru: true` satırı bunun sınandığını gösterir — ve bu 35 sürümün **11
tanesi 11 üretim kaydının bugünkü değeridir.**

Sonuç şudur: anahtar döndürmek geçmişi kapatmaz. Yeni işlemede yeni şifreli değer durur, ama eski
işlemede eski şifreli değer olduğu gibi kalır ve o değer eski anahtarla açıldığında **bugün de
geçerli olan sırrı** verir. Örnek satır bunu somutlaştırıyor: toplayıcının üretim veritabanı
kaydının eski işlemeden çözülen değeri, bugün çalışan servisin kullandığı değerin aynısıdır.
Geçmişi gerçekten kapatan tek işlem, sırrın **değerini** kaynağında değiştirmektir — parolayı
veritabanında döndürmek, jetonu ölçüm ağı tarafında iptal etmek. Anahtar döndürme 4 dosyaya
dokunur; değer döndürme her tüketiciye dokunur ve depo tarafındaki 4 dosya bu işin en ucuz
parçasıdır.

## Çözme Yetkisi Kapısının Defteri

Bu konudaki kapı bir gözden geçirme ya da uzlaştırma turu değil, bir **çözme yetkisidir**. Defteri
üç kalemde okunur.

**Tuttuğu ve yalnız kendisinin tuttuğu.** Ortam başına anahtar yerleşiminde engellenen (özne,
kayıt) çifti 682'dir. Bu sayının tamamı marjinaldir, çünkü 29 öznenin hepsi depoyu zaten
okuyabiliyor — depo erişim kapısı bu çiftlerin hiçbirini durdurmuyor. Şifreleme kalksa 682 okuma
serbest kalırdı; kaba bir yerleşimde bile kapı tek başına iş görüyor. Ortam-servis yerleşiminde
aynı sayı 840'a çıkar.

**Kaçırdığı.** İki sınıf var. Birincisi geçmiştir: 35 şifreli sürüm kapının hiç devreye girmediği
bir yoldan okunabilir, çünkü eski anahtar sızdığında ortada bir yetki denetimi yoktur. İkincisi
fazlalıktır: ortam yerleşiminde 158 çift, anahtarı meşru taşıyan öznelerin ihtiyacından fazlasını
açar. Kapı bunları tutmaz, açıkça geçirir — ölçüm ekibinin 6 üyesi geliştirme ortamının faturalama
sırlarını da çözebilir.

**Yanlış tuttuğu ve eklediği süre.** Son tablo takası kapatıyor. Ortam-servis yerleşimi fazlalığı
sıfırlar, ama 24 gece olayının 11'inde üretim nöbetçisi ihtiyaç duyduğu anahtarı taşımıyordu ve her
olay 35 dakikalık eskalasyon ekledi: 385 dakika. Ortam yerleşiminde bu sayı sıfırdır, karşılığında
158 fazla çift açıktır. Karar bu iki sayı arasında verilir ve "daha ince anahtar daha güvenlidir"
cümlesi tek başına karar taşımaz. Kapının işletim yükü de sayılıdır: 33 kaydın 180 günlük geçmişi
104 şifreleme işlemi, doğrulaması 35 çözme işlemi eder; her sır değişikliği bir şifreleme, her
uzlaştırma turu ajan tarafında bir çözme ister.

## Özet

- 29 özne ve 33 kayıtla olası 957 (özne, kayıt) çiftinden tek anahtarlı yerleşim 495'ini açıyor,
  ortam başına anahtar 275'ini, ortam-servis başına anahtar 117'sini; ihtiyaç üç yerleşimde de 117.
- Fazlalık yetki 378'den 158'e, oradan 0'a iniyor; en geniş öznenin çözebildiği kayıt 33'ten 11'e.
- Bir anahtarı döndürmek tek anahtarlı yerleşimde 33 kayıt ve 12 dosya, ortam-servis yerleşiminde
  3 kayıt ve 1 dosyadır; ama bütün anahtarları döndürmek üç yerleşimde de 33 kayıt ve 12 dosyadır.
- Üretim anahtarı döndürüldükten sonra eski anahtar sızarsa geçmişteki 35 şifreli sürüm çözülüyor
  ve 11'i 11 üretim kaydının bugünkü değeri — anahtar döndürme geçmişi kapatmaz, değer döndürme
  kapatır.
- Çözme yetkisi kapısının marjinal değeri ortam yerleşiminde 682 engellenen çifttir ve tamamı
  marjinaldir, çünkü 29 öznenin hepsi depoyu okuyabiliyor.
- Kapının bedeli ölçüldü: ortam-servis yerleşiminde 24 gece olayının 11'i anahtarsız kaldı ve 385
  dakika eskalasyon ekledi; ortam yerleşiminde gecikme 0, açık fazlalık 158 çifttir.

## Sonraki Adım

Sırlar depoda şifreli durabiliyor ve kimin neyi çözebildiği sayılabiliyor. Bu, depodaki istenen
durumun **içeriğine** dair bir soruydu. Bir sonraki ders içeriğin başka bir yüzüne döner: bir yayının
gerçekte hangi bileşenlerden oluştuğu. Uygulama kendi kodundan ibaret değildir — doğrudan ve dolaylı
bağımlılıklar, çalışma zamanı kitaplıkları, taban katman içeriği. Ölçülecek şey bu envanterin
kapsamıdır: kaç bağımlılık görünüyor, kaçı dolaylı, envanteri üretmek ne kadar sürüyor ve envanteri
üretmeyen bir hatta kaç bileşen hiç sayılmamış oluyor.
