60 saniyede konteyner güvenlik sınırı
Konteynerin içindeki süreç UID 10001 ile çalışıyordu. İmaj tarayıcısı hiçbir kritik paket bulamadı. Kök dosya sistemi salt okunurdu (read-only) ve dağıtım tüm politika denetimlerinden başarıyla geçti. Bunlar faydalı gözlemlerdir. Ancak süreç ele geçirildikten sonra asıl önemli olan soruyu yanıtlamaz:
Bu iş yükü, hedeflenen uygulama sınırının dışında hala hangi yetkilere ulaşabilir?
Non-root bir süreç; bir host bind mount’u üzerinden dosya yazabilir, mount edilmiş bir runtime soketini çağırabilir, aşırı yetkilendirilmiş bir service account kullanabilir, bulut metaveri uç noktasına (IMDS) ulaşabilir, dışa açılmış bir aygıtı tüketebilir veya tehlikeli bir Linux kernel capability’sini kullanabilir. Bu saldırı yollarının hiçbiri, uygulama sürecinin konteyner içinde UID 0 olarak görünmesini gerektirmez.
Bu metodoloji, konteyner güvenliğini bir saldırı yolu (attack path) problemi olarak ele alır. Konteyner imajı, çalışma zamanı (runtime) yapılandırması, orkestrasyon politikası, node ve harici kimlikler tek bir sistem olarak değerlendirilir. Bir güvenlik kontrolü, ancak uygulama sağlıklı kalmaya devam ederken tanımlanmış yetkisiz bir eylem engellendiğinde geçerli sayılır.
Bu kaynak incelemesine dayalı bir test tasarımıdır; gizli bir konteyner kaçış (escape) zafiyeti iddiası değildir. Örnek iş yükünün ele geçirildiğini veya aşağıdaki her kontrolün canlı bir üretim kümesinde uygulandığını iddia etmez. Komutlar mülkiyeti size ait olan bir laboratuvar veya açıkça yetkilendirilmiş bir ortam için tasarlanmıştır; kanıt modeli ise gözlemleri sonuçlardan titizlikle ayırır.
Non-root olmak yalnızca tek bir koordinatı değiştirir
runAsNonRoot: true yönergesi dar ve değerli bir soruyu yanıtlar: Konteyner giriş süreci UID 0 ile başlamamalıdır. Bu kural, uygulama ihlalinin sonuçlarını hafifletebilir, kötü tasarlanmış yazılımların varsayımlarını engelleyebilir ve daha güçlü admission politikalarını destekleyebilir. Ancak tek başına bir izolasyon sınırı değildir.
Efektif güvenlik sınırı birkaç bağımsız düzlemin birleşiminden oluşur:
| Düzlem | Güvenlik sorusu | Yaygın kalan açık yol |
|---|---|---|
| Kullanıcı kimliği | Süreç hangi UID ve GID’yi kullanıyor? | Host dosya mülkiyeti veya ek gruplar erişimi hala yetkilendirir |
| Kernel ayrıcalığı | Hangi capability’ler ve sistem çağrıları (syscalls) açık? | Dar görünen bir capability host açısından kritik bir operasyonu mümkün kılar |
| Dosya sistemi | Hangi host yolları, birimler, soketler ve aygıtlar görünür? | Yazılabilir bir bind mount veya runtime soketi sınırı aşar |
| Süreç izolasyonu | Hangi namespace’ler ve LSM politikaları geçerli? | Host PID/network namespace’i veya kısıtlanmamış (unconfined) bir profil ayrımı ortadan kaldırır |
| İş yükü kimliği | Hangi API kimlik bilgileri enjekte edilmiş veya erişilebilir? | Service account veya bulut kimliği kontrol düzlemi yetkisi sağlar |
| Kaynak sınırlaması | Tek bir süreç neleri tüketebilir? | Eksik CPU, bellek, PID veya depolama sınırları ihlali node kesintisine dönüştürür |
| Yerleşim (Placement) | Node ve kernel’ı başka neler paylaşıyor? | Halka açık bir iş yükü yüksek yetkili bir sistem bileşeniyle aynı node’da oturur |
Asıl önemli özellik bileşimdir (composition). Salt okunur bir kök dosya sistemi, yazılabilir bir host mount’unu salt okunur yapmaz. Çoğu capability’yi düşürmek Docker soketini etkisiz kılmaz. Seccomp, Kubernetes API izinlerini ortadan kaldırmaz. Kısa ömürlü bir service account token’ı geçerlilik süresi boyunca hala son derece güçlüdür.
İncelemeden önce iş yükü sözleşmesini tanımlayın
Genel geçer bir benchmark yerine bir iş yükü sözleşmesiyle (workload contract) başlayın. Sözleşme; konteynerin ne yapması gerektiğini, nereye ulaşabileceğini ve ele geçirildikten sonra nelerin kesinlikle imkansız kalması gerektiğini tanımlar.
En azından şunları kaydedin:
- Onaylanmış imaj özeti (digest) ve derleme kaynağı;
- Uygulama giriş noktası, dinleme portu ve sağlık kontrolü (health check);
- Gerekli yazılabilir yollar ve içeriklerinin kalıcı olması gerekip gerekmediği;
- Gerekli giden (outbound) hedefler ve protokoller;
- Beklenen Linux UID, GID, ek gruplar ve capability’ler;
- Gerekli Kubernetes API eylemleri veya harici bulut izinleri;
- Beklenen RuntimeClass, node havuzu ve komşu iş yüklerinin hassasiyeti;
- Sertleştirme değişikliğinin engellemesi amaçlanan kesin yetkisiz etki.
“Konteyner güvenli olmalıdır” ifadesi yeniden test edilemez. Ancak “Uygulama yalnızca tanımlı veri birimine yazabilir, bir host kontrol soketine erişemez, hiçbir Kubernetes kimlik bilgisi alamaz ve ek ayrıcalıklara sahip yeni bir süreç başlatamaz” ifadesi net bir şekilde test edilebilir.
Bu son cümle doğrudan negatif kontrol planınız haline gelir. Ayrıca istisnaları erkenden açığa çıkarır. İş yükü gerçekten bir aygıta, host namespace’ine, runtime soketine veya geniş kontrol düzlemi kimliğine ihtiyaç duyuyorsa, o standart bir uygulama konteyneri değildir. Yüksek yetkili bir altyapı bileşenidir; özel bir yerleşime, daha dar bir arayüze ve daha güçlü bir izolasyon kararına tabi tutulmalıdır.
Salt okunur taban çizgisini koruyun
Canlı ortam durumunu değiştirmeden önce envanterini çıkarın. Yalnızca Git’teki manifest’e güvenmeyin: Admission mutasyonları, Helm değerleri, platform varsayılanları ve acil durum yamaları çalışan nesneyi incelenen kaynaktan farklı kılabilir.
Kubernetes için aşağıdaki komutlar gizli değerleri yazdırmadan yapılandırmayı toplar:
kubectl version
kubectl get pod -n "$NAMESPACE" "$POD" -o yaml > pod-live.yaml
kubectl get pod -n "$NAMESPACE" "$POD" \
-o custom-columns='NAME:.metadata.name,NODE:.spec.nodeName,SA:.spec.serviceAccountName,HOSTPID:.spec.hostPID,HOSTNET:.spec.hostNetwork'
kubectl get pod -n "$NAMESPACE" "$POD" \
-o jsonpath='{range .spec.containers[*]}{.name}{"\t"}{.securityContext}{"\n"}{end}'
kubectl get pod -n "$NAMESPACE" "$POD" \
-o jsonpath='{range .spec.volumes[*]}{.name}{"\t"}{.hostPath.path}{"\t"}{.persistentVolumeClaim.claimName}{"\n"}{end}'
kubectl auth can-i --as="system:serviceaccount:$NAMESPACE:$SERVICE_ACCOUNT" --list -n "$NAMESPACE"
kubectl get namespace "$NAMESPACE" --show-labels
--list çıktısı geniş olabilir ve kaynak adlarını açığa çıkarabilir; bu nedenle halka açık bir rapora yapıştırmak yerine değerlendirme kanıtlarıyla birlikte saklayın. Bu çıktı izin verilen API işlemlerini tanımlar; bir kimlik bilgisinin mount edildiğini, erişilebilir olduğunu veya başarıyla kullanıldığını tek başına kanıtlamaz.
Tek başına çalışan bir Docker iş yükü için hem konteyner yapılandırmasını hem de daemon bağlamını kaydedin:
docker version
docker info --format '{{json .SecurityOptions}}'
docker inspect "$CONTAINER" > container-inspect.json
docker inspect --format '{{json .HostConfig}}' "$CONTAINER" > host-config.json
docker inspect --format '{{json .Mounts}}' "$CONTAINER" > mounts.json
docker top "$CONTAINER" -eo pid,user,group,comm,args
Bu kayıtlar dahili yolları, imaj referanslarını ve ortam metaverilerini içerebilir. Bunları hassas kanıt olarak ele alın. docker inspect erişilebilir kılıyor diye ortam değişkeni (env) değerlerini gelişigüzel toplamayın veya yayınlamayın.
Yapılandırma temizliğinden önce yetki triyajı yapın
Konteyner denetimleri ilk saatlerini genellikle imaj boyutu, paket sayısı veya YAML’ın runAsNonRoot içerip içermediği üzerinde harcar. Bunun yerine doğrudan host’u veya kontrol düzlemini değiştirebilecek saldırı yollarıyla başlayın.
1. Runtime ve orkestrasyon soketleri
Mount edilmiş bir Docker, containerd, CRI veya yönetim soketi sıradan bir uygulama verisi değildir. Bir kontrol düzlemi arayüzüdür. Docker güvenlik dokümantasyonu, daemon erişimini hassas bir sınır olarak kabul eder; çünkü daemon, kasıtlı olarak rootless modda çalıştırılmadığı sürece genellikle host düzeyinde yetki taşır.
Doğru soru “soket salt okunur mu?” değildir. Unix soket erişimi, depolanan verilere dosya sistemi yazımları yerine API istekleri üzerinden ifade edilir. Servisin hangi işlemleri kabul ettiğini ve uygulamanın bunlardan herhangi birine ihtiyacı olup olmadığını belirleyin. Normal bir web uygulaması için doğru cevap genellikle soketin hiçbir şekilde mount edilmemesi gerektiğidir.
2. Host yolları, aygıtlar ve yayılma (propagation)
Bir bind mount, bir host yolu ile konteyner arasında doğrudan bir ilişki kurar. Docker, bind mount’ların varsayılan olarak yazılabilir olduğunu ve dolayısıyla host dosyalarını değiştirebileceğini belgeler. Non-root bir konteyner, host yolunun UID’si, GID’si, ACL’i, ek grubu veya aşırı izin veren izin modu tarafından hala yetkilendirilebilir.
Şunları envantere alın:
- Tam host kaynağı ve konteyner hedefi;
- Salt okunur (read-only) ve yazılabilir (writable) durum;
- Mount yayılması (mount propagation);
- Host mülkiyeti, grup, ACL ve SELinux/AppArmor bağlamı;
- Yolun soketler, çalıştırılabilir yapılandırmalar, kimlik bilgileri veya ayrıcalıklı bir host servisi tarafından tüketilen dosyalar içerip içermediği;
- Aynı amaca adlandırılmış bir volume, yansıtılmış bir dosya veya dar bir API ile hizmet edilip edilemeyeceği.
Salt okunur olmak anlamlı bir risk azaltımıdır, evrensel bir garanti değildir. Bir dağıtım anahtarını, runtime kimlik bilgisini, host süreç metaverisini veya ayrıcalıklı servis yapılandırmasını okumak farklı bir saldırı yolu için fazlasıyla yeterli olabilir.
3. Privileged mod ve Linux capability’leri
privileged: true kategorik bir istisnadır. Kubernetes, ayrıcalıklı konteynerlerin kernel kısıtlamalarını geçersiz kıldığını belgeler: Seccomp unconfined hale gelir ve AppArmor veya SELinux sınırlamaları atlatılır. Ayrıcalıklı bir iş yükünü aynı zamanda runAsNonRoot veya salt okunur kök dosya sistemi ayarladığı için asla sertleştirilmiş olarak tanımlamayın.
Sıradan uygulamalar için işe tüm capability’leri düşürerek (drop: ["ALL"]) başlayın ve yalnızca adlandırılmış, işlevsel olarak test edilmiş bir gereksinimi geri ekleyin. Bir capability adı alışılmadık diye zararsız değildir. Özellikle CAP_SYS_ADMIN, birbiriyle ilgisiz birçok yönetimsel operasyonu kapsar ve ayrıca Kubernetes’te allowPrivilegeEscalation bayrağının fiilen true kalmasını zorunlu kılar.
4. İş yükü kimliği (Workload identity)
Kubernetes her Pod’a bir service account atar ve hiçbir şey belirtilmediğinde namespace’in default service account’ını kullanır. Güncel kümeler normalde kısa ömürlü, dönen token’lar yansıtır. Kısa ömürlü olmak statikten daha iyidir; ancak düşük yetkili anlamına gelmez.
Kubernetes API’sini çağırmayan iş yükleri için automountServiceAccountToken: false ayarlayın ve hiçbir token’ın mevcut olmadığını doğrulayın. API’yi çağıran iş yükleri için, özel bir service account’ı pozitif iş yükü testini destekleyen en küçük isim alanlı (namespaced) role bağlayın. Harici iş yükü kimliğini ayrı olarak inceleyin: Kubernetes izni olmayan bir Pod, node’u, metaveri rotası veya kimlik federasyonu yapılandırması üzerinden hala bulut yetkisi alabilir.
5. Paylaşımlı kernel ve iş yükü yerleşimi
Bir node üzerindeki konteynerler, daha güçlü bir sandbox runtime başka bir sınır getirmedikçe aynı kernel’ı paylaşır. Seccomp, AppArmor, SELinux, kullanıcı ad alanları (user namespaces), capability azaltımı ve güncel çekirdekler maruziyeti farklı şekillerde azaltır; hiçbiri yüksek ve düşük güvenilirlikli iş yüklerini geriye dönük olarak iyi birer komşu yapmaz.
Kubernetes’in güvenlik kontrol listesi, farklı hassasiyetteki iş yüklerinin ayrılmasını ve hassas yerleşimler için sandbox runtime’ların değerlendirilmesini önerir. Halka açık bir ayrıştırıcı (parser), dosya dönüştürücü, tarayıcı worker’ı veya güvenilmeyen bir derleme işi; küme yönetimi veya yüksek yetkili kimlik bileşenleriyle aynı node’u gelişigüzel paylaşmamalıdır.
Minimum uygulama profili
Aşağıdaki güvenlik bağlamı (securityContext), geleneksel durum bilgisi olmayan (stateless) bir Linux uygulaması için savunulabilir bir başlangıç noktasıdır. Evrensel bir şablon değildir; önce digest ile sabitlenmiş bir imajla geliştirme ortamında test edilmelidir.
apiVersion: apps/v1
kind: Deployment
metadata:
name: example-api
spec:
replicas: 1
selector:
matchLabels:
app: example-api
template:
metadata:
labels:
app: example-api
spec:
automountServiceAccountToken: false
securityContext:
runAsNonRoot: true
runAsUser: 10001
runAsGroup: 10001
seccompProfile:
type: RuntimeDefault
containers:
- name: api
image: registry.example/example-api@sha256:<approved-digest>
ports:
- name: http
containerPort: 8080
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
readOnlyRootFilesystem: true
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
volumeMounts:
- name: tmp
mountPath: /tmp
readinessProbe:
httpGet:
path: /health/ready
port: http
volumes:
- name: tmp
emptyDir:
sizeLimit: 64Mi
Her alanın bağlı olduğu bir güvenlik iddiası vardır:
runAsNonRootve açık kimlikler, kazara root olarak çalıştırmayı ve kararsız imaj varsayılanlarını engeller;allowPrivilegeEscalation: false, kernel’ınno_new_privsdavranışını devreye alır;- tüm capability’lerin düşürülmesi, açıkça geri verilmedikçe ayrıcalık parçalarını temizler;
RuntimeDefault, runtime tarafından yönetilen sistem çağrısı filtresini korur;- salt okunur bir kök dosya sistemi, beyan edilmemiş yazma işlemlerinin başarısız olmasını sağlar;
- sınırlandırılmış bir
emptyDir, uygulamanın beyan edilen geçici yazma yolunu sağlar; - kaynak istekleri ve limitleri, bazı hizmet dışı bırakma (DoS) etkilerini kısıtlar;
- token automount’ın kapatılması, kullanılmayan bir kimlik bilgisi yolunu kaldırır;
- readiness probe, dağıtım sırasında pozitif bir kontrol sağlar.
Burada kasıtlı eksiklikler vardır. Bu örnek network policy, admission policy, AppArmor veya SELinux politikası, Pod Disruption Budget, imaj imza doğrulaması veya bulut iş yükü kimliği kısıtlamaları içermez. Bunlar komşu kontrol düzlemlerine aittir ve kendi test edilebilir iddialarını gerektirir.
Kontroller yalnızca birer etiket olarak görüldüğünde neden çöker?
Sabit ve uyumlu bir imaj kullanıcısı olmadan runAsNonRoot
Bir imaj sembolik bir kullanıcı tanımlayabilir, root mülkiyetindeki yollara bağımlı olabilir veya derlemeler arasında mülkiyeti değişen dizinlere yazabilir. Başlatmayı, yükseltmeleri, log rotasyonunu ve düzgün kapanmayı test etmeden non-root zorlamak, güvenlik çalışmasını bir erişilebilirlik kesintisine dönüştürür. Tersine, UID’yi örtük bırakmak aynı manifest’in farklı imajlarda farklı davranmasına yol açar.
Sayısal kimliği sabitleyin, gerekli mülkiyeti doğrudan imajın içine derleyin ve tüm yaşam döngüsünü kanıtlayın.
Başka yere yazma yetkisi mount edilmişken readOnlyRootFilesystem
Konteyner kökü değişmez olabilir ancak /data, /config, /cache veya bir host yolu yazılabilir kalabilir. Her mount’u çıktısını neyin tükettiğine göre sınıflandırın. Yalnızca yazılan bir dışa aktarma dizini, ayrıcalıklı bir host otomasyon servisi tarafından izlenen bir dizinle asla eşdeğer değildir.
Çalışma zamanı kanıtı olmadan Seccomp
Kubernetes ve Docker, pratik bir taban çizgisi olarak runtime varsayılan seccomp profilini kullanmayı önerir. Bir YAML alanı, node’un hedeflenen profili desteklediğinin veya uyguladığının kanıtı değildir. Runtime’ı, node yapılandırmasını, admission sonucunu ve güvenli olan durumlarda kontrollü engellenen bir işlemi kaydedin. Açıklanamayan bir uygulama hatasını düzeltmek için varsayılan profili devre dışı bırakmaktan kaçının; sistem çağrısını tanımlayın, uygulamanın buna ihtiyacı olup olmadığına karar verin ve en küçük değişiklikten sonra yeniden test edin.
Seccomp ayrıca izin verilen sistem çağrılarını otomatik olarak güvenli kılmaz. Kernel saldırı yüzeyini azaltır; ancak uygulama girdisini doğrulamaz veya paylaşımlı kernel güven kararını ortadan kaldırmaz.
Node kapsamı olmadan AppArmor veya SELinux
Profiller uygun her node’da mevcut olmalı ve zorunlu kılınmalıdır. Kubernetes, node’da AppArmor devre dışı bırakıldığında örtük bir runtime-default seçiminin hiçbir kısıtlama getirmeyebileceğini; oysa profili açıkça talep etmenin admission’ın başarısız olmasına yol açabileceğini belirtir. Bu başarısızlık tercih edilebilir: Sessiz politika yokluğunu görünür bir dağıtım kanıtına dönüştürür.
Zorunluluk ve sürüm sabitlemesi olmadan Pod Security etiketleri
Pod Security Admission warn, audit ve enforce modlarını destekler. Yalnızca uyaran (warn) bir namespace, iş yükünü kabul etmeye devam ederken temiz görünen bir denetim raporu üretebilir. Modu, politika düzeyini ve sabitlenmiş sürümü kaydedin. Geçiş sırasında warn ve audit kullanın ancak enforce modu bilinen kötü negatif kontrol manifest’ini reddedene kadar saldırı yolunu kapatılmış olarak raporlamayın.
Çalışma zamanı yetki incelemesi olmadan imaj taraması
Paket ve zafiyet taraması imajda neyin bulunduğunu ve bu konuda hangi kamuya açık bilgilerin var olduğunu yanıtlar. Canlı bir Pod’un mount’larını, service account’ını, namespace paylaşımını, admission sonucunu veya node yerleşimini envantere almaz. İmaj taramasını tek bir kanıt kaynağı olarak kullanın; iş yükü izolasyonu için nihai karar olarak asla kullanmayın.
Üç durumlu deney (Three-state experiment)
İmaj özetini, uygulama girdisini, node sınıfını ve beklenen iş yükü davranışını sabit tutun. Yalnızca kontrol durumunu değiştirin.
Durum A — Non-root taban çizgisi
Süreç UID 10001 olarak çalışır ancak laboratuvarda kasıtlı olarak seçilmiş bir yetki yolu açık bırakılır: Örneğin gereksiz bir service account token’ı, yazılabilir bir canary bind mount’u veya uygulamanın gerektirdiğinden daha geniş bir capability kümesi. Testi dramatik hale getirmek için gerçek bir runtime soketini veya canlı ortam host yolunu asla açığa çıkarmayın.
Pozitif uygulama kontrolünü ve seçilen yetki yolunun mevcut olduğunu gösteren güvenli gözlemi kaydedin. Bu erişilebilirliği tesis eder, etkiyi değil.
Durum B — Etiket odaklı sertleştirme
Seçilen yetki yolunu olduğu gibi bırakırken yaygın sertleştirme alanlarını ekleyin. Dağıtım artık bir politika tarayıcısını geçebilir. Aynı negatif kontrol hala başarılı oluyorsa, sonuç tam olarak dersi ortaya koyar: Birçok değerli kontrol, orijinal saldırı yoluyla bir arada var olabilir.
Durum C — Tehdit odaklı sertleştirme
Seçilen yetki yolunu kaldırın veya daraltın. Aynı pozitif iş yükü kontrolünü ve aynı negatif kontrolü yeniden çalıştırın. Durum, ancak servis sağlıklı kalmaya devam ettiğinde ve yetkisiz davranış beklenen nedenle başarısız olduğunda geçer.
Pozitif ve negatif kontroller
Kontrolleri değişiklikten önce seçin; böylece sonuç çıktıyı gördükten sonra uydurulamaz.
Pozitif kontroller
İş yüküne bağlı olarak şunları kaydedin:
- Readiness ve liveness probe davranışları;
- Kimliği doğrulanmış temsilci tek bir istek;
- Beyan edilen veri veya geçici birime gerekli yazma işlemi;
- Düzgün başlatma, sonlandırma ve yeniden başlatma;
- İzin listesindeki bir bağımlılığa beklenen giden bağlantı;
- Log iletimi veya metrik toplama gibi rutin bir operasyonel eylem.
Negatif kontroller
Her güvenlik iddiası için zararsız bir laboratuvar düzeneği kullanın:
- Kök dosya sistemi, beyan edilen yazılabilir birimlerin dışına yazmayı reddeder;
- Canary host yolu mevcut değildir veya salt okunur mount edilmiştir;
- Uygulama dosya sisteminde hiçbir Docker veya CRI soketi yoktur;
- Süreç, laboratuvar için tasarlanmış bir test aracı üzerinden yeni bir ayrıcalık kazanamaz;
- İş yükü Kubernetes API erişimini kullanmadığında service account token yolu mevcut değildir;
kubectl auth can-i, iş yükü sözleşmesi dışındaki adlandırılmış bir API eylemini reddeder;- Admission; host namespace’lerini, privileged modu veya izin verilmeyen bir capability’yi kullanan manifest kopyasını reddeder;
- Kaynak sınırları, node kararsızlığı yerine kontrollü bir konteyner düzeyi hata üretir.
Canary olarak asla /etc/shadow, gerçek bir yönetici anahtarı, canlı bir runtime soketi veya yıkıcı kaynak tüketimi kullanmayın. Amaç yeni bir olay yaratmadan politika davranışını kanıtlamaktır.
Yetkilendirilmiş test konteyneri içinde, düşük etkili kontroller kimlik bilgilerini yazdırmadan beyan edilen durumu doğrulayabilir:
id
grep -E '^(CapInh|CapPrm|CapEff|CapBnd|NoNewPrivs|Seccomp):' /proc/1/status
findmnt -o TARGET,SOURCE,FSTYPE,OPTIONS
test ! -S /var/run/docker.sock
test ! -S /run/containerd/containerd.sock
test ! -e /var/run/secrets/kubernetes.io/serviceaccount/token
Son üç komutu iş yükü sözleşmesine göre yorumlayın. Başarısız bir test ! -e otomatik bir güvenlik açığı değildir; yolun mevcut olduğu ve bir yetki incelemesi gerektirdiği anlamına gelir.
Admission kalıcı kontroldür
Tek bir manifest’i düzeltmek tek bir örneği kapatır. Admission politikası ise aynı yetki yolunun bir sonraki dağıtım, başka bir namespace veya acil durum Helm değeri üzerinden geri dönmesini engeller.
Kubernetes Pod Security Standards üç faydalı profil sunar: Privileged, Baseline ve Restricted. Restricted, uygulama iş yükleri için güçlü bir genel hedeftir; Baseline’ın üzerindeki istisnalar ise uygulamaya özgüdür ve açık mülkiyeti hak eder. Politikaları aşamalı olarak uygulayın:
- Mevcut ihlalleri ve sahiplerini envantere alın;
- Bir test namespace’inde sürüm sabitlenmiş
warnveauditmodlarını etkinleştirin; - İstisnaları onarın veya izole edin;
- Hedeflenen namespace’leri
enforcemoduna geçirin; - Bilinen kötü bir manifest gönderin ve reddedilmeyi negatif kontrol olarak saklayın;
- Admission olaylarını izleyin ve platform yükseltmelerinden sonra periyodik olarak yeniden test edin.
Admission her şeyi denetlemez. Pod Security Standards kasıtlı olarak tüm volume, imaj kaynağı, ağ, iş yükü kimliği veya işe özgü gereksinimleri çözmez. Adlandırabileceğiniz ve test edebileceğiniz boşluklar için dar kapsamlı politikalar ekleyin. Tek kanıtı politika motorunun onu yüklemiş olması olan hantal bir kural kümesinden kaçının.
Rootless mod host tarafı bir risk azaltımıdır, iş yükü kararı değildir
Docker rootless modu, hem daemon’ı hem de konteynerleri root ayrıcalıklı bir daemon olmadan bir kullanıcı ad alanı (user namespace) içinde çalıştırır. Bu, geleneksel bir rootful dağıtıma kıyasla daemon ve runtime saldırı yüzeyini önemli ölçüde azaltır. Geliştirici sistemleri ve uyumlu tek kullanıcılı servisler için özellikle değerlidir.
Rootless mod iş yükü sözleşmesinin tamamını yanıtlamaz. Süreç hala rootless kullanıcının sahip olduğu verilere erişebilir, mount edilmiş kimlik bilgilerini kullanabilir, rootless daemon soketini çağırabilir, ağa ulaşabilir veya mevcut kaynakları tüketebilir. Bunu host tarafı bir kontrol olarak kaydedin ve aynı mount, kimlik, ağ ve negatif kontrol incelemesini sürdürün.
Aynı mantık orkestrasyonlu ortamlardaki kullanıcı ad alanları için de geçerlidir. Konteyner root’unu ayrıcalıksız bir host ID’sine eşlemek sonuçları azaltabilir; ancak kasıtlı olarak açığa çıkarılmış bir kontrol soketi veya aşırı yetkilendirilmiş API kimliği bu avantajı tamamen ortadan kaldırabilir.
Kanıt matrisi (Evidence matrix)
| İddia | Gerekli gözlem | Pozitif kontrol | Negatif kontrol | Sonuç sınırı |
|---|---|---|---|---|
| Uygulama non-root çalışıyor | Canlı PID 1 UID/GID ve kabul edilen securityContext | Servis başlangıcı ve normal istek başarılı | Bilinen root-only başlatma reddedilir | Host izolasyonunu kanıtlamaz |
| Ayrıcalık artırılamaz | Capability kümeleri, NoNewPrivs, privileged bayrağı | Gerekli operasyon hala başarılı | Laboratuvar yetki yükseltme aracı reddedilir | Kernel zafiyetlerini kapsamaz |
| Sistem çağrısı yüzeyi kısıtlıdır | Uygulanan seccomp durumu ve runtime profili | Tam uygulama yaşam döngüsü başarılı | Seçilen izin verilmeyen sistem çağrısı beklenen reddi döndürür | İzin verilen sistem çağrılarını güvenli kılmaz |
| Host dosya sistemi yazılabilir değildir | Canlı mount tablosu, kaynak, seçenekler, host mülkiyeti | Beyan edilen veri yolu yazılabilir kalır | Canary host yolu mevcut değildir veya salt okunurdur | Mount edilen verinin hassas olmadığını kanıtlamaz |
| Runtime kontrol düzlemine erişilemez | Soket envanteri ve canlı mount’lar | İş yükünün hiçbir runtime API’sine ihtiyacı yoktur | Bilinen soket yolları mevcut değildir | Uzaktan yönetim uç noktalarını kapsamaz |
| Kubernetes kimlik bilgisi yok veya dar kapsamlı | Token mount durumu ve RBAC incelemesi | Varsa gerekli API eylemi başarılı | Sözleşme dışı adlandırılmış eylem reddedilir | Bulut iş yükü kimliğini kapsamaz |
| Admission regresyonu engeller | Namespace etiketleri, politika sürümü, ret olayı | Onaylanan manifest kabul edilir | Bilinen kötü manifest reddedilir | Halihazırda çalışan Pod’ların düzeltildiğini kanıtlamaz |
| Kaynak etkisi sınırlandırılmıştır | İstekler, limitler, PID/depolama politikası, node telemetrisi | Beklenen tepe iş yükü başarılı | Kontrollü laboratuvar baskısı konteyner sınırında kalır | Node kapasite planlamasını kanıtlamaz |
| Yerleşim güvenle eşleşiyor | RuntimeClass, node, taints, affinity, komşu sınıflandırması | Zamanlama ve kurtarma başarılı | Yüksek riskli iş yükü korumalı havuza zamanlanamaz | Runtime’da kaçış açığı olmadığını kanıtlamaz |
Her seferinde tek bir sınırı devreye alın
Konteyner sertleştirme değişiklikleri bariz olmayan yerlerde kırılabilir: Init container’lar, sidecar’lar, debug container’ları, volume mülkiyeti, paket önbellekleri, sertifika yenileme, log iletimi, düzgün kapanma veya otomatik ölçeklendirme. Açık bir geri alma nesnesiyle aşamalı bir dağıtım kullanın.
Her değişiklik için:
- Orijinal canlı manifest’i, imaj özetini, politika çıktısını ve iş yükü sonucunu koruyun;
- Tek bir yetki düzlemini veya birbiriyle tutarlı bir alan grubunu değiştirin;
- Eşdeğer bir test namespace’ine veya canary iş yüküne dağıtın;
- Önceden tanımlanmış pozitif ve negatif kontrolleri çalıştırın;
- En az bir yeniden başlatma ve yeniden zamanlama gözlemleyin;
- Laboratuvarda bir kez geri alma işlemi yapın ve kurtarmayı doğrulayın;
- Ancak ret ve iş yükü kanıtları uzlaştığında canlıya alın.
Tek bir istisnayı barındırmak için küme çapındaki bir kontrolü gevşetmeyin. İstisnayı izole edin, koruduğu yetkiyi belgeleyin, kimin dağıtabileceğini kısıtlayın ve güven düzeyine uyan node’lara yerleştirin.
İncelemeden sağ çıkan raporlama dili
Sınırlandırılmış ifadeleri tercih edin:
- “Canlı uygulama süreci UID 10001 olarak çalıştı ve admission politikası UID 0’ı reddetti.”
- “Kabul edilen Pod tanımlamasında hiçbir runtime soketi veya hostPath birimi mevcut değildi.”
- “Service account kendi isim alanındaki ConfigMap’leri okuyabildi ve Secret okumaları reddedildi.”
- “Tüm capability’ler düşürüldükten sonra pozitif istek ve yeniden başlatma testleri başarıyla geçti.”
- “Sonuç, bilinmeyen kernel kaçış zafiyetlerini veya bulut kimlik katmanını değerlendirmez.”
Şunlardan kaçının:
- “Konteyner tamamen güvenlidir.”
- “Non-root konteynerden kaçışı engeller.”
- “Restricted politika Pod’un node’u etkileyemeyeceği anlamına gelir.”
- “Tarayıcı geçti, dolayısıyla iş yükü en az yetkiye sahiptir.”
- “Kısa ömürlü bir token zararsızdır.”
Konteyner güvenliği en çok her katmanın yalnızca kendi iddiasını ortaya koymasına izin verildiğinde güçlenir. İmaj kaynağı artefakta olan güveni destekler. Non-root bir UID süreç ayrıcalığını azaltır. Seccomp ve LSM profilleri kernel etkileşimini kısıtlar. Mount politikası dışa açılan dosyaları ve soketleri yönetir. RBAC Kubernetes API’sini yönetir. Yerleşim hangi hataların bir çekirdeği paylaştığını yönetir. Hiçbiri diğerlerinden ödünç kesinlik almamalıdır.
Nihai yeniden test
Nihai inceleme, değişikliği öneren sertleştirme aracı olmadan da anlaşılabilir olmalıdır. İş yükü sözleşmesiyle başlayın, orijinal yetki yolunu gösterin, kabul edilen ve canlı durumu kaydedin, uygulamanın hala çalıştığını gösterin ve aynı zararsız negatif kontrolün artık başarısız olduğunu kanıtlayın.
Bu kanıt dar ama kalıcı bir sonucu destekler:
Konteyner işlevsel kalmaya devam etmiş ve test edilen yapılandırma altında uygulama ihlalinden host veya kontrol düzlemi yetkisine giden bu spesifik yol tamamen kaldırılmıştır.
Bu, iş yükünü yalnızca “non-root” olarak adlandırmaktan çok daha değerlidir. Sınırı, davranışı, kanıtı ve testin dışında kalanları açıkça tanımlar.
MITRE ATT&CK eşlemesi
| Taktik | Teknik ID | Teknik adı | Savunucu doğrulama sinyali |
|---|---|---|---|
| Privilege Escalation | T1611 | Escape to Host | Host path mount denetimi + varsayılan seccomp profil yaptırımı |
| Defense Evasion | T1610 | Deploy Container | Admission controller webhook logları (Kyverno / Gatekeeper) |
| Discovery | T1613 | Container and Resource Discovery | automountServiceAccountToken engelleme + namespace izolasyonu |
| Execution | T1609 | Container Administration Command | API denetim loglarıyla kubectl exec / Docker soket erişimi denetimi |
Savunucu eylem kontrol listesi
Konteyner iş yüklerini sertleştirirken bu uygulanabilir kontrol listesinden yararlanın:
- Tüm capability’leri düşürün: Konteyner manifest’lerinde
securityContext.capabilities.drop: ["ALL"]ayarlayın. - Kök dosya sistemini salt okunur kılın:
/tmpiçin ayrılmış geçiciemptyDirmount’ları ilesecurityContext.readOnlyRootFilesystem: trueayarlayın. - Ambient API token’larını kapatın: İş yükü açıkça Kubernetes API’sini çağırmadıkça
automountServiceAccountToken: falseayarlayın. - Yetki yükseltmeyi reddedin: Pod içindeki tüm konteynerlerde
allowPrivilegeEscalation: falseyapılandırıldığından emin olun. - Negatif kontrolleri doğrulayın: Non-root sürecin host mount’larına yazamadığını veya kısıtlı sistem çağrılarını çalıştıramadığını teyit edin.
Bu teknik not ne kadar güncel?
En son kaynak incelemesi, içerik güncellemesi veya yayın tarihi gösterilmektedir.
Yazar teknik incelemeyi tamamladı. Bu etiket tek başına laboratuvar yeniden üretimi iddiası taşımaz.
- kubernetes.io · security-checklist ↗
- kubernetes.io · pod-security-standards ↗
- kubernetes.io · security-context ↗
- kubernetes.io · linux-kernel…ty-constraints ↗
- kubernetes.io · rbac-good-practices ↗
- kubernetes.io · service-accounts ↗
- docs.docker.com · security ↗
- docs.docker.com · rootless ↗
- docs.docker.com · seccomp ↗
- docs.docker.com · bind-mounts ↗
- csrc.nist.gov · final ↗
