Düne kadar 'Aman firewall arkasındayız, sunucuya SSH açık değilse sorun yok' diyorduk. Sonra konteynerler geldi, derken kendimizi devasa Kubernetes cluster'larını yönetirken bulduk. Trend artık sunucunun fiziksel güvenliğinden ziyade, o sunucuları yöneten orkestrasyon katmanının ne kadar 'akıllı' yapılandırıldığına evrildi. Eskiden 8 karakterli parolalar tartışılırdı, şimdi ise 'K8s API Server'ı internete açmalı mıyım?' gibi çok daha can yakıcı sorularla boğuşuyoruz. Selam dostlar, ben Sedat. Bugün biraz 'cloud-native' dünyasının karanlık dehlizlerine, yani Kubernetes güvenliğine dalacağız.
API Server: Kalenin Ana Kapısı
Kubernetes dendiğinde akla gelen ilk şey kubectl komutudur. Ama aslında o komutun arkasında devasa bir REST API çalışıyor. Eğer bir saldırgan API Server'ınıza erişim sağlarsa, cluster içindeki her şeyi kontrol edebilir. Genelde 'testCompany' gibi ortamlarda hız kazanmak için API Server'ın public IP üzerinden erişilebilir bırakıldığını görüyoruz. Bu, anahtarı kapının üzerinde bırakmaktan farksız.
Bir Red Teamer olarak ilk baktığım yerlerden biri anonymous-auth ayarıdır. Eğer bu ayar true ise (ki eski sürümlerde varsayılan buydu), yetkisiz bir kullanıcı bile cluster hakkında bilgi toplayabilir.
# Zararsızlaştırılmış kontrol komutu
curl -k https://127.0.0.1:6443/livez
# Eğer buradan 200 OK alıyorsanız, kapı en azından 'orada' demektir.
Savunma Reçetesi: API Server'ı asla halka açık internete açmayın. Sadece belirli IP'lerden (VPN veya Jumpbox) erişime izin verin ve anonymous-auth=false olduğundan emin olun.
RBAC: Yetki Zehirlenmesi
RBAC (Role-Based Access Control) yapılandırmak tam bir baş ağrısıdır, kabul ediyorum. Bu yüzden genelde 'çalışsın da gerisi mühim değil' denilerek her ServiceAccount'a cluster-admin yetkisi veriliyor. İşte bu, bizim en sevdiğimiz 'misconfiguration' türüdür.
Bir podun içine sızdığımızda ilk yaptığımız şey, o podun bağlı olduğu ServiceAccount'un token'ını kontrol etmektir. Eğer o token ile cluster seviyesinde işlem yapılabiliyorsa, oyun biter.
Kötü Bir Örnek (Over-privileged Role):
# BU BİR ANTIPATTERN'DİR - ASLA YAPMAYIN
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: dev-service-account-binding
subjects:
- kind: ServiceAccount
name: default
namespace: dev-app
roleRef:
kind: ClusterRole
name: cluster-admin # Felaket senaryosu!
apiGroup: rbac.authorization.k8s.io
Bunun yerine Least Privilege (En Az Yetki) prensibini uygulayın. Sadece ihtiyacı olan namespace'e, sadece ihtiyacı olan 'verb'leri (get, list, watch) tanımlayın.
Pod Security: İçerideki Casus
Konteynerler izole sanılır ama aslında hepsi aynı kernel'ı paylaşır. Bir podun privileged: true olarak çalıştırılması, o podun host makinedeki cihazlara erişebilmesi demektir. Bu, 'root' olma yolundaki en kestirme bilettir.
Saldırgan bir podun içine girdiğinde ilk deneyeceği şey nsenter gibi araçlarla host namespace'ine geçmektir.
Güvenli Konfigürasyon Örneği:
apiVersion: v1
kind: Pod
metadata:
name: secure-pod
spec:
containers:
- name: app-container
image: example.com/my-secure-app:latest
securityContext:
allowPrivilegeEscalation: false
runAsNonRoot: true
runAsUser: 1000
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
Yukarıdaki örnekte ne yaptık? Root olmayı yasakladık, dosya sistemini sadece okunur yaptık ve tüm gereksiz Linux kabiliyetlerini (capabilities) çöpe attık. İşte 'hardening' budur.
Network Policies: Herkes Herkesle Konuşmamalı
Kubernetes'in varsayılan ağ yapısı 'flat'tir. Yani bir pod, diğer tüm podlarla kısıtlama olmaksızın konuşabilir. 'Front-end' podunuzun, doğrudan 'Database' podunuzun portlarına (örn: 5432) erişebilmesi normal olabilir, peki ya 'Payment' podunuzun 'Monitoring' poduna gitmesi?
Saldırı anında yanal hareketleri (lateral movement) engellemek için NetworkPolicy kullanmak zorunluluktur. Varsayılan olarak her şeyi kapatıp (default-deny-all), sadece gereken trafiğe izin vermelisiniz.
# Default Deny Policy
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
Bu kuralı uyguladığınız an cluster içinde sessizlik hakim olur. Sonra her uygulama için 'whitelist' mantığıyla kural yazarsınız. Zahmetli mi? Evet. Güvenli mi? Kesinlikle.
Secrets Management: 'Base64' Şifreleme Değildir!
En çok karşılaştığım yanılgılardan biri de K8s Secret objelerinin güvenli sanılması. Secret'lar varsayılan olarak sadece base64 ile encode edilir. Yani bir kubectl get secret -o yaml çeken herkes, DB parolanızı kabak gibi görür.
Gerçek bir güvenlik için şunları yapmalısın:
- at-rest encryption: etcd üzerinde verilerin şifreli tutulması.
- External Secrets / Vault: Parolaları Kubernetes dışında (HashiCorp Vault, AWS Secrets Manager vb.) tutup oradan 'inject' etmek.
Son Söz Yerine
Kubernetes karmaşık bir dev. Bu devin dizginlerini elinize almazsanız, o sizi istediği yere sürükler. Güvenlik bir 'check-box' listesi değil, sürekli devam eden bir süreçtir. Cluster'ınızı bir 'Red Teamer' gözüyle incelemekten korkmayın. 'Buradan sızsam nereye giderim?' sorusunu kendinize her gün sorun.
Unutmayın, en güvenli cluster, henüz kurulmamış olandır şakası bir yana; en güvenli cluster, her katmanında 'sıfır güven' (Zero Trust) modelini benimsemiş olandır. Bir sonraki teknik analizimizde görüşmek üzere, terminalinizden hata mesajları eksik olmasın (ama güvenlik açığı asla olmasın)!
