Hiç sabahın köründe 'Cluster patlamış abi, podlar birbirini yiyor' diye uyanıp, sorunun aslında sadece 6443 portunun tüm dünyaya açık olması olduğunu fark ettiğin oldu mu? Eğer bu soruyu okurken hafifçe gülümsediysen veya içinden 'Of, biz de az kalsın yaşıyorduk' dediysen, doğru yerdesin demektir.
Selam millet, ben Sedat. Bugün biraz 'modern altyapıların baş tacı, güvenlikçilerin ise bazen kabusu' olan Kubernetes (K8s) dünyasına dalacağız. Biz Red Team tarafında K8s cluster'ı gördüğümüzde gözlerimiz parlıyor, çünkü genellikle 'çalışsın da nasıl çalışırsa çalışsın' mantığıyla ayağa kaldırılmış devasa bir oyun alanı buluyoruz. Ama bugün masanın diğer tarafına geçip, o oyun alanını nasıl güvenli bir kaleye çeviririz, onu konuşacağız.
API Server: Kalbin Kapılarını Açık Unutmak
Kubernetes'in beyni API Server'dır. Her şey onun üzerinden döner. Eğer API Server'ı doğru korumazsanız, bütün cluster'ın anahtarını bir gümüş tepside saldırgana sunmuş olursunuz.
Bir sızma testinde testCompany sistemlerini incelerken gördüğümüz en klasik hata, --anonymous-auth=true bayrağının açık bırakılmasıdır. Bu, kapıdaki güvenliğe 'Ben kimim önemli değil, içeri girmeme izin ver' demekle aynı şey.
Bir saldırgan olarak şunu yapabilirim:
# Defanged K8s API request
curl -k https://127.0.0.1:6443/api/v1/namespaces/default/pods
Eğer bu komut bana bir çıktı döndürüyorsa, geçmiş olsun. İçerideki tüm podları, onların içindeki environment variable'ları (ki genellikle veritabanı şifreleri orada olur) görebiliyorum demektir.
Ne yapmalıyız? API Server yapılandırmasında anonymous erişimi kapatın ve mutlaka RBAC (Role-Based Access Control) kullanın. Ayrıca, API Server'ı internete değil, sadece iç ağa veya belirli VPN iplerine açın. 'Whitelisting' hayat kurtarır.
RBAC: 'Herkes Admin' Modundan Çıkma Zamanı
K8s dünyasındaki en büyük yanılgılardan biri, geliştiricilere veya servis hesaplarına (ServiceAccounts) gereğinden fazla yetki vermektir. Bir mikroservisin sadece kendi namespace'indeki podları listelemesi gerekiyorsa, ona cluster-admin yetkisi vermeyin. Bu, sinek öldürmek için atom bombası kullanmak gibidir.
Red Team operasyonlarında bir podun içine sızdığımızda ilk yaptığımız şey /var/run/secrets/kubernetes.io/serviceaccount/token dosyasını kontrol etmektir. Eğer o token ile kubectl get secrets --all-namespaces komutunu çalıştırabiliyorsak, o cluster artık bizimdir.
Şu tehlikeli ClusterRole örneğine bir bakalım:
# TEHLİKELİ - ClusterRole Örneği
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: her-seyi-yapan-rol
rules:
- apiGroups: [""]
resources: ["*"]
verbs: ["*"] # Her şeyi yapabilir!
Bunun yerine 'Least Privilege' (En Az Yetki) prensibini uygulayın. Sadece gereken kaynağa, gereken yetkiyi verin.
Secrets: Base64 Şifreleme Değildir!
Bunu her fırsatta söylüyorum ama hala karşımıza çıkıyor: Base64 bir şifreleme (encryption) yöntemi değildir, sadece bir kodlama (encoding) biçimidir. Kubernetes 'Secret' nesneleri default olarak ETCD üzerinde şifrelenmeden tutulur (Eğer 'Encryption at Rest' açık değilse).
Bir saldırgan ETCD'ye erişirse, bütün secret'ları saniyeler içinde çözer:
# Defanged secret decode
echo "TDRmYVNoaWZyZWkxMjM=" | base64 --decode
# Çıktı: MySuperSecret123
Çözüm: EncryptionConfig kullanarak ETCD üzerindeki verileri şifreleyin. Ya da daha iyisi, HashiCorp Vault veya AWS Secrets Manager gibi harici bir çözüm kullanıp podlara 'sidecar' veya 'CSI driver' ile inject edin. testCompany gibi ölçeklenen yapılarda bu kaçınılmazdır.
Network Policies: İçerideki Vahşi Batı
K8s cluster'larında varsayılan olarak her pod, her podla konuşabilir. Bir front-end podu, arka taraftaki veritabanı poduna direkt 'selam' diyebilir. Eğer front-end podunuzda bir RCE (Remote Code Execution) açığı varsa, saldırgan cluster'ın içinde istediği gibi 'lateral movement' yapabilir.
Burada 'Network Policy' kavramı devreye giriyor. Cluster içinde mikro-segmentasyon yapmak zorundasınız.
Örnek bir 'Deny All' politikasıyla başlayıp, sadece gereken trafiğe izin verin:
# Varsayılan her şeyi engelle
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
Bu politikayı uyguladığınızda cluster sessizliğe bürünür. Sonrasında 'Front-end sadece Back-end'e gitsin' gibi kuralları tek tek eklemeniz gerekir. Zahmetli mi? Evet. Güvenli mi? Kesinlikle.
Runtime Güvenliği ve Konteyner İzolasyonu
Podları privileged: true modunda çalıştırmak, o podun host makine üzerinde root yetkilerine sahip olması demektir. Eğer podun içinden kaçıp (container breakout) host makineyi ele geçirmek istiyorsak, ilk aradığımız şey budur.
Pod Security Admission (eski adıyla Pod Security Policies) kullanarak bu tarz tehlikeli yapılandırmaları daha pod oluşmadan engelleyebilirsiniz. Ayrıca, podların root user ile çalışmasını engelleyin:
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
Unutmayın, bir saldırgan podun içine girdiğinde eğer root değilse, işi çok daha zordur. Sistemi ne kadar kısıtlarsanız, hata payını o kadar azaltırsınız.
Son Söz Yerine: İzlemediğin Şey Senin Değildir
Loglama ve monitoring K8s güvenliğinin olmazsa olmazıdır. Falco gibi araçlarla runtime sırasında şüpheli hareketleri (örneğin bir pod içinde shell açılması veya beklenmedik bir binary'nin çalıştırılması) yakalayabilirsiniz.
Kubernetes karmaşık bir canavar, evet. Ama onu evcilleştirmek bizim elimizde. Güvenliği en sona bırakmak yerine, daha tasarım aşamasında (Shift Left) sürece dahil etmek gerekiyor. Yoksa gece saat 03:00'te o telefon mutlaka çalar.
Bir sonraki yazıda görüşmek üzere, cluster'ınıza ve podlarınıza iyi bakın. Root kalmayın!
