K8S — AdmissionWebhook

Merhabalar,

Bugün Kubernetes üzerinde api-server erişimleri nasıl özelleştirebileceğimizi ve Kubernetes’in nasıl genişletilebilir bir yapı sunduğunu (SOLID — OpenClosed) inceleceğiz.

Birbiri arasinda konusan butun yapilar eger ki belirli bir contract tanimlamalarina uyarsa platform ekiplerine extend edilebilir yapilar sunabiliyor.

Mesela Kubernetes uzerinde CRD’leri izleyip Kafka kurabileceginiz controllerdan tutunda giderek tum istekleri APISERVER isteklerini degistirebileceginiz ya da istediginiz policylere gore izin verebileceginiz yapilari programlayabiliyorsunuz.

Kubernetes Alemi

Bugun de aslinda tam bunun nasil oldugu uzerinden konusacagiz.

İlk olarak bir isteğin Kubernetes üzerinde nasıl gerçekleştiğini bir inceleyelim.Birçok yerde görebileceğimiz ve pek çoğumuzun aşina olduğu aşağıdaki görsele bir bakalım;

Bir kubectl get po nun hikayesi — Bölüm 1

İlk olarak hali hazırda uygun sertifikada cluster kurulurken atanmış API server arkadaşın dinlediği yere kubeconfigteki certifika ile gidiyoruz orada handshake yapıldıktan sonra SA tokenlerimize veya client sertifikamızdaki CN değerine bakılarak kim olduğumuza ve RBAC üzerindeki haklarımız kontrol ediliyor.

İşte bizim artık clusterda bir şeyler yapmaya başladığımız hikaye burada başlıyor, mutating admission controller herhangi bir objede yaratırken veya güncellerken devreye giriyor mesela çok ortada bir örnek olarak belirli sertifikaların volume olarak inject edilmesi ya da sidecar eklenmesi gibi işlemler bu mutating admission controller objelerinin /mutate endpointlerine gidiyor ve bizim objelerimiz güncelleniyor.

4.adım ise API Server’ın muhtemelen kabul edilebilecek valid bir JSON objesi olup olmadığını kontrol ediyor.Çünkü içerideki bütün komponentler iletişiminde JSON’ca konuşacağız diyorlar.

5.Adım

Bu kısım bizim yazımızın demo kısmını ve hands-on kısmını meydana getiriyor.

Burada hem MutatingAdmissionWebhook hem de ValidationAdmissionWebhook, ETCD üzerinde gidecek isteklerde araya girmeyi amaçlıyoruz.

Bir obje artık clusterda bir aksiyona sebep olmadan önce acaba istediğim gibi tanımlanmış mı buna izin vermeden önce bir check etmek istiyorsak devreye ValidationAdmissionWebhook devreye giriyor.

What is the Problem?

Burada yaşadığım konu son dönemlerde dockerhub açık imaj repolarına belirli limitler getiriyor olması hem de internal private repolarım haricinde bir yerden imaj çekilmesini engellemeyi amaçlayan bir validator yazmak istedim.

Gelin şimdi Kubernetes üzerinde isteklerin arasına nasıl giriyoruz ona bir göz atalım.

İlk olarak ValidationAdmissionWebhook objesini tanımlamamız gerekiyor.

Asagidaki obje tanimina bakacak olursak Kubernetes Api Server’a diyoruz ki pod ve deployment objeleri olusturmadan once img-validation namespace uzerindeki the-validator servisinin /validate endpointine bir ugra diyoruz

apiVersion: admissionregistration.k8s.io/v1beta1
kind: ValidatingWebhookConfiguration
metadata:
name: manas
webhooks:
- name: the-validator.img-validation.svc
clientConfig:
service:
name: the-validator
namespace: img-validation
path: "/validate"
caBundle: "${CA_BUNDLE}"
rules:
- operations: ["CREATE"]
apiGroups: [""]
apiVersions: ["v1"]
resources: ["pods"]
- operations: ["CREATE"]
apiGroups: [""]
apiVersions: ["apps/v1"]
resources: ["deployment"]

Burada componentler arasındaki iletişim yapılırken K8S tls’i bildigi bir certificate üzerinden validate etmek ister bunu da caBundle adresine base64 encode şekilde verdikten sonra her istek /validate endpointine sorularak yoluna gidecek.

Burada tabikide önce servislerini bir geliştirmenin ardından yolumuza devam etmemiz gerekiyor burada asağıdaki koda bakabiliriz.

Ilk olarak flask uzerinde çok basit bir şekilde gelen requestin payloadını parse edip, yine controller içinde izin verilen repo adresleriyle imajimin nereden geldiğini kontrol ediyorum. Bu kısım sizin ne yapmak istediginize bağlı o nedenle şimdilik geçiyorum.

Asıl önemli olan kısım KubernetesAPI’ye döneceğimiz olan response objesi.

Burada API Server üç önemli cevap bekliyor; istek durumuna gore True/False dönecek allowed key ardından isteğin uid numarası bunun önemi her istege bir uid tanimlanmasi gerekiyor eger bunlar eslesmez ise K8S neyi validate ettgimizi bilemez.

Son olarak status mesajı oraya da Isteginiz engellendi, birDost tadinda bir seyler yazabilirsiniz.

Burada önemli kısım ise servisin içeride kendi servis adının /CN olarak tanımladığım bir sertifika üzerinden yayin yapiyor olması yani yukarida tanimladigim CaBundle daki sertifikasının private key ile `the-validator.img-validation.svc` için bir sertifika daha generate edip servisimi deploy ediyorum.

Img-Validator servisimin Flask tarafinda asagidaki gibi configure ettim.

context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
context.load_cert_chain("/etc/certs/cert.pem", "/etc/certs/key.pem")
if __name__ == "__main__":
app.run(debug=True, host="0.0.0.0", ssl_context=context)

Bütün bu yapıyı kod bloğundaki akiş da dahil olacak şekilde deploy ettim ve istek yapiyorum.

kubectl run app-new --generator=run-pod/v1 --image my-repo/re1dis

Bana dönen çıktı şu şekilde :

Error from server: admission webhook "the-validator.img-validation.svc" denied the request: Image repository is not allowed

Benim ValidationAdmissionWebhook controllerim isteğin arasına girdi ve my-repo`nun ona izin verdiğim repolar arasında olduğunu görerek Kubernetes tarafına bu isteğin dönmesini engelledi.

Uygulama’nın nasıl çaliştığını anlamak içinde asağıdaki linklere bakabilirsiniz.

Son olarak;

Kubernetes yapı olarak kendi içerisinde extend edilebilen bir yapı olduğu için bir çok ürün bu özellik uzerinden ilerliyor.

Mesela istio side-car injector uzerinden objeleri mutate ediyor ve yeni bir container ekliyor.

ValidationAdmissionController kullanarak kendi içinizde bir logic belirleyip bunu cluster icerisine entegre edebilir hatta bir adim daha goturup JSON tanımlamalarını parse eden bir kod blogu ile kendi PolicyAgent’larinizi yazabilirsiniz.

Umarım açıklayıcı beğendiğiniz bir yazım olmuştur. İyi çalışmalar diliyorum

İlgili deployment objelerini kod bloğunu ve sertifika oluşturma joblarını aşağıdaki github linkinde bulabilirsiniz.

Kaynakça

Rahmetle ve sevgiyle .. Selim Sesler

PythonRubyLinux(❤)