Container image'ları için Kubernetes admission control: kapıda ne kontrol edilmeli.
Validating admission webhook, kim uygularsa uygulasın, her pod'un çalışmadan önce geçtiği tek noktadır. Bu kılavuz, o kapının bir container image'dan neler istemesi gerektiğini, henüz cevabı yokken ne yapması gerektiğini ve gerçekten ayakta olup olmadığını nasıl kontrol edeceğinizi anlatıyor.
Neden olay sonrası değil kapıda
Runtime güvenliği, bir container'ın gece üçte tuhaf davranmaya başladığını size söyler. Admission control ise o container'ın hiç başlayıp başlamayacağına karar verir. İkisi farklı soruları cevaplar ve ikincisini doğru kurmak daha ucuzdur: admission sırasında API server zaten bir evet ya da hayır bekliyordur, image referansı pod spec'inde hazırdır ve bir ret, bir olaya değil yalnızca başarısız bir deploy'a mal olur.
Kontrol API server'ın admission zincirinde olduğu için apply'ı kimin yaptığı fark etmez: kubectl, Argo CD, Flux, bir Job ya da kendi CRD'sini reconcile eden bir operator. Kısa bir bypass namespace listesi dışındaki her pod CREATE ve UPDATE isteği aynı webhook'tan geçer. Policy'ye takılan bir GitOps sync, hata olarak ret mesajını gösterir; bu mesajı okumak istediğiniz yer de tam olarak orasıdır.
Beş kontrol
Bir image kapısının pod spec'inden ve tarama önbelleğinden cevaplayabileceği sorular, açmaya değer oldukları sırayla aşağıda. İlk üçü için hiç tarayıcı gerekmez.
- Tag hijyeni. Değişebilen bir tag, kimse deploy etmeden çalışan şeyi değiştirebilir.
:latest'i reddetmek yazılabilecek en küçük kuraldır ve ilk gün en çok işe yarayacak olanıdır. - Registry izin listesi. Sizin registry'lerinizden gelmeyen her şey doğrudan reddedilir. Liste boşsa her registry'ye izin verilir; listede kayıt varsa diğer bütün registry'ler reddedilir.
- Digest sabitleme. Üç seviyesi var: tag'e izin ver, digest'i tercih et, digest'i zorunlu kıl. Digest zorunluluğu, neyin çalıştığı konusunda en güçlü iddiayı verir ama geçişi de en zahmetli olanıdır; bu yüzden çoğu ortam "tercih et" ile başlar ve namespace bazında sıkılaştırır.
- Zafiyet eşikleri. Herhangi bir critical bulguda, herhangi bir high bulguda ya da medium ve low bulgular belli bir tavanı aştığında reddedin. Arkasında tarayıcı gerektiren kontrol budur ve bir sonraki bölüm burada önem kazanır.
- İmzalar ve attestation'lar. Doğrulanmış bir cosign imzası, bir SBOM attestation'ı ve bir zafiyet tarama attestation'ı isteyin; belirli bir yaştan eski attestation'ları reddedin. Bunları build pipeline'ınız gerçekten ürettiğinde açın; öncesinde her şeyi reddederler.
Aynı inceleme image'ın yanında workload'a da bakabilir: privileged container'lar, hostPath mount'ları ve benzerleri sıkılaştırma bulgularıdır; bunların reddedilmesine, uyarı vermesine ya da geçmesine policy karar verir.
Kimsenin taramadığı bir image
Tarama dakikalar sürer; API server ise saniyeler bekler. Bu yüzden isteğin içinde tarama yapan bir kapı er ya da geç zaman aşımına düşer ve deployment'ları da beraberinde götürür. İşleyen tasarım ikisini ayırır: tarayıcı arka planda bir önbelleği doldurur, admission bu önbelleği okur. Böylece yavaş bir registry deployment'ı yavaşlatamaz.
Kurulumdan önce vermeniz gereken karar şu: daha önce hiç görülmemiş bir digest'in henüz tarama sonucu yoktur. Bunun iki dürüst cevabı var ve Attestkeep'te bunlardan birini admissionTiming policy alanı seçer:
- İzin ver ve tara. Bilinmeyen image'ı kabul edin, taramayı hemen kuyruğa alın ve o digest'in sonraki her admission'ında tamamlanan rapora göre karar verin. Benimsemesi kolaydır ve varsayılan budur: ilk gün deploy ettiğiniz hiçbir şey yeni olduğu için reddedilmez.
- Taranana kadar reddet. Bilinmeyen image'ı "a scan has been queued — retry shortly" mesajıyla reddedin ve rapor oluşunca kabul edin. Yeni bir digest'in her deploy'u bir tekrar denemeye mal olur. Bunu, bu sıkılığı hak eden namespace'ler için seçin.
Her iki durumda da yeni bir digest'in ilk taraması arka planda çalışır. Yeni kurulmuş bir cluster'da bu birkaç dakika sürebilir, çünkü tarayıcı image'ı çeker ve zafiyet veritabanı henüz soğuktur. Tarama bitene kadar image unknown sayılır, asla compliant sayılmaz. İki ayar da Community dahil her Attestkeep sürümünde var.
Fail open mı, fail closed mı
Güvenlik duruşuyla ilgili diğer karar image'la değil, kapının kendisiyle ilgilidir: webhook'unuz cevap vermediğinde API server ne yapmalı? Kubernetes buna webhook'un failurePolicy ayarı der ve iki değerden hiçbiri her durumda doğru değildir.
- Ignore (fail open), webhook kesintisi sırasında cluster'a deploy yapılabilmesini sağlar, ama bir boşluk bırakır: o süre boyunca hiçbir şey incelenmemiştir. Güvenilmeye değer bir kontrol, sessizce temiz bir çeyrek göstermek yerine bu süreyi kayda geçirir.
- Fail (fail closed), hiç boşluk olmadığı kanıtlanabilen bir kontrol sağlar; bunun bedeli, webhook kesintisinin deployment'ları durdurmasıdır. Bu gerçek bir maliyettir ve yeterli replica sayısı ve izlemeyle desteklenerek bilinçli olarak seçilmelidir.
Boşluk sırasında içeri giren her şey, boşluk kapandıktan sonra da çalışmaya devam eder. Bunları periyodik bir reconciliation taraması bulur: çalışan her container'ın gözlenen image digest'i admission kayıt defteriyle karşılaştırılır ve arkasında karar olmayanlar raporlanır ya da durdurulup yeniden admission'dan geçirilir. Attestkeep varsayılan olarak Ignore ile gelir, her kesinti süresini kanıt belgesi için kaydeder ve bu taramayı saatte bir çalıştırır.
Ayakta olduğunu gösterin
Sessizce çalışmayan bir admission webhook, kurallara uyan bir cluster'la tıpatıp aynı görünür. Bu yüzden herhangi bir kapıyı kurduktan sonra ve her upgrade'den sonra ilk iş, bir şeyi reddettiğini kendi gözünüzle görmektir. Attestkeep'in varsayılan policy'sinde en küçük ihlal :latest tag'idir:
kubectl run enforcement-check --image=nginx:latest
Error from server: admission webhook "image-security.attestkeep.com" denied the request:
image security policy: nginx:latest: the :latest tag is blocked by policy
kubectl get pod enforcement-check
Error from server (NotFound): pods "enforcement-check" not foundBurada iki şey dikkat çekici. Hata Error from server olarak gelir: ret, uyuyor olabilecek bir agent'ta değil, API server'ın admission zincirinin içinde gerçekleşmiştir. Ayrıca sebep kuralın adını verir; bunu CI log'unda okuyan geliştirici neyi düzelteceğini bilir. Pod yine de oluştuysa olağan şüphelileri sırayla kontrol edin: bypass edilmiş bir namespace, audit modunda bir policy, fail open çalışan bir webhook ya da hiç aktive edilmemiş bir kurulum.
Kaydı tutun
Kayıt bırakmayan bir ret, kanıtı olmayan bir enforcement'tır. Retlerin yanında izinleri de kaydedin; yalnızca retleri listeleyen bir kanıt, kontrolün çalıştığını gösteremez. Her kararı onu veren policy'nin hash'iyle birlikte kaydedin; böylece "bu kabul edildiğinde hangi kural geçerliydi" sorusu git geçmişinde arkeoloji değil, basit bir sorgu olur.
Ücretsiz araçların vermediği ve denetçinin gerçekten okuduğu kısım budur. Attestkeep her kararın içerik hash'ini yazma anında alır, kayıt defterini imzalı ve zincirlenmiş checkpoint'lerle mühürler ve bir dönemin kararlarını, seçtiğiniz kontrol setine eşlenmiş imzalı bir kanıt paketine dönüştürür. Bu kayıt defterinin değişikliği nasıl görünür kıldığı ve garantilerinin tam olarak nerede bittiği evidence trust model sayfasında (İngilizce) anlatılıyor.
Burada adı geçen her ayar dokümantasyonda var: alan referansı ve hemen uygulanabilir örnekler için Policies, iki duruş ayarı için Admission, runbook'un tamamı için Prove it is enforcing. Community sürümü ücretsizdir ve bu sayfadaki bütün kontrolleri içerir.