Bir itirafla başlayayım. Geçen yıl bir bankacılık müşterimizde production cluster’a bootstrap sırasında girdik — yeni bir region açılışı vardı, etcd snapshot’tan restore ediyorduk. API server kalktı, kubelet’ler bağlandı, derken bir cron job devreye girdi ve privileged: true ile bir pod ayağa kalktı. Politikalarımız mı? Henüz yüklenmemişti. Yani teknik olarak doğru çalışıyordu her şey… ama güvenlik açısından üç dakikalık bir “her şey serbest” penceresi yaşadık.
Acıydı. Hatta biraz saçmaydı.
Şunu fark ettim: İşte tam bu yaraya dokunan yerde Kubernetes v1.36, açık konuşayım, baya ilginç bir alpha özellik getiriyor: manifest tabanlı admission control. Kısaca, admission politikalarını artık disk üzerinde dosya olarak tanımlayıp API server daha tek bir istek bile karşılamadan yüklüyorsunuz. Silinemiyor da. Yani biri API üzerinden kurcalamaya kalksa bile iş değişmiyor; çünkü ortada API objesi yok, dosya var, düz dosya.
Peki neden önemli?
Bakın, Çünkü bootstrap anı hep biraz kaygan ölür. Bir yandan cluster ayağa kalksın istersiniz, öte yandan ilk gelen pod’un kafasına göre privileged açmasını istemezsiniz; hani tam o aralıkta küçük bir boşluk oluşur ya, işte bu özellik önü daraltmaya çalışıyor. Şey gibi düşünün: kapıyı açmadan önce kilidi takmış oluyorsunuz.
Bilmem anlatabiliyor muyum, Tabii burada ufak bir not düşeyim: bu özellik alpha seviyesinde, yani üretimde hemen “tamamdır bunu standart yapalım” demek biraz erken olabilir. Ama fikir temiz; manifestleri diskten yükle, politikayı erken ayağa kaldır, sonra API tarafı gelince zaten koruma başlamış olsun.
Kısa bir not düşeyim buraya.
Evet. etcd 3.7.0-beta.0 Yayında: RangeStream ve v2store Vedası yazımızda bu konuya da değinmiştik.
Benim hoşuma giden taraf şu oldu: politika yönetimi artık sadece cluster içindeki nesnelere bağlı kalmıyor. Yani config’i kaybettin mi bitti senaryosu biraz azalıyor. Yukarıda anlattığım olayda o üç dakikalık pencere olmasaydı, o pod muhtemelen hiç şans bulamayacaktı.
Az önce “silinemiyor” dedim ama dür bir saniye — aslında mesele sadece silinememesi değil, daha çok API server’ın bu kuralları başlangıçta dışarıdan bağımsız okuyabilmesi. Bu da özellikle recovery, bootstrap ve kontrollü açılış senaryolarında baya iş görüyor.
Dürüst olmak gerekirse, Siz ne dersiniz? Böyle bir yaklaşımın asıl değeri güvenlikte mi, yoksa operasyonel rahatlıkta mı?
Neden bu kadar önemli bir mesele?
Aslında çoğu kişi bu sorunu yaşadığını bile fark etmiyor. Çünkü ortam sakinken her şey yolunda gidiyor, politikalarınız etcd’de duruyor, ValidatingAdmissionPolicy nesneleri görevini yapıyor, hayat da gayet sakın akıyor. Ama sonra iki yerde iş bir anda karışıyor; biri bootstrap penceresi, öteki de self-protection tarafı.
Birinçisi bootstrap penceresi. API server ayağa kalkıyor, istek almaya başlıyor ama o politika nesneleri henüz yüklenmemiş olabiliyor. GitOps’tan geliyorsa Flux/ArgoCD’nın reconcile etmesini bekliyorsunuz, Helm’den geliyorsa init job’unun bitmesini kolluyorsunuz. Küçük bir cluster’da bu 30 saniye sürebiliyor, disaster recovery senaryosunda işe dakikalarca uzayabiliyor; işte tam burada insanın canı sıkılıyor.
Ne yalan söyleyeyim, İkincisi — ve açık konuşayım, daha sınır bozucu olanı — self-protection problemi. Bakın şöyle: Kubernetes, döngüsel bağımlılık çıkmasın diye ValidatingWebhookConfiguration veya ValidatingAdmissionPolicy gibi kaynakların kendi üzerlerindeki işlemleri admission chain’den geçirmiyor. Yani teoride cluster-admin yetkisi olan biri, en kilit güvenlik politikanızı bir kubectl delete ile silip götürebiliyor. Evet, bayağı tatsız.
Kısa bir not düşeyim buraya.
Türkiye’deki kurumsal müşterilerimde bunu özellikle regülasyona tabi sektörlerde (bankacılık, sigorta, telekom) sık görüyorum. BDDK denetiminde size “PCI kapsamındaki cluster’da admission policy’lerin silinmesini nasıl engelliyorsunuz?” diye sorduklarında “ee, RBAC ile” demek pek ikna edici olmuyor sayın denetçiye. Hatta bazen soru basit geliyor. Cevap kısmı biraz çamura yatıyor; çünkü mesele sadece yetki değil, zincirin kendisini korumak.
İşin mekaniği: staticManifestsDir
İnanın, Çözüm aslında baya sade. API server’a zaten --admission-control-config-file flag’i ile bir AdmissionConfiguration dosyası veriyorsunuz, değil mi? Şimdi bu dosyanin içine yeni bir alan ekleniyor: staticManifestsDir. Bir klasör gösteriyorsunuz, YAML dosyalarınızı oraya bırakıyorsunuz, API server açılırken bunları okuyup belleğe alıyor. Hepsi bu.
Peki neden?
apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
— name: ValidatingAdmissionPolicy
configuration:
apiVersion: apiserver.config.k8s.io/v1
kind: ValidatingAdmissionPolicyConfiguration
staticManifestsDir: "/etc/kubernetes/admission/validating-policies/"
Burada tek bir şart var,. Açık konuşayım, önemli bir şart bu: o dizindeki tüm nesnelerin adı .static.k8s.io ile bitmek zorunda. Kısacası, peki neden? İki sebep var. Birinçisi, normal API nesneleriyle işim çakışması olmasın diye. İkincisi — bence daha pratik tarafı bu — audit log’a ya da metric’lere bakınca “bu admission kararı nereden geldi?” sorusunun cevabını isimden hemen çıkarabiliyorsunuz.
Evet.
Tam bir örnek üzerinden bakalım
Diyelim ki kube-system dışında hiçbir yerde privileged container çalışmasın istiyoruz (en azından benim deneyimim böyle). Klasik senaryo yani. Hani ilk bakışta basit duruyor ama sonra iş biraz kayıyor; çünkü policy’nın nerede tutulduğu da en az kuralın kendisi kadar önemli oluyor, özellikle de cluster büyüyünce. Kim neyi ne zaman değiştirdi sorusu ortaya çıkınca (buna dikkat edin) Bu konuyla ilgili npm Staged Publishing GA: Tedarik Zinciri Artık Daha Güvenli yazımıza da göz atmanızı tavsiye ederim.
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
name: "deny-privileged.static.k8s.io"
annotations:
kubernetes.io/description: "kube-system harici privileged pod yasak"
spec:
failurePolicy: Fail
matchConstraints:
resourceRules:
— apiGroups: [""]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["pods"]
validations:
— expression: "object.spec.containers.all(c, !has(c.securityContext) || c.securityContext.privileged != true)"
message: "Privileged container'a izin yok kardesim"
Bu dosyayı diske koyduğunuz anda artık kubectl get validatingadmissionpolicies komutu bunu göstermez. Çünkü etcd’de yok. Sadece API server’ın belleğinde duruyor. Silmek isterseniz de API server’ı durdurup dosyayı kaldırmanız lazım; yani iş biraz daha sertleşiyor (genelde node’a SSH erişimi gerekiyor). Şey, burada güzel olan taraf şu: ortada görünmeyen ama çalışan bir policy var ve bunu yanlışlıkla silmek de öyle kolay olmuyor.
Çok konuştum, örnekle göstereyim.
Peki, tam da öyle. Daha fazla bilgi için Visual Studio Plan Agent: Önce Düşün, Sonra Kodla yazımıza bakabilirsiniz.
API tabanlı vs Manifest tabanlı: hangisi ne zaman?
Burada küçük bir tuzak var, dikkat. Her politikayı manifest tabanlı yapmak iyi fikir değil; bazen işin içinde esneklik oluyor, mesela geliştirici ekip “şu namespace’te şu image registry’sine de izin verin” deyince, bunu her defasında API server restart ederek çevirmek açıkçası can sıkıyor.
Bence, Bende bu ayrım kafamda şöyle oturdu: bazı kurallar var ki yerinden oynamasın, nokta. Ama bazıları da var, hani ekipten ekipte değişiyor, ortamdan ortama kayıyor (ve dürüst olayım, bunları sert kalıba sokunca sonra siz uğraşıyorsunuz).
| Politika Türü | Manifest Tabanlı | API Tabanlı |
|---|---|---|
| Privileged container yasağı | ✅ Evet | ❌ Hayır |
| hostNetwork/hostPID kısıtı | ✅ Evet | ❌ Hayır |
| Registry whitelist (esnek) | ❌ | ✅ Evet |
| Resource limit zorunluluğu | Duruma göre | Duruma göre |
| Compliance (PCI, HIPAA) kuralları | ✅ Evet | |
| – Hayır
Lafı gevelemeden söyleyeyim: “hiçbir koşulda esnetilmemeli, kimse dokunmamalı” dediğiniz şeyler manifest’e gider (evet, doğru duydunuz). “Duruma göre değişebilir, ekipler talepte bulunabilir” dedikleriniz işe API tarafında kalır; yani orada yönetmek daha rahat ölür, ama tabi biraz da disiplin ister. Evet. Peki, peki neden? Çünkü bazı kurallar güvenlik ve compliance tarafında çizgi çekiyor, öbürleri işe günlük operasyonun içinde kıvrılıp bükülüyor; işin aslı bu kadar basit görünüyor ama pratikte ince ayar kaçınca sonradan toparlaması zor oluyor. Neyse, çok uzatmayayım. Siz ne dersiniz? Daha fazla bilgi için MSVC’de SPGO Devri: Production’dan PGO Kalitesi yazımıza bakabilirsiniz. Türkiye perspektifinden bir değerlendirmeKurumsal müşterilerde Kubernetes adaptasyonu var ya, özellikle güvenlik tarafı, biraz garip bir rota izliyor. Şirketler önce OPA Gatekeeper’a abanıyor, sonra Kyverno’ya bakıyorlar, ardından native ValidatingAdmissionPolicy’ye dönüyorlar; şimdi bir de manifest tabanlı kontrol gelince ekiplerin kafası iyice karışıyor, haklılar da yani. Yani, Açık konuşayım: Türkiye’deki orta ölçekli şirketler için bu özellik bugün için biraz fazla kaçıyor. Çoğu cluster zaten düzgün RBAC ile idare ediyor, o iş şimdilik fena değil. Ama enterprise tarafa geçince — mesela 50+ cluster’lık fleet’i olan bankalar, telekomlar, e-ticaret devleri — iş değişiyor; burada manifest tabanlı kontrol bayağı işe yarıyor, çünkü “bir kere yazayım, her yerde çalışsın, kimse ellemesin” fikri tam da o ölçekte anlam kazanıyor. Bir de maliyet ve işletim tarafı var, hani herkes önü sonradan fark ediyor. AKS, EKS, GKE gibi managed servislerde staticManifestsDir’e doğrudan dosya bırakma şansınız şimdilik yok; Microsoft tarafında bunun AKS’ye gelmesi muhtemelen 1.36+1 veya +2 sonrası ölür diye düşünüyorum (yani 2026 sonunu beklemek gerekebilir). Self-managed cluster çalışanlar işe — kubeadm olsun, Cluster API olsun, on-prem olsun — bunu hemen kurcalayabiliyor. Daha açık söyleyeyim, peki neden? Çünkü kontrol düzeyi bambaşka.
Pratik kurulum: ilk adımlarDiyelim ki karar verdiniz, tamam dediniz. Self-managed bir cluster’da iş biraz elle tutuluyor,. Sihirli bir buton yok; önce
💡 Bilgi: İlk denememde failurePolicy’yi
Fail bıraktım ama matchConstraints’te bir typo vardı. API server start oldu, ama her pod create isteği reddedildi — kube-system dahil! 4 dakika cluster offline gitti. Önerim: production’a koymadan önce mutlaka Ignore ile veya bir dry-run/test cluster’da deneyin. Bu da uzmanın söylediği klişe bir laf değil, başıma geldi çünkü.
Evet, burası biraz hassas. Neyse, peki neden? Çünkü küçük bir yazım hatası bile tüm admission akışını ters yüz edebiliyor, hele de policy’yi doğrudan fail modunda çalıştırdıysanız iş büyüyor; ben olsam önce sakın sakın test ederim, sonra prod’a dokunurum. Neyse, çok dağıtmayayım. Yukarıda bahsettiğim o olay var ya, işte onun yüzünden artık böyle işleri önce lab ortamında dönduruyorum; hem log okumak kolay oluyor hem de “cluster neden sustu?” sorusunu gece yarısı kendime sormuyorum. Azure NetApp Files EDA İçin: Bulutta Çip Tasarımı Devri yazımızda bu konuya da değinmiştik. Tam da öyle. Pat diye akla gelen sorularWebhook’lar da destekleniyor mu?Evet. ValidatingWebhookConfiguration. MutatingWebhookConfiguration nesnelerini de aynı şekilde manifest dizinine koyabiliyorsunuz. Ama burada küçük bir pürüz var: webhook tabanlı bir politika yazarsanız, o webhook’un servisinin cluster ayağa kalkar kalkmaz erişilebilir olması gerekiyor, yoksa Peki ya güncellemek istersem?Bunu yaşayan biri olarak söyleyeyim, Dosyayı diskte değiştiriyorsunuz ve sonra API server’ı restart ediyorsunuz. Hot reload yok, en azından şu an için. Kulağa biraz eski usul geliyor, evet; ama işin içinde güvenlik olunca da çok şaşırmıyorum. Çünkü hot reload olsaydı, “diske write erişimi olan biri politikayı değiştirir mi” sorusu hemen kapıda belirirdi, yani esneklik artar ama risk de sessizce yaninda gelir. Şu hâliyle güvenlik tarafını öne koymuşlar. Hmm, bunu nasıl anlatsamdı… Performans etkisi var mı?Bu konuda kaynakta çok detay yok, biraz boşluk bırakmışlar açıkçası. Ama mantık yürütürsek, politikalar bellekte tutulduğu için API tabanlı politikalara göre biraz daha hızlı çalışması gayet mümkün görünüyor; çünkü her seferinde etcd’ye gidip “bu policy hâlâ duruyor mu?” diye bakma derdi yok. Tabii sahada fark edilir mi, ölçmeden konuşmak kolay, orası ayrı. Sınırlamalar ve eksikliklerŞimdi, lafı gevelemeden söyleyeyim: bu özellik daha alpha aşamasında. Yani üretimde koşturmadan önce bir durup bakın, hatta iki kere bakın; ben olsam can alıcı ortamda hemen atlamam, çünkü küçük görünen bir eksik sonra can sıkabiliyor (en azından benim deneyimim böyle). Şu an bildiğimiz açıklar da az değil. Durun, bir saniye.
O yüzden ben müşterilere genelde şunu söylüyorum: kilit politikaları, mesela privileged container yasağı ya da hostPath mount yasağı gibi şeyleri manifest tabanlı tutun; geri kalan operasyonel politikalar API’de kalabilir. Hibrit model işte. Şimdi, açık konuşayım, bu bana baya mantıklı geliyor. Bu arada Kubernetes v1.36 Server-Side Sharded Watch: Saha Notları yazımda da değindiğim gibi, v1.36 biraz “operasyonel olgunluk” sürümü gibi duruyor — yeni oyuncaklardan çok, eksik kalan yerleri toparlamaya odaklanmış durumda. Doğrusu, Evet. Kimler bunu hemen denemeli?Kendi deneyimimden konuşuyorum, Kısaca durup düşünelim. Bugün gidip kurcalaması en mantıklı olan ekipler belli, ama ilk bakışta sanıldığı kadar düz bir liste de değil. Şöyle ki, Self-managed cluster çalıştıran enterprise: Eğer kubeadm, Cluster API, Rancher RKE2 ya da benzer bir araçla kendi cluster’larınızı yönetiyorsanız. Regülasyona tabi bir sektördeyseniz — bankacılık, sağlık, kamu — bu özellik tam size göre. Kubernetes v1.36’da PSI Metrikleri GA: Sahadan Notlar yazısında bahsettiğim gibi, v1.36 sürümü işletim derinliği isteyen ekipler için baya iş görüyor; ama şey, burada asıl mesele sadece sürüm numarası değil, kontrolü elinizde tutma ihtiyacı. Multi-tenant platform sağlayıcıları: Müşterilerinize Kubernetes-as-a-Service sunuyorsanız ve onların cluster-admin yetkisine sahip olabileceği bir model varsa, bu özellik sizin için “kırılmaz koruma katmanı” demek. Evet, biraz iddialı duruyor; yine de çok kiracılı yapılarda küçük bir açık bile sonra baş ağrısına dönüyor. Disaster recovery senaryosu olan ekipler: Etcd restore’ları yapıyorsanız ve restore sırasında oluşan boşluk sızı geceleri uyutmuyorsa. Peki neden? Çünkü böyle ortamlarda ufak görünen veri kaybı ya da tutarsızlık, sabah toplantısında tatsız bir sürprize dönüşebiliyor; hani bazen her şey yolunda giderken tek bir eksik kayıt bütün hikâyeyi bozuyor ya, işte öyle. Buna karşılık, küçük ekipseniz veya managed Kubernetes kullanıyorsanız, şimdilik beklemenizde fayda var. Kubernetes v1.36: Service ExternalIPs Tarihe Karışıyor yazısında olduğu gibi, her v1.36 özelliği herkese hitap etmiyor; açık konuşayım, bazı şeyleri erkenden almak yerine oturup izlemek daha akıllıca oluyor. Sıkça Sorulan SorularManifest tabanlı politikaları kubectl ile görebilir mıyım?Kısaca hayır. Aslında bu bilinçli bir tasarım kararı — hani bu nesneler API’de gerçekten var olmuyor, sadece API server’ın belleğinde yaşıyor. Yani ne Bir static policy hata verirse cluster offline mı kalır?Bu tamamen AKS, EKS veya GKE’de bu özelliği kullanabilir mıyım?Bence, Şu an için hayır, maalesef. Managed Kubernetes servislerinde API server konfigürasyonuna doğrudan erişemediğiniz için OPA Gatekeeper veya Kyverno yerine bunu kullanmalı mıyım?Hayır, yani bunları rakip olarak görmemek lazım aslında. Gatekeeper ve Kyverno daha zengin politika dilleri, daha iyi tooling ve çok daha esnek constraint template’leri sunuyor. Manifest tabanlı admission sadece “hani bunlar asla değişmemeli” dediğiniz birkaç kilit kural için var. Bence ideal kullanım şöyle: kritik %5’lık politikanız manifest’te dursun, geri kalan %95 operasyonel politikanız Kyverno veya Gatekeeper’da. Politikayı güncellemek için her seferinde API server restart şart mı?Şu an için evet. Alpha sürümünde hot reload yok. Açıkçası bu kasıtlı bir tasarım kararı — kaynak makaledeki tartışmalardan anladığım kadarıyla güvenlik gerekçesiyle böyle bırakılmış. İleriki sürümlerde belki signed manifest desteğiyle hot reload gelebilir, ama bu henüz roadmap’te net bir şekilde durmuyor. Kaynaklar ve İleri OkumaKubernetes Resmî Blog: Manifest-Based Admission Control ValidatingAdmissionPolicy Resmî Dokümantasyonu |
Bu içerik işinize yaradı mı?
Benzer içerikleri kaçırmamak için beni sosyal medyada takip edin.



