Bak şimdi, Bir uygulamayı çalıştırmakla, onu güvenli çalıştırmak arasında dağlar kadar fark var. Gerçekten. 2023’ün sonlarında İstanbul’da bir kurumsal projede bunu acı şekilde gördüm; konteyner ayağa kalkıyordu, her şey “çalışıyor” görünüyordu, ama içerideki yetkiler gevşek bırakıldığı için ekip gece yarısı “neden bu pod her yere yazabiliyor?” diye birbirini arıyordu. Panik tam anlamıyla. İşte Zero-Trust bakış açısı tam da bu noktada devreye giriyor: varsayılan olarak kimseye güvenme, hatta kendi container’ına bile.
Eh, Bu yazıda Golang ile hazırlanmış güvenli bir backend’in GKE üzerinde nasıl daha sıkı kurallarla ayağa kaldırıldığını kendi ağzımla anlatacağım. Sahadan bakıyorum olaya — production’a çıkınca mesele sadece “çalışsın” değil, “yanlış şeyler yapamasın” oluyor. Hani bazen bir sistem gayet hızlıdır ama kapıları ardına kadar açıktır ya… işte tam o durum.
Güvenlik neden deployment aşamasında başlıyor?
Birçok ekip güvenliği uygulamanın içine sıkıştırmaya çalışıyor. Güzel niyet. Ama yetmiyor. Çünkü container imajı tertemiz olsa bile runtime’da verilen izinler gevşekse saldırganın işi inanılmaz kolaylaşıyor — küçük bir açıklık zincirlenerek büyüyebiliyor ve sen fark ettiğinde iş işten geçmiş oluyor.
Şimdi gelelim işin can alıcı noktasına.
2024 başında Ankara’daki küçük bir SaaS ekibinde bunu test ederken net gördüm. Distroless imaj kullanıyorlardı, evet — bu iyi. Fakat pod root gibi davranabildiği için durum tehlikeliydi. Yani imaj temizliği tek başına yetmiyor, runtime tarafı da sıkı olmalı.
İşin aslı şu ki Kubernetes sana çok güçlü bir kontrol yüzeyi veriyor. O yüzeyin üstünde ne bıraktığın önemli. securityContext‘i pod’un eline görünmez kelepçe takmak gibi düşün — çok sert geliyor olabilir,. Production’da tam da bunu istiyorsun.
Küçük bir detay: Geçen ay Berlin’de uzaktan çalışan bir ekip arkadaşım bana “container zaten izole değil mi?” diye sordu. Klasik soru bu. Cevap kısa: kısmen evet, yeterince hayır. Container izolasyonu tek başına mucize değil; Linux kernel paylaşılıyor, namespace paylaşılıyor ve bazı kaçış senaryoları hâlâ ciddi dert olabiliyor. O yüzden ekstra katman şart.
GKE Autopilot neden bu iş için bayağı uygun?
Şöyle düşün: node yönetimiyle kafa yormuyorsun, Google alttaki işi büyük ölçüde üstleniyor, sen de enerjini ürüne harcıyorsun. Küçük bir startup için bu gerçekten nimet gibi geliyor — ekibin zaten darmadağın olduğu dönemde bir de node bakımıyla boğuşmak istemiyorsun.
Tabi bir parantez açayım: Autopilot sihirli değnek değil. Her şeyi otomatik yapıyor diye düşünürsen yanılırsın. Manifest’i düzgün yazmazsan sistem seni kurtarmıyor. Kolaylık var, evet — ama disiplin hâlâ sende (yanlış duymadınız) Bu konuyla ilgili WordPress SSO’da Ayrı Domainler İçin Sağlam Yol Haritası yazımıza da göz atmanızı tavsiye ederim.
Bunu yaşayan biri olarak söyleyeyim, Büyük kurum tarafında avantaj başka yerde ortaya çıkıyor. Standardizasyon sağlıyor, operasyon yükünü azaltıyor. Güvenlik politikasını tek yerden daha tutarlı uygulamana yardım ediyor. Bir bankacılık projesinde buna benzer bir geçişi yakından izlemiştim; asıl kazanç hızdan çok uyumluluk ve öngörülebilirlikti. Bunlar küçük şeyler değil. Bu konuyla ilgili AOC Agon Pro’nun Yeni OLED Canavarı: Fiyatı Belli Oldu yazımıza da göz atmanızı tavsiye ederim. Daha fazla bilgi için dağıtım konusundaki yazımız yazımıza bakabilirsiniz.
Küçük ekip vs enterprise yaklaşımı
| Senaryo | Neye odaklanır? | Zor tarafı ne? |
|---|---|---|
| Küçük startup | Hızlı yayına alma, düşük operasyon yükü | Maliyet ve yanlış yapılandırma riski |
| Enterprise | Uyumluluk, denetim izi, standardizasyon | Süreçlerin ağırlaşması |
securityContext: Pod’un kemerini sıkmak
Lafı gevelemeden söyleyeyim. Bu bölüm yazının kalbi. Container’a hangi hakları verdiğini burada belirliyorsun ve buradaki her satırın ağırlığı var — root olarak çalışmamak ilk adım, sonra privilege escalation kapatılıyor, ardından dosya sistemi salt okunur hale getiriliyor ve capability’ler topluca düşürülüyor.
apiVersion: apps/v1
kind: Deployment
metadata:
name: secure-api-deployment
spec:
replicas: 3
selector:
matchLabels:
app: secure-doc-api
template:
spec:
securityContext:
runAsNonRoot: true
runAsUser: 65532
containers:
— name: secure-api
image: us-central1-docker.pkg.dev/PROJECT_ID/REPO/IMAGE:latest
ports:
— containerPort: 8080
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
— ALL
runAsUser: 65532 detayı özellikle önemli — distroless dünyasında sık görülen kullanıcı numaralarından biri bu, uygulama kökten bağımsız çalışıyor. İyi haber bu. Kötü haber ise şurada çıkıyor: eğer uygulamanın gerçekten dosya yazmaya ihtiyacı varsa ekstra volume ya da tmp alanı tasarlaman gerekiyor. Yani güvenliği artırırken mimariyi de biraz dürtüyorsun — bunu baştan hesaba katmak lazım.
Şimdi gelelim işin can alıcı noktasına.
Daha açık söyleyeyim, küçük bir detay:
Salt okunur dosya sistemi kulağa sert geliyor. Pratikte saldırganın elini ciddi biçimde bağlıyor; RCE çıksa bile rastgele dosya oluşturup işi büyütmesi zorlaşıyor.
Neden readOnlyRootFilesystem gerçekten işe yarıyor?
Bunu ilk kez canlı ortamda test ettiğimde şaşırdım açıkçası. İnsan önce şunu düşünüyor: “Tamam da uygulama log yazacaksa ne olacak?” Haklı soru. Fakat doğru tasarımda log akışı stdout üzerinden gider, geçici dosyalar da ayrı mount edilir —. Bu sorunun çözümü var, sadece biraz düşünmek gerekiyor.
Dürüst olmak gerekirse, Saldırgan kabuğa sızarsa ya da RCE yakalarsa önüne duvar örmüş oluyorsun. Script indirip diske basması zorlaşıyor. Config değiştirmesi zorlaşıyor. Klasik kötü niyetli hareketlerin çoğu patlıyor — ya da en azından ciddi ölçüde yavaşlıyor. Bu fark küçük değil.
Pipeline tarafında değişmeyen şey yok mu? Var tabii!
E tabi dağıtım sadece manifest ile bitmiyor. Cloud Build tarafında imaj build edildikten sonra yeni sürümü doğrudan cluster’a itiyorsun ve buradaki kritik nokta tag kullanımı oluyor. 32 Saat Geri Kazandıran Yapay Zekâ Ajanları: Gerçek Hesap yazımızda da bu konuya değinmiştik. Pokemon Kartlarında Yapay Zekâ Hamlesi: Fiyat, Grading ve Ajanlar yazımızda da bu konuya değinmiştik.
$COMMIT_SHA ile imaj etiketlemek bence güzel hamlelerden biri — immutable infrastructure fikrini destekliyor, aynı kod commit’i eşittir aynı çalışan sürüm demek oluyor. Ne yürüdüğünü bilmek güzel şeydir. Mesela gece saat ikide prod alarmı çalıyorsa…
- name: 'gcr.io/cloud-builders/kubectl'
args:
— 'set'
— 'image'
— 'deployment/secure-api-deployment'
— 'secure-api=$_REGION-docker.pkg.dev/$PROJECT_ID/$_REPO_NAME/$_IMAGE_NAME:$COMMIT_SHA'
Bana sorarsan bunun en sevdiğim yani rollback kolaylığı oldu. Frankfurt bölgesinde çalışan bir servisimizde yeni imaj ufak bir davranış farkı üretmişti bir keresinde; commit bazlı tag kullandığımız için eski sürüme dönmek iki dakikalık işti — tamam, belki üç dakika. Tag’siz deploy’larda ise insan resmen tahmin oyunu oynuyor, “acaba hangisi?” diye.
Koddan ziyade süreç de önemli
Açıkçası, Sadece teknik komutları ezberlemek yetmiyor. Mesela CI/CD hattında onay mekanizması koymadıysan yanlış imaj yanlış anda prod’a gidebilir. Küçük ekiplerde bu hata genelde “bir kere olur” diye geçiştiriliyor. Ama o bir kere bazen haftasonunu yiyor.
- Versiyon sabitle: Belirsiz “latest” yerine commit tabanlı tag kullan. — bunu es geçmeyin
- Salt okunur kök dizin: Gereksiz yazmayı kapat.
- Kapasiteyi düşür: ALL capability’lerini atıp gerekirse tek tek geri ekle. (bu kritik)
- Kullanıcıyı sınırla: Root dışına çıkma konusunda taviz verme.
Peki eksik tarafları yok mu? Var, hem de az buz değil
Açık konuşayım: bu yaklaşım kusursuz değil. Read-only filesystem bazı geliştirme alışkanlıklarını bozuyor — legacy kodlar aniden ağlamaya başlayabiliyor çünkü /tmp’ye ya da çalışma dizinine yazmaya alışmış oluyorlar. Düzeltmesi mümkün ama emek istiyor. Ciddi emek.
Ayrıca Autopilot’ın rahatlığına fazla güvenmek de riskli olabiliyor (bizzat test ettim). Nodlarla uğraşmıyorum diye politikaları gevşetirsen iş ters teper — bunu gördüm, yaşandı, acı ders oldu. Güzel özellik ama körü körüne güvenmek tehlikeli.
Benden sahadan iki küçük not
Editör masasında bu konuyu işlerken kendi lab ortamımda aynı manifest’i denedim (en azından benim deneyimim böyle). MacBook üzerinde minik bir local cluster kurup bilinçli olarak write denemeleri yaptım; sonuç beklediğimden iyi çıktı, şaşırdım açıkçası. Uygulama bazı yerlerde hata verdi ama tam da istediğimiz şey buydu zaten — sessizce yanlış davranmasındansa yüksek sesle “dur” demesi çok daha değerli.
Bir başka örnek de İzmir’den. Bir arkadaşım müşteri portalını GKE’ye taşırken ilk etapta her şeyi root bırakmıştı. Sonra security audit gelince toparlamak zorunda kaldılar ve en çok zaman alan şey kod değil manifest düzeni oldu. Yani mesele yalnızca teknik beceri değil; mimari alışkanlık meselesi de var. Mantıklı değil mi? Baştan doğru kurmak, sonradan düzeltmekten kat kat kolay.
Bu yüzden önce küçük başlayıp sonra politikaları genişletmek çoğu zaman daha akıllıca.
Sıkça Sorulan Sorular
Bu içerik işinize yaradı mı?
Benzer içerikleri kaçırmamak için beni sosyal medyada takip edin.



