NSA Kubernetes Hardening Notları - Tüm Bölümler

August 28, 2021

Merhaba,

NSA, 3 gün önce Kubernetes’e yönelik bir Hardening kitapçığı yayınladı. Genel olarak kendi alanımla ilgili uygulama ve yapıların güvenlik sıkılaştırmalarına yönelik özel bir ilgim olduğu için bu kaynaktaki bazı noktalara değinmek istedim.

İlk olarak şunu belirtmek gerekiyor; bu Kubernetes’e ait ilk hardening kaynağı değil, birkaç sene önce CIS de bir benchmark yayınlamıştı, tabii o kaynak teorik bilgiden çok parametrelerin kullanımına ve sistem tarafındaki konfigürasyona değiniyor ama genel olarak ikisinin de aynı amaca hizmet ettiğini söyleyebiliriz.

Hatta yaklaşık 4 sene önce CIS’ın bu kaynağını baz alarak hazırladığım bir script de vardı, maalesef yarıda bırakmıştım, yine de merak edenler için linki bırakayım:

Zephyrus

Bu yazı veya yazı dizisinde Kubernetes’in mimarisine veya giriş seviyesindeki bilgilere derinlemesine değinmeyeceğim, niyetim doğrudan hardening tarafına odaklanmak ve bunu yaparken NSA’in yayınladığı kaynağı kendimce yorumlayıp örneklerle aktarmaya çalışacağım.

İlk olarak çok kısa da olsa mimariden bahsedelim.

Bir Kubernetes cluster’ı, master ve worker node’lardan oluşmaktadır, bu node’lar fiziksel veya sanal makineler olabilir. Pod dediğimiz yapı(lar), worker node’lar içerisinde bulunur. Her bir Pod da uygulamaların çalıştığı container’ları host eder.

Cluster’ın beyni olarak nitelendirebileceğimiz Control Plane, şu yapıları içerir:

  • Controller manager - Varsayılan port numarası 10252
  • Cloud controller manager - Varsayılan port numarası 10258
  • API Server - Varsayılan port numaraları 6443 veya 8080
  • etcd - Varsayılan port numaraları 2379 ve 2380
  • kube-scheduler - Varsayılan port numarası 10251

Worker node’lar ise şu servislere sahiptir:

  • Kubelet
  • kube-proxy

k

Cluster’lar fiziksel veya sanal makinelerde oluşturulabileceği gibi, aynı zamanda Cloud sağlayıcıları üzerinde de host edilebilir ki zaten siber güvenlik perspektifinden baktığımızda saldırganların en çok hedef aldığı cluster yapıları genellikle bir cloud sağlayıcısında (CSP) bulunmaktadır.

Bu noktada elbette söz konusu CSP’nin kendisine ait sıkılaştırma işlemleri ayrı bir başlık konusudur.

Docker ve Kubernetes ortamlarına yönelik saldırılar, özellikle son iki senede büyük bir artış gösterdi. Burada sadece container uygulamalarına yönelik exploitlerden bahsedemiyoruz çünkü yanlış yapılandırma, sağlayıcı tarafında ele geçirilen bir birim üzerinden yetki yükseltme gibi senaryolar da azımsanamayacak kadar fazla.

Söz konusu Supply Chain Attack olduğunda, atak vektörleri ve hedef yapıların değişkenliğinden dolayı güvenliği tek bir noktaya bağlı olarak düşünmemek gerekmektedir. Mesela container düzeyindeki güvenlik açıkları, birden çok sebeple oluşabilir; kullanılan bir outdated paket & uygulama & kütüphane dosyası, eksik firewall yapılandırması, hatalı konfigürasyon vs. saldırganın en küçük birimden başlayıp Cluster seviyesine kadar gelebileceği bir zafiyet oluşturabilir.

Altyapı tarafında ise kümeyi biraz daha geniş tutabiliriz çünkü bu aşamada kullanılan ortamın fiziksel, sanal, hibrit veya cloud üzerinde yer almasına yönelik güvenlik önlemleri de değişkenlik gösterecektir. Örnek olarak, saldırganın herhangi bir yolla bir Linux sunucusunda yetki sağlaması, aynı şekilde cluster güvenliğini de tehlikeye atacaktır. Bu yüzden Kubernetes güvenliği tek aşamalı olarak düşünülmemeli, mimariye bir bütün olarak bakılmalıdır. Elbette burada sadece donanımsal veya yazılımsal bir güvenlik problemi olacak diye bir şey de yok, sosyal mühendislik ve ‘insan’ faktörü oldukça her an tetikte olmak zaruridir.

Pod Güvenliği

Pod’u Kubernetes mimarisindeki en küçük birim olarak özetleyebiliriz. Bir pod birden çok container içerebileceği gibi bu container’lar da birden fazla uygulamaya / mikro servise sahip olabilir. Bir saldırgan gözünden baktığımızda herhangi bir şekilde bir container’ın exploit edilmesi, mimariye göre yatay ve(ya) dikey olarak yetkilendirmeye yol açabilir, bu da gerekli izolasyonun sağlanmadığı bir senaryoda bir Pod’un ele geçirilmesinin aslında Cluster’ın bütününe etki edecek bir problem oluşturabileceğini göstermektedir.

Non-root container kavramı

Docker üzerinden konuşacak olursak, varsayılan olarak imajlar root kullanıcısına yönelik hazırlanmış olabilir. Bir Dockerfile içerisinde USER direktifiyle kullanıcı tanımladığımızda root yerine o kullanıcı üzerinden container oluşturulur, bu da en basit haliyle yetkinin kısıtlanması anlamına gelmektedir.

Bir busybox imajını herhangi bir ayar yapmadan doğrudan çalıştıralım ve kontrol sağlayalım:

1

Şimdi de bir Dockerfile oluşturarak yine aynı busybox imajını kullanalım fakat bu sefer ‘non-root’ olarak ilerleyelim.

FROM busybox
RUN adduser --uid 700 --shell bin/sh --skel /dev/null deniz --disabled-password
USER deniz
CMD ps aux

2

Bu işlemin Kubernetes tarafındaki karşılığı ise bir SecurityContext oluşturmaktır. runAsUser parametresiyle tanımlanan kullanıcı seçilerek, bir Pod içerisinde çalıştırılacak container root yetkisi olmadan kullanılabilir.

Görüldüğü gibi Dockerfile ile bu işlemi yapmak oldukça basit fakat bunu takip eden problem şu: her seferinde kendi imajımızı mı oluşturacağız veya kullandığımız imajı düzenlemek / kontrol etmek mi zorundayız? Hayır. Bu noktada birazdan Kubernetes’e dönüp Pod Security Policy konusuna bir göz atacağız ama öncelikle rootless kavramına da değinelim.

Rootless Container Engine kavramı

Özellikle ilk kurulumlarda ve sonrasında Docker’ın genellikle normal bir kullanıcı yerine root kullanıcısıyla çalıştırıldığını söyleyebiliriz.

Docker daemon’ı varsayılan olarak 2375 port’unu kullanmaktadır. Atak vektörü olarak düşünmemiz gereken noktalar ise; çeşitli platformlarda veya bir script / tool yardımıyla yapılacak tarama işlemlerinde, API’ın çalıştığı adresler expose edilebilir ve hassas verilerin ele geçirilmesi, cryptojacking saldırıları, container’ların çalıştığı internal network’lere pivoting saldırıları gibi çeşitli senaryoların yaşanabilmesi ihtimalidir.

Bu noktada özellikle cryptojacking konusuna ayrı bir parantez açmak isterim zira eski eğitimlerimde de değindiğim konulardan birisiydi, henüz büyük çapta saldırılar yaşanmamışken üzerine düşündüğüm ve potansiyel saldırı senaryoları oluşturduğum bir konuydu. İlk büyük saldırıyı 2018 yılında okumuştum, birkaç ay önce de 20 milyondan fazla indirme almış bazı Docker imajlarının cryptojacking saldırılarına hizmet ettiği ortaya çıkmıştı.

Örnek Cryptojacking saldırısı

Tekrar rootless mantığına dönelim.

Mevcut mimaride namespaces, cgroups, SELinux, seccomp, AppArmor gibi izolasyon sağlayan yapı ve uygulamalar bulunuyor, lakin root kullanıcısıyla çalışmak her halukarda potansiyel bir exploitation’a davet çıkarabilir desek yanlış olmaz.

Mesela runc’de bu tarz bir zafiyet yayınlanmıştı.

CVE-2019-5736

Docker, 19.03 sürümüyle birlikte rootless modunu bir nevi beta sürümü gibi kullanıma sunmuştu fakat burada bazı problemler mevcuttu; mesela cgroups ve OverlayFS desteklenmiyordu, minor release’lerle birlikte buradaki uyumsuzluklar da giderildi.

Rootless modunu Docker ile birlikte kullanmak için bir script yayınlandı, ayrıca 20.10 sürümünden itibaren paket kurulumuyla da geliyor. Şimdi root kullanıcısı ve sudo yetkisi olmadan bir Docker kurulumu yaparak bu bölümü bitirelim.

Öncelikle yeni bir kullanıcı oluşturalım.

adduser rootless

Docker’ın yayınladığı script’i indirip çalıştırıyoruz.

curl -sSL https://get.docker.com/rootless | sh

12

Gerekli olan çevresel değişkenleri set edelim.

export PATH=/home/rootless/bin:$PATH
export PATH=/home/rootless/bin:$PATH
export XDG_RUNTIME_DIR=/home/rootless/.docker/run

Not: Bu değişkenleri boot sonrası tekrar set etmemek için ~/.bashrc dosyasına yazabilirsiniz.

Son olarak herhangi bir imajı indirebilir veya shell alarak container çalıştırabilirsiniz.

13

rootless kavramının Kubernetes tarafındaki implementasyonu için usernetes projesini inceleyebilirsiniz.

Usernetes

Pod Security Policy

Kısaca bir PSP, Cluster’daki Pod’lara yönelik güvenlik mekanizmaları oluşturmamızı sağlar. Tüm detaylara girmeyeceğim, yine bir Pod’a yönelik yetki kısıtlama konusu üzerinden devam edelim ve bir önceki yaptığımız örneği bu sefer PSP ile gerçekleştirelim.

Öncelikle kullandığımız nsa isimli imaj üzerinden bir Deployment oluşturduğumuzda neler olduğunu görelim.

3

4

apiVersion: v1
kind: Pod
metadata:
name: psp
spec:
securityContext:
runAsUser: 700
containers:
- name: psp
  image: nsa
  command: ["sh", "-c", "sleep 700"]
  securityContext:
  allowPrivilegeEscalation: false
  imagePullPolicy: Never

Bir securityContext oluşturarak, Dockerfile üzerinde UID’sini 700 olarak tanımladığımız deniz isimli kullanıcı üzerinden yetkilendirme verdik, bunu da runAsUser parametresiyle belirttik. allowPrivilegeEscalation ile ek bir güvenlik katmanı oluşturulduğuna dikkat edin.

5

exec ile whoami komutunu çalıştırarak veya bir shell alarak sağlamayı yapabiliriz.

6

NSA’in örnek olarak verdiği diğer PSP kullanımlarına da değinmek gerekirse:

privileged: Pod’ların yetkilendirilmiş bir container’ı çalıştırıp çalıştıramayacağını belirler, false olarak kullanılması önerilir.

hostPID, hostPC: Container’ların host namespace’i ile bir süreç (process) paylaşımında bulunup bulunmayacağını belirler, false olarak kullanılması önerilir.

hostNetwork: Container’ların host ağını kullanıp kullanmayacağını belirler. false olarak kullanılması önerilir.

allowedHostPaths: Container’ların host dosya sistemine yönelik erişimini kısıtlamayı sağlar. Bu noktada host tarafında yeni bir dizin oluşturularak sadece salt okunur (read-only) izni verilmesi önerilir.

readOnlyRootFilesystem: Salt okunur root dosya sistemi kullanılmasını sağlar. Şayet kullandığınız ortam ve uygulamalar buna müsaitse ‘true’ olarak set edilmesi önerilir.

runAsUser, runAsGroup, supplementalGroups, fsGroup: Container’daki uygulamaların root veya root grubu yetkisiyle kullanılıp kullanılmayacağını belirler. root yerine ‘normal’ haklara sahip olan bir kullanıcı ve(ya) grubun kullanılması önerilir.

allowPrivilegeEscalation: Kullanıcı yetkilerinin root yetkisine yükseltilip yükseltilemeyeceğini kontrol eder. false olarak kullanılması önerilir.

seLinux: SElinux kurallarının hedef container’a uygulanıp uygulanmayacağını belirler. Şayet kullanılan baz imaj SElinux’u destekliyorsa, SElinux context’leri oluşturulabilir.

AppArmor parametreleri: AppArmor profilleri container’lara set edilebilir. Bu konuyla ilgili olan yazıma da bakabilirsiniz:

Kubernetes & AppArmor

seccomp parametreleri: sandbox container’ları için seccomp profilleri oluşturulabilir. seccomp’u SElinux’un bir varyasyonu gibi düşünebiliriz.

Oluşturulan PSP sınırlandırmaları iki nedenden dolayı tüm Cluster seviyesinde uygulanmaz:

  • Bir PSP uygulanmadan önce PodSecurityPolicy plugin’i aktif hale getirilmelidir.
  • Oluşturulan policy, RBAC üzerinden yetkilendirilmiş olmalıdır. Bu sayede Cluster’dan sorumlu olan kişi veya kişiler PSP’lerin etki alanını gözlemleyebilir.

NOT: PSP, 1.21 sürümüyle deprecated olarak işaretlenmiştir. Kubernetes’in 1.25 sürümüyle birlikte tamamen kaldırılacak, onun yerine geçici olarak PSP Replacement Policy şeklinde isimlendirilen bir mekanizma kullanılacaktır.

Salt okunur dosya sistemleri

Varsayılan olarak container’lardaki dosya sistemlerinde ek bir güvenlik önlemi bulunmaz. Yani bir container’ı ele geçiren bir saldırgan dosya / klasör oluşturma, şayet dış dünyaya erişimi varsa herhangi bir uygulama / script indirme, sistem dosyalarında değişiklik yapabilme gibi ciddi güvenlik problemleri oluşturabilecek eylemler yapabilir. Siber güvenlik dünyasında Post-exploitation olarak adlandırdığımız bu aşamayı engellemek için alınabilecek bazı önlemler vardır.

Yalnız burada dikkat edilmesi gereken bir nokta da şudur, dosya sistemi üzerinde kısıtlamalara gitmek, container’da bulunan uygulamaların çalışmasında bir uyumsuzluk - problem de oluşturabilir. Böyle bir senaryonun yaşanmaması için developer’larla birlikte hareket ederek container’da çalışan servislerin tam olarak ne yaptığı, sistem üzerinde nelere etki ettiği, özellikle dosya sistemi veya disk üzerinde herhangi bir işlem yapıp yapmadığı sorgulanmalı ve ancak bu sorular cevaplandıktan sonra gerekli önlemler alınmalıdır.

Bununla ilgili bir Deployment üzerinden örnek yapalım.

7

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: rofs
  name: rofs
spec:
  selector:
    matchLabels:
      app: rofs
  replicas: 1
  template:
    metadata:
      labels:
        app: rofs
    spec:
      containers:
        - command: ["sleep"]
          args: ["700"]
          name: nginx
          image: nginx
          securityContext:
            readOnlyRootFilesystem: true
          volumeMounts:
            - name: ro
              mountPath: /var/run
      volumes:
        - name: ro
          emptyDir:

Beklentimiz şu: /var/run dizini haricinde herhangi bir yerde herhangi bir nesne oluşturulmamalı, kontrol edelim.

8

Görüldüğü gibi, root kullanıcısında olmamıza rağmen /mnt ve hatta /tmp dizininde dahi boş bir dosya veya klasör oluşturamadık, fakat mountPath olarak belirttiğimiz /var/run dizininde yazım işlemi yapabiliyoruz.

İmajların güvenli hale getirilmesi

Docker kullanıcıları genellikle bir repository belirtilmediği sürece Docker Hub üzerinden imajlar çeker ve imajla ilgili herhangi bir araştırma yapmadan kullanır. Elbette official olarak tanıtılan imajların güvenilebilirliği konusunda bir şüphemiz olmasa da, public olarak yayınlanan, şahıslara bağlı imajlara yönelik aynı düşüncelerde bulunmak her zaman kolay değil. Diyebilirsiniz ki resmi imajların %100 güvenli olduğunu neye dayanarak söylüyorsunuz? Aslında hiçbir zaman %100 güvenlikten söz edemeyiz. Bir gün Docker sunucularının hacklenip, official imajların zararlı yazılım içeren imajlarla değiştirilmeyeceğinin veya bir Docker çalışanının cinnet geçirip aynı eylemi içeriden yapmayacağının garantisini kim verebilir ki?

2017’de yaşanan CCleaner olayını hatırlayanlar vardır, internal network’e erişim sağlayan saldırganlar, uygulamaya geçerli bir Avast sertifikası vesilesiyle malware yerleştirerek 2 milyondan fazla kişiye zararlı yazılım bulaşmasına neden olmuştu.

CCleaner Malware Attack

Biraz daha olası ihtimallere dönmek gerekirse, imajların oluşturulma aşamasında güvenliklerinin sağlanması, paketlerin güncel tutulması, mümkün olduğunca minimal distro’lar kullanılması ve imajların scan edilmesi önemlidir.

Bir Dockerfile oluştururken güvenlik açısından nelere dikkat edilmeli? sorusunun cevabına ise bir başka yazıda değinmeyi planlıyorum.

Service Account Güvenliği

Normalde bir Pod oluşturulduğunda, aksi belirtilmediği sürece bir Service Account secret token ile birlikte mount edilir. Burada sorulması gereken soru şudur: container’daki uygulamaların bu Service Account’a erişiminin olması gerekiyor mu? Eğer doğrudan bir erişim gereksinimi söz konusu değilse, automountServiceAccountToken parametresi false olarak tanımlanmalıdır.

Örnek olarak herhangi bir Pod’a bakalım, token ve mount ile ilgili bölümü görebiliyoruz:

9

Şimdi de yeni bir Pod oluşturalım ve Service Account token’ının mount edilmesini devre dışı bırakalım.

10

apiVersion: v1
kind: Pod
metadata:
  name: sa
spec:
  serviceAccountName: test
  automountServiceAccountToken: false
  volumes:
    - name: test
      emptyDir: {}
  containers:
    - name: test
      image: nginx
      volumeMounts:
        - name: test
          mountPath: /usr/share/nginx/html

Oluşturduğumuz Pod’a token’ın mount edilmemesi gerekiyor, kontrol sağlayalım.

11

Container Engine Güvenliği

Container izolasyonu için hypervisor katmanı da kullanılan uygulamalara göre sıkılaştırılabilir. NSA, kaynakta Hyper-V’yi örnek göstermiş, mesela Hyper-V üzerinden sağlanabilecek faydaları söylemek gerekirse:

  • Dedike edilmiş (dedicated) kernel kullanılabilir.
  • Uygulamaların kendi aralarındaki ve host işletim sistemine yönelik trust iletişimi kısıtlanabilir.
  • RAM, doğrudan Hyper-V tarafından atanır, bu da buffer overflow gibi ataklarda bir izolasyon sağlayabilir.

Windows üzerinde çalıştırılacak container uygulamaları, şayet Hyper-V desteği alınıyorsa özellikle Krypton ve Xenon tipindeki container’lar izolasyon ve kernel tarafında ekstra güvenlik sağlayacaktır.

Namespaces

Bir namespace, Kubernetes objelerini mantıksal olarak kümelere bölmemizi sağlar. Buradaki amaç, farklı takımların kendilerine ait kaynaklarını bir başka takımın erişememesidir. Elbette bu namespace haricinde farklı auuthentication yöntemleriyle de yapılabilir ama namespace bu izolasyonu sağlayabileceğimiz en pratik yoldur.

Namespace’ler kendi içlerinde de label yardımıyla bir kullanıcı veya Pod atamasına yönelik özelleştirilebilir. Varsayılan olarak üç namespace bulunur ve bu namespace’ler silinemez:

  • default (Kullanıcının oluşturduğu kaynakları tutar)
  • kube-public (Public kaynaklar tutulur)
  • kube-system (Kubernetes bileşenlerini içerir)

Ekran görüntüsünden aradaki farkı daha iyi anlayabilirsiniz.

9b

default namespace’i hariç, oluşturulan herhangi bir nesne diğer iki varsayılan namespace’de tutulmamalıdır. Farklı namespace’ler içerisinde bulunan Pod ve(ya) servisler, herhangi bir engelleyici kural oluşturulmadığı sürece birbirleriyle iletişim kurabilirler.

Bir namespace oluşturmanın iki yolu vardır, birincisi tek komut olarak şu şekildedir:

kubectl create namespace namespace_adı

İkinci yol ise YAML dosyası kullanmaktır, örnek olarak:

apiVersion: v1
kind: Namespace
metadata:
  name: test

Yeni bir kaynak oluştururken namespace özelleştirmesi aynı şekilde metadata altında yapılabilir veya komut tarafında ise —namespace veya -n parametresiyle hedef namespace belirtilebilir.

10b

Network Policies

Networking, birden çok uygulamanın veya nesnenin birbirleriyle haberleştiği hemen hemen her mimaride ayrı bir önem arz etmektedir. Kubernetes dünyasında ise Podların, containerların, master ve worker node bileşenlerinin birbirleriyle olan iletişimi sebebiyle bir saldırgan gözünden bakıldığında olası zayıf noktalar bir şekilde dikkat çekeceği için, cluster yöneticisi olarak network düzeyinde gerekli yapılandırmaları ve kuralları uygulamak oldukça önemlidir.

Bir network policy, cluster üzerindeki trafiğin kural bazında yönetilmesine olanak sağlar. Farklı bir aksiyon alınmadığı sürece, varsayılan olarak herhangi bir network policy uygulanmaz.

Bir policy oluşturabilmek için en önemli şart, NetworkPolicy API’nı destekleyen bir network plugin’in kullanılması gerektiğidir.

Not: Kullanılan CNI plugin’i türüne göre Network Policy’nin kullanım formatı farklılık gösterebilir.

Örneklere başlamadan önce Policy Type kavramına değinelim. Bir Network Policy oluştururken iki seçeneğimiz bulunur:

ingress: Gelen trafiği kontrol eder. egress: Çıkan trafiği kontrol eder.

Lakin birazdan örneklerde de göreceğiniz üzere her iki tip de aynı Network Policy içerisinde bulunabilir. Bu type’lar, from ve to parametreleriyle kuralın hangi Pod veya Pod’lar üzerinden geleceği veya hangi Pod veya Pod’lara uygulanacağı konusunu belirler.

Bir diğer önemli bileşen de podSelector parametresidir. podSelector, kuralların uygulanacağı Pod veya Pod’ları belirler.

Şimdi sık kullanılabilecek bazı senaryoları örnekleyelim.

Gelen tüm trafiğe izin verilmesi:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all-ingress
spec:
  podSelector: {}
  ingress:
    - {}
  policyTypes:
    - Ingress

Gelen tüm trafiğin engellenmesi:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
spec:
  podSelector: {}
  policyTypes:
    - Ingress

Giden tüm trafiğe izin verilmesi:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all-egress
spec:
  podSelector: {}
  egress:
    - {}
  policyTypes:
    - Egress

Giden tüm trafiğin engellenmesi:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-egress
spec:
  podSelector: {}
  policyTypes:
    - Egress

Son olarak, yine pratik olarak kullanılabilecek bir diğer senaryo olan hem gelen hem de giden trafiğin engellenmesine bakalım.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
spec:
  podSelector: {}
  policyTypes:
    - Ingress
    - Egress

Tüm bu işlemlerde podSelector parametresinin {} olarak ayarlandığına dikkat edin, bu şekilde Pod seçimi spesifik olmaktan çıkıp cluster’daki tüm Pod’lara yönelik olacaktır.

Şimdi biraz farklı örnekler yapalım.

Mesela, TCP protokolü için tüm Pod’ların 80. port’a erişimlerini bloklayalım.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all-egress
spec:
  podSelector: {}
  egress:
    - to:
      ports:
        - protocol: TCP
          port: 80
  policyTypes:
    - Egress

TCP haricinde aynı şekilde UDP protokolü de eklenebilir.

  ports:
    - protocol: TCP
      port: 80
    - protocol: UDP
      port: 80

podSelector haricinde namespace üzerinden de hedef(ler) belirlenebilir. Mesela şu örneği inceleyelim:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all-egress
spec:
  podSelector: {}
  egress:
    - to:
        - namespaceSelector:
            matchLabels:
              namespace: test
          podSelector:
            matchLabels:
              app: ssh
      ports:
        - protocol: TCP
          port: 22
  policyTypes:
    - Egress

Buradaki kural tüm Pod’lara yönelik uygulanmıştır. Lakin hedef olarak sadece test namespace’i ve label’ı app=ssh olarak belirtilen Pod’lar seçilmiştir.

Bir başka örneği inceleyelim.

kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
  name: allow-egress-test
  namespace: test
spec:
  podSelector:
    matchLabels:
      app: matrix
  egress:
    - to:
        - ipBlock:
            cidr: 172.17.0.0/24

Bu örnekte ise hedef olarak CIDR belirtilmiştir. ipBlock parametresiyle 172.17.0.0/24 bloğunda bulunan Pod’lara giden trafiğe izin verilmiştir.

Son olarak her iki tipi de içeren bir örnekle bu bölümü sonlandıralım.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: den1z
  namespace: test
spec:
  podSelector:
    matchLabels:
      app: matrix
  policyTypes:
    - Ingress
    - Egress
  ingress:
    - from:
        - ipBlock:
            cidr: 172.18.0.0/24
        - namespaceSelector:
            matchLabels:
              name: allowedns
        - podSelector:
            matchLabels:
              app: allowedpod
      ports:
        - protocol: TCP
          port: 22
  egress:
    - to:
        - ipBlock:
            cidr: 10.0.7.0/24
        - namespaceSelector:
            matchLabels:
              name: targetns
        - podSelector:
            matchLabels:
              app: targetpod
      ports:
        - protocol: TCP
          port: 53

Ingress ve Egress aynı anda uygulanmış, Ingress için CIDR bloğu ve hedef Port olarak 22 verilerek namespace ve Pod label’ı özelinde kuralın kimlere uygulanacağı belirtilmiştir. Egress tarafında da aynı şekilde hem namespace hem Pod için selector kullanılmış, DNS portu belirtilerek sadece 10.0.7.0/24 bloğundaki Pod’lar kurala dahil edilmiştir.

Resource Policies

Namespace ve node düzeyinde limitlendirme işlemlerinde kullanılabilecek iki resource policy vardır:

  • LimitRange
  • ResourceQuota

LimitRange, herhangi bir namespace’deki Pod ve(ya) container’daki CPU ve disk kullanımlarını kısıtlamayı sağlar. Her bir namespace’e yönelik sadece bir LimitRange kısıtlayıcısı oluşturulabilir.

ResourceQuota ise namespace üzerindeki toplam CPU / RAM kullanımına yönelik kısıt oluşturmaya yarar. İki kısıtlayıcıyı da örneklemeden önce testlerde kullanmak üzere ‘test’ isminde yeni bir namespace oluşturalım:

kubectl create namespace test

Şimdi de cpu isminde bir dosya oluşturarak aşağıdaki gibi bir konfigürasyon sağlayalım. Minimum CPU isteğini 200 MB, maksimum 500 MB olarak belirleyelim.

apiVersion: v1
kind: LimitRange
metadata:
  name: cpu-min-max-demo
spec:
  limits:
    - max:
        cpu: "500m"
      min:
        cpu: "200m"
      type: Container

k apply -f cpu --namespace=test

Not: k komutunu kubectl’in alias’ı olarak atadığım için örneklerde kestirme olarak bu şekilde kullanıyorum. Siz de aynı şekilde kullanmak istiyorsanız aşağıdaki komutu girebilirsiniz:

alias k=kubectl

Bu kuralı namespace özelinde spesifik hale getirdiğimize dikkat edin. Şimdi aynı test namespace’i üzerinde CPU limitlerini aşarak yeni bir Pod oluşturmayı deneyelim.

apiVersion: v1
kind: Pod
metadata:
  name: constraints-cpu-demo
  namespace: test
spec:
  containers:
  - name: constraints-cpu-demo
    image: nginx
    resources:
      limits:
        cpu: "900m"
      requests:
        cpu: "700m"

4b

Gördüğünüz gibi maximum CPU limitini 500 MB olarak ayarladığımız için, Pod tarafında 900M limitini kullanmamıza izin vermedi ve Pod’u oluşturmadı. Pod limitini 499M olarak, requests limitini de 400M şeklinde güncelleyelim ve tekrar deneyelim.

k describe po ... komutu ile incelediğimizde limitleri görebiliyoruz.

5b

Şimdi de ResourceQuota kullanarak bir namespace üzerindeki tüm containerların kullanabileceği toplam CPU ve RAM miktarını kısıtlayan bir örnek yapalım. İlk olarak ResourceQuota nesnesini oluşturalım ve önceden oluşturduğumuz namespace’i kullanalım.

apiVersion: v1
kind: ResourceQuota
metadata:
  name: mem-cpu-test
  namespace: test
spec:
  hard:
    requests.cpu: "1"
    limits.cpu: "2"
    requests.memory: 700M
    limits.memory: 1Gi

6b

Şimdi de iki ayrı Pod konfigürasyonu oluşturalım ve limitler toplamının oluşturduğumuz kuralı geçmesine göre ayarlayalım.

1. Pod

apiVersion: v1
kind: Pod
metadata:
  name: quota-mem-cpu-test
  namespace: test
spec:
  containers:
    - name: quota-mem-cpu-test
      image: nginx
      resources:
        limits:
          memory: "400Mi"
          cpu: "400m"
        requests:
          memory: "400Mi"
          cpu: "400m"

2. Pod

apiVersion: v1
kind: Pod
metadata:
  name: quota-mem-cpu-test2
  namespace: test
spec:
  containers:
    - name: quota-mem-cpu-test2
      image: nginx
      resources:
        limits:
          memory: "700Mi"
          cpu: "700m"
        requests:
          memory: "700Mi"
          cpu: "700m"

Şimdi beklentimiz şu; bu Pod’ları ayrı ayrı oluşturmamız durumunda herhangi bir hata almamamız gerekiyor çünkü kendilerine ait olan limit değerleri kuralı geçmiyor, fakat biz art arda oluşturmayı deneyerek hata almayı bekleyeceğiz.

7b

Evet, tam olarak istediğimiz şekilde 2. Pod’u oluştururken hata aldık. Burada dipnot edilmesi gereken nokta, az önce oluşturuduğumuz LimitRange nesnesinin öncelikli olarak çalıştığıdır, yani ResourceQuota sınırlamasına gelmeden önce şayet talep edilen kaynak miktarı LimitRange kuralının üzerindeyse hata alırsınız, bu yüzden:

k delete -f dosya_adı -n test

komutu ile LimitRange’i silebilirsiniz.

Bu başlıkta önerilen aksiyonları özetlemek gerekirse:

  • NetworkPolicy API’nı destekleyen bir CNI plugin’i kullanın.
  • Policyleri podSelector ve(ya) namespaceSelector ile uygulayın.
  • Gelen ve giden trafiği engellemek için bir ‘default’ policy kullanın.
  • Bir namespace veya Pod düzeyinde kaynak limitlemek için LimitRange ve ResourceQuota policylerini kullanın.

kube-apiserver Güvenliği

kube-apiserver, bir önceki bölümde de değindiğimiz gibi çekirdek bileşenlerden birisi ve yanlış - eksik yapılandırmadan dolayı API üzerinden saldırganların cluster erişimini ele geçirmesi, potansiyel atak senaryolarından sadece bir tanesidir.

Bir önceki yazıda da bahsettiğim cryptojacking saldırısıyla yaklaşık 3.5 sene önce Tesla’nın başı bu konuda yandığı için, bilmeyenlerin ilgisini çeker diye kaynak paylaşıyorum:

Tesla

Yine önceki yazıda da belirttiğim CIS’in yayınladığı benchmark tavsiyelerini kube-apiserver üzerinde uygulayabilirsiniz.

Bu işlemleri uygulamak için /etc/kubernetes/manifests/kube-apiserver.yaml dosyasını uygun şekilde değiştirdikten sonra yeni konfigürasyonun uygulanması için kubelet servisini yeniden başlatabilirsiniz.

CIS No Title Varsayılan
1.2.1 —anonymous-auth parametresi kullanılmamalı ya da ‘false’ olarak set edilmelidir Aktif
1.2.2 —basic-auth-file parametresi kullanılmamalıdır Aktif
1.2.3 —token-auth-file parametresi kullanılmamalıdır Aktif
1.2.4 —kubelet-https parametresi kullanılmamalı ya da ‘true’ olarak set edilmelidir Aktif
1.2.5 –-kubelet-client-certificate ve —kubelet-client-key parametreleri uygun şekilde set edilmelidir Aktif
1.2.6 -–kubelet-certificate-authority parametresi uygun şekilde set edilmelidir Pasif
1.2.7 –-authorization-mode parametresi AlwaysAllow olarak set edilmemelidir Aktif
1.2.8 -–authorization-mode parametresi Node’u kapsamalıdır Aktif
1.2.9 -–authorization-mode parametresi RBAC’ı kapsamalıdır Pasif
1.2.10 Admission control plugin’i EventRateLimit set edilmelidir Pasif
1.2.11 Admission control plugin’i AlwaysAdmit kullanılmamalıdır Pasif
1.2.12 Admission control plugin’i AlwaysPullImages kullanılmamalıdır Pasif
1.2.13 PSP kullanılmıyorsa, admission control plugin’i SecurityContextDeny set edilmelidir Pasif
1.2.14 Admission control plugin’i ServiceAccount set edilmelidir Pasif
1.2.15 Admission control plugin’i NamespaceLifecycle set edilmelidir Pasif
1.2.16 Admission control plugin’i PodSecurityPolicy set edilmelidir Pasif
1.2.17 Admission control plugin’i NodeRestriction set edilmelidir Aktif
1.2.18 —insecure-bind-address parametresi kullanılmamalıdır Aktif
1.2.19 -–insecure-port parametresi 0 olarak set edilmelidir Aktif, 0
1.2.20 -–secure-port parametresi 0 olarak kullanılmamalıdır Aktif, 6443
1.2.21 -–profiling argument parametresi ‘false’ olarak set edilmelidir Pasif
1.2.22 ––audit-log-path parametresi set edilmelidir Pasif
1.2.23 -–audit-log-maxage parametresi ‘30’ veya bir başka uygun değere set edilmelidir Pasif
1.2.24 -–audit-log-maxbackup ‘10’ veya bir başka uygun değere set edilmelidir Aktif
1.2.25 —audit-log-maxsize ‘100’ veya bir başka uygun değere set edilmelidir Aktif
1.2.26 -–request-timeout parametresi uygun bir değere set edilmelidir Aktif
1.2.27 -–service-account-lookup parametresi ‘true’ olarak set edilmelidir Pasif
1.2.28 —service-account-key-file parametresi uygun bir değere set edilmelidir Aktif
1.2.29 ––etcd-certfile ve —etcd-keyfile parametreleri uygun değerlere set edilmelidir Aktif
1.2.30 ––tls-cert-file ve -–tls-private-key-file parametreleri uygun değerlere set edilmelidir Aktif
1.2.31 ––client-ca-file uygun bir değere set edilmelidir Aktif
1.2.32 ––etcd-cafile argument uygun bir değere set edilmelidir Aktif
1.2.33 ––encryption-provider-config parametresi uygun bir değere set edilmelidir Pasif
1.2.34 Encryption sağlayıcıları konfigüre edilmelidir Pasif
1.2.35 API Server sadece güçlü kriptografik cipher’lar kullanmalıdır Pasif

etcd Güvenliği

etcd, cluster’a ait durum bilgisini ve secret’ları muhafaza eder, bu durum da kendisini güvenlik açısından son derece önem arz eden bir konuma getirir. Bir şekilde etcd erişimini ele geçiren bir saldırgan, tüm cluster düzeyinde yetki sağlayabilir ki böyle bir durumda secret’ları ve diğer hassas verileri de okuyabilir. etcd’nin sadece API server üzerinden bir yetkilendirme mekanizması doğrultusunda erişilebilmesi önerilmektedir. Alınabilecek aksiyonlardan birisi, etcd ve API server arasındaki iletişimin TLS sertifikaları ile HTTPS protokolüne zorunlu olarak yönlendirilmesidir.

Kubernetes, varsayılan kurulumda etcd için gerekli olan TLS encryption’ı sağlamaktadır. Process kontrolü ile üç parametreyi kontrol etmeniz gereklidir:

  • —cert-file=…
  • —client-cert-auth=…
  • —key-file=…

ps aux | grep etcd komutuyla kontrol sağlayalım.

1b

Görüldüğü gibi etcd için TLS sağlanmış durumda. (Görüntüyü yeni sekmede açıp kontrol edebilirsiniz, some resolution problems…)

Not: Benim örnekte kullandığım cluster Minikube ile oluşturulduğu için, /var/lib/minikube/… dizinini göstermektedir. kubeadm veya bir başka araç ile oluşturulan cluster’lar için bu dizinlerin yolu farklılık gösterebilir.

Bu noktada Kubernetes’in önerdiği ek güvenlik önlemlerinden birisi de verilere encryption uygulamaktır. Varsayılan ayarlarda bu güvenlik katmanı uygulanmaz, kube-apiserver parametrelerini kontrol ederek —encryption-provider-config parametresinin bulunmadığını görebilirsiniz.

Şimdi bir EncryptionConfiguration nesnesi oluşturarak ilgili parametreyi bu nesneyi işaret edecek şekilde set edeceğiz.

İlk olarak 32 bytelık bir random anahtar oluşturarak base64 ile encode edelim.

head -c 32 /dev/urandom | base64

Çıkan değeri kullanarak bir konfigürasyon oluşturalım. Ben /mnt dizini altında encry.yaml adında bir dosya oluşturdum.

apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
    - secrets
    providers:
    - aescbc:
        keys:
        - name: key
          secret: QsGpzJCzuuBXhw8/6QtmRGujD4VAcsnyzE8Lhakfhn0=
    - identity: {}

Şimdi oluşturduğumuz bu yapılandırmayı —encryption-provider-config parametresiyle kube-apiserver üzerinde set edelim. Bunun için ilk olarak /etc/kubernetes/manifests/kube-apiserver.yaml dosyasını açıyoruz.

8b

—encryption-provider-config=…

parametresini dosyayı oluşturduğunuz dizine set ettikten sonra API server’ı yeniden başlatıyoruz. Bunun için kubelet servisini yeniden başlatabilir veya kube-apiserver.yaml dosyasını silebilirsiniz, otomatik olarak tetiklenecektir.

kubeconfig dosyasının Güvenliği

Daha önce Kubernetes ile herhangi bir şekilde vakit geçirdiyseniz, kubeconfig dosyasının cluster’ın temel yapıtaşlarından birisi olduğunu zaten fark etmişsinizdir. Bu dosya, cluster veya cluster’lara ait yetkilendirme parametreleri, kullanıcı, endpoint, namespaces gibi önemli bilgileri içerir ve varsayılan olarak kullanıcının ev dizini altındaki .kube klasöründe tutulur.

Ek bir güvenlik önleminin alınmadığı bir senaryoda, kubeconfig dosyasını ele geçirmiş bir saldırgan, cluster’a erişim sağlayabilir. Bu durumda konu Kubernetes özelinden çıkıp belki biraz daha Linux tarafında değerlendirilebilir fakat Cloud sağlayıcıların da kubeconfig dosyasının yetkilendirilmesiyle ilgili sundukları çözümler bulunmaktadır. Olaya Linux tarafından baktığımızda, en basitinden chmod ile dosya ve dizin yetkileri kontrol edilebilir.

TLS Bootstrapping

Control plane güvenliğindeki aşamalardan birisi de TLS Bootstrapping’tir. Bir cluster’ın TLS 1.2 ya da TLS 1.3 ile birlikte kullanılması önerilmektedir. Bu işlemleri bu yazıda göstermeyeceğim ama kaynak olarak Kubernetes’in dokümantasyonunu veya Kubernetes The Hard Way reposunu inceleyebilirsiniz.

Secrets

Bir secret; parola, OAuth token, SSH anahtarı gibi nesnelerin bilgilerini tutar. Hassas verileri bir YAML konfigürasyonunda, Dockerfile içerisinde veya çevresel değişken olarak plain text şeklinde saklamaktansa, secret nesnesini kullanırız. Varsayılan olarak secret bilgileri base64 olarak encode edilmiş string’ler şeklinde tutar. Bu bağlamda secret erişimlerinin RBAC ile yetkilendirilmesi veya secret’lara yönelik encryption uygulanması önerilir.

Encryption; API server üzerinden veya cloud sağlayıcılarında da bulunan bir Key Management Service (KMS) ile sağlanabilir.

API Server örneği için etcd başlığındaki işlemi takip edebilirsiniz. KMS ise hemen hemen aynı şekilde yapılır, aradaki fark olarak provider’ı kms’i kullanarak endpoint & cachesize & timeout parametrelerini belirtmek gerekir.

apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
      - secrets
    providers:
      - kms:
          name: myKmsPlugin
          endpoint: unix:///tmp/socketfile.sock
          cachesize: 100
          timeout: 3s
      - identity: {}

Aynı şekilde —encryption-provider-config parametresi API Server için set edildikten sonra yeniden başlatılması gerekir.



title: NSA Kubernetes Hardening Notları - 3. Bölüm date: “2021-08-28” template: post


Merhaba, önceki bölümde ağırlıklı olarak Network Policies kavramına ve Control Plane bileşenlerine değinmiştik. Bu bölümde ise Authentication ve Authorization konularıyla başlayıp ogging ile devam edeceğiz.

İlk olarak Authentication ve Authorization kalıplarının farkını açıklamakta fayda var. Authentication, yani kimlik doğrulama kısaca bir kullanıcının belirli bir kaynağa erişimi sırasındaki kimlik doğrulanması işlemidir. Authorization, yani yetkilendirme ise söz konusu kullanıcının hedef kaynak üzerindeki yetkilerini tanımlar.

Kubernetes dünyasında da bu iki kavram, kullanıcıların cluster erişimlerine yönelik birincil mekanizma olarak mimaride yerini alır. İlk iki bölümde de verdiğim örneklerde saldırganların bilindik API port’larına, etcd’ye yönelik tarama eylemleri yaptığından bahsetmiştim. Bu tarz saldırıları engellemek adına bazı Kubernetes bileşenlerinin nasıl yapılandırılabileceğini de inceledik. Bu bölümde ise aynı bakış açısıyla kullanıcı yetkilendirmelerine değineceğiz.

Authentication

Cluster yapısında iki tip kullanıcı bulunur:

  • Servis kullanıcıları (Service Accounts)
  • Normal kullanıcılar

Servis kullanıcıları, Pod’lar için gelen API isteklerini işleyebilir. Bu noktada yetkilendirme ServiceAccount Admission Controller ve buna bağlı bir token vasıtasıyla ilerler.

Token’lar Pod’lara mount edilir ki buna da önceki yazıda Secret alt başlığında değinmiştik. Secret güvenliğinin sağlanmasında bunun kullanıcı tarafındaki implementasyonlarından birisi de birazdan bahsedeceğimiz RBAC fonksiyonunu kullanmaktır.

Authentication için kullanılabilecek farklı yöntemler bulunur:

  • Token’lar
  • X509 Sertifikaları
  • Authentication Plugin’leri
  • OpenID
  • Dosya bazlı parola yetkilendirmesi

vs.

Elbette bulunduğunuz senaryoya göre yöntemlerin birbirlerine karşı avantajları ve dezavantajları mevcut. Mesela herhangi bir durumda dosya bazlı parola yetkilendirilmesinin kullanılması önerilmez, zira olası bir saldırıda görece daha kolay bypass edilebilir.

Bunun Minikube ile bir örneği şu şekildedir:

  • Bir .csv dosyasına kullanıcı adı ve parola bilgileri girilir.
  • minikube --extra-config=apiserver.Authentication.PasswordFile.BasicAuthFile=/pass.csv start komutuyla yetkilendirme bilgilerinin olduğu dosya işaret edilerek cluster oluşturulur.
  • 8443 port’undan API’a (…/api/v1/namespaces) curl isteği atıldığında normal şartlarda 401 hatası alınır ancak saldırganın bir şekilde açık text olarak yazılmış bu dosyaya erişimi olması durumunda curl üzerinden authentication sağlanarak API ile etkileşime girilebilir.

Anonim İstekler (Anonymous Requests)

Anonim istekler, herhangi bir kullanıcıya veya Pod’a bağlı değildir. Kubernetes’in 1.16 sürümüyle birlikte varsayılan olarak aktif olarak gelir ve etkin olduğunda yapılandırılmış diğer authentication yöntemleri tarafından reddedilmeyen istekler, anonim istek olarak işlenerek, ABAC veya RBAC olması durumunda kullanıcı adı olarak system:anonymous ve grup olarak da system:unauthenticated atanır.

Test ortamına yönelik veya bir başka geçerli nedeniniz yoksa, API Server güvenliği için —anonymous-auth parametresinin false olarak ayarlanması önerilmektedir ki ikinci yazıda bu konudan bahsetmiştik.

Rol Bazlı Erişim Kontrolü (Role-Based Access Control | RBAC)

RBAC, Kubernetes için en popüler rol bazlı cluster erişim mekanizmalarından birisidir. 1.16 sürümüyle birlikte varsayılan olarak aktif halde gelir. RBAC’in cluster’ınızda aktif olup olmadığını görmek için:

kubectl api-versions

komutunu kullanabilirsiniz. .rbac.authorization.k8s.io/v1 listede olmalıdır.

1c

Eğer RBAC’i aktif etmek isterseniz, API Server’a aşağıdaki parametreyi ekleyerek yeniden başlatabilirsiniz:

--authorization-mode=RBAC

RBAC iki türde izin desteklemektedir:

  • Roles
  • ClusterRoles

Roles, spesifik bir namespace’i hedef alırken, ClusterRoles namespace’den bağımsız olarak tüm cluster yapısını ele alır. İki tip de sadece yetki ekleme mantığıyla çalışır, yani yetki alma gibi bir opsiyon yoktur. RBAC’in aktif olduğu bir cluster’da anonim erişimi pasif durumdaysa, API Server izinleri reddedecektir. Roles ve ClusterRoles sayesinde bir kullanıcı veya grup, namespace düzeyinde kısıtlandırılabilir. Kısıtlama işlemi yapılırken şu sorular sorulur:

  • Kim?
  • Neyi?
  • Nasıl?

Örnek vermek gerekirse; “Bir X kullanıcısı, bir Pod üzerinde listeleme işlemi yapabilmeli mi?” gibi.

Örnek bir RBAC yapılandırması:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: pod-reader
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "watch", "list"]

Bu örnekte hedef olarak Pod’lar seçilmiştir, Pod bilgilerinin listelenmesi ve izlenmesine izin verilmiştir.

get ile şu şekilde bir API sorgusu atılabilir:

GET /apis/apps/v1/namespaces/{namespace}/deployments/{name}

list ile aşağıdaki API sorguları yapılabilir:

GET /apis/apps/v1/namespaces/{namespace}/deployments
GET /apis/apps/v1/deployments

watch ile aşağıdaki API sorguları yapılabilir:

GET /apis/apps/v1/deployments?watch=true
GET /apis/apps/v1/watch/namespaces/{namespace}/deployments?watch=true

Oluşturduğumuz örnek RBAC kuralını etkinleştirelim:

kubectl apply -f rbac.yaml

Şimdi de bir ClusterRole uygulaması yapalım.

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: secret-read-cluster
rules:
- apiGroups: [""]
  resources: ["services", "endpoints", "pods"]
  verbs: ["get", "list", "watch"]

Aynı şekilde aktif edelim.

kubectl apply -f clusterrole.yaml

Role ve ClusterRole oluşturulan kuralları bir kullanıcıya tanımlamaz. Bu eylem için RoleBindings ve ClusterRoleBindings tanımlamaları kullanılır, bu şekilde oluşturulan kurallar kullanıcı, grup ve servis kullanıcısı (service account) düzeyinde tanımlanabilir. RoleBindings, bu işlemi bir namespace düzeyinde gerçekleştirirken, ClusterRoleBindings bunu tüm cluster seviyesinde yapar.

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: pod-read
  namespace: default
subjects:
- kind: User
  name: deniz
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

Oluşturduğumuz RoleBinding’i aktif edelim.

kubectl apply -f rolebinding.yaml

Bir de ClusterRoleBinding oluşturalım.

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: secret-read
subjects:
- kind: Group
  name: manager
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: secret-read-cluster
  apiGroup: rbac.authorization.k8s.io

Mesela, get ve(ya) list eylemini kısıtlayıp bir kullanıcıya atadığımızda, artık bu kullanıcının hedef bileşenleri listeleyememesi gerekir. Node ve Pod listelemesi yapmaya çalışan bir kullanıcı, aşağıdaki gibi hata alacaktır.

8c

Roles ve ClusterRoles oluşturulduğunda veya güncellendiğinde, hedef kullanıcı yeni rol için gerekli yetkilere aynı kapsam düzeyinde sahip olmalıdır. Kullanıcı veya gruba bir atama işlemi yapıldığında, mevcut yetki üzerinde değişiklik yapılamaz, şayet yeni bir yetki yapılandırması isteniyorsa öncelikle söz konusu yetki atamasının silinmesi gerekir.

Güvenlik açısından baktığımızda, diğer birçok platformda olduğu gibi, burada da izin düzeyi değiştirilecek olan bir kullanıcı, grup ya da servis kullanıcısının ‘minimum yetki’ yaklaşımıyla yapılandırılması gerekir. Varsayılan olarak bir servis kullanıcısı API erişimi için her bir namespace için oluşturulur. RBAC kuralları, servis kullanıcılarının namespace düzeyinde yapabilecekleri eylemleri de özelleştirebilir.

Şayet yönetimsel anlamda kullanıcıların RBAC erişimini özelleştirmek istiyorsanız, bu amaca yönelik hazırlamış olduğum RBAC Controller scriptine de göz atabilirsiniz.

RBAC Controller

Bunun haricinde yine RBAC için işleri yönetmede kolaylık sağlayacak bazı araçlar:

RBAC Tool RBAC Manager RBAC Audit

Logging

Loglama konusu, önceki başlıklar gibi doğrudan bir güvenlik önlemi sağlıyor olmasa da, yazılım dünyasındaki hemen her platformda olduğu gibi neyin ne zaman nasıl olduğuyla ilgili fikir vermesi açısından son derece önemli bir konudur. Kubernetes özelinde sadece spesifik bir bileşenin veya event’ların loglanması, resmin bütününü görebilmek açısından yeterli olmayacaktır. Bu noktada cluster’ın çalıştığı yapı veya yapıların ve aynı şekilde cluster’ın sahip olduğu servislerin de loglanması işin içine dahil olmaktadır. Yani, sadece host veya uygulama özelinde değil, şayet bir sanallaştırma platformu üzerinde çalışılıyorsa veya bir cloud sağlayıcısı kullanılıyorsa aynı şekilde bu yapıların da loglanması önemlidir.

Kubernetes ortamında şu eylemler monitor edilebilir / loglanabilir:

  • API istek geçmişi
  • Performans metrikleri
  • Deployment’lar
  • Kaynak tüketimi
  • İşletim sistemi çağrıları (syscalls)
  • Protokoller
  • Yetki değişiklikleri
  • Ağ trafiği
  • Pod ölçeklendirmesi

En ufak birim üzerinden düşünecek olursak, bir Pod oluşturulduğunda veya güncellendiğinde mevcut log mekanizmasının gelen-giden istekleri, response zamanını, performans metriklerini ve ağ trafiğinin gözlemlenmesi gerekir. Anonim isteklerin kısıtlanması gerektiğinden bahsettik, lakin bu durum olası bir anomali durumunu takip edebilmek adına loglama mekanizmasında anonim kullanıcıları da kapsamaya devam etmelidir.

RBAC ile oluşturulan yetkilendirmelerin belirli periyotlarla izlenmesi ve bir auditing işleminden geçmesi önerilir. Zira geçen süre içerisinde eklenen - çıkarılan kullanıcılar ve(ya) güncellenen yetkilendirmeler olabilir.

Audit denetimleri, mevcut loglarda var olan metriklerin ve her türlü cluster içi faaliyetlerdeki ölçümlerin değişimlerini ve karşılaştırmalarını içermelidir. Burada verilebilecek en temel örneklerden birisi; kaynak kullanımına ait metriklerin beklenmedik bir anda tepe noktasına çıkması -spike- gibi bir senaryonun yaşanmasıdır. Mesela yazının önceki bölümlerinde de bahsettiğimiz cryptojacking saldırılarında Pod’ların kaynak kullanımlarının arttığını ve bir anomali oluşturduğunu biliyoruz. Bu durumda sadece RAM veya CPU kullanımı değil, aynı zamanda tüm trafiğin de aynı şekilde loglanması önemlidir.

Logging işlemi, cluster’ın bulunduğu sistem veya sistemler haricinde dış bir kaynağa da aktarılabilir ki yüksek erişilebilirlik (high availability) penceresinden baktığımızda logging mekanizmasının bir başka kaynakta tutulması genel itibariyle daha mantıklı bir yaklaşım olacaktır. Çünkü iş sadece tüm cluster’ı loglamakla da bitmiyor, merkezi bir logging sunucusu olması durumunda elbette bu sunucunun da periyodik olarak bakımdan geçmesi ve aynı şekilde güvenlik denetimlerinin yapılması mühimdir. Mesela ELK gibi bir yapı kullanıldığında, işin içine bir de performans iyileştirmeleri girebilir ki yanlış bir yapılandırma mevcut verilerin kaybına yol açabilir ve aynı zamanda logların aktarımının minimum TLS 1.2 kullanılarak yapılması beklenir. Logging tarafındaki temel önlemleri özetlemek gerekirse:

  • Mümkünse harici bir kaynak kullanılmalıdır.
  • Log trafiği TLS üzerinden gerçekleşmelidir.
  • Yanlış bir yapılandırma durumuna karşı log aktarımı sadece salt okunur (read-only) modunda yapılmalıdır.

Logging yapılandırması

kube-apiserver’ın iç ve dış API isteklerini işlediğini söylemiştik. Her bir API isteği ki bu bir kullanıcı, uygulama veya servis vasıtasıyla oluşturulmuş olabilir, her bir aşamada bir audit olayı oluşturur. Bu şekilde bir audit olayı oluşturulduğunda, API Server bir audit policy’si veya uygulanabilir bir kural olup olmadığını kontrol eder. Burada bir eşleşme olursa loglama gerçekleşir. Varsayılan olarak Kubernetes’te audit mekanizması aktif değildir.

Bir audit eyleminin loglanması için bir audit policy’si oluşturularak kural veya kuralların aktif edilmesi gerekir. Söz konusu policy dosyasıyla yapılacak eşleşme sonradan kube-apiserver’a iletilir. Bir kuralın geçerli olabilmesi için şu dört audit düzeyinden birisini belirtmesi gerekir:

  • None: Loglama yapılmaz.
  • Metadata: Response veya request body gereksinimi olmadan, sadece metadata’ya (kullanıcı, timestamp, kaynak vs.) bakılır.
  • Request: Metadata ve request body işlenir, response body dahil edilmez.
  • RequestResponse: Metadata, request ve response body dahil edilir.

Tüm audit olaylarını en yüksek seviyede loglamak için gerekli Policy en basit haliyle şu şekildedir:

apiVersion: audit.k8s.io/v1
kind: Policy
rules:
  - level: RequestResponse

Burada da özelleştirme yapılabilir. Mesela:

apiVersion: audit.k8s.io/v1
kind: Policy
rules:
  - level: RequestResponse
    resources:
    - group: ""
      resources: ["pods"]
  - level: Metadata
    resources:
    - group: ""
      resources: ["pods/log", "pods/status"]

İki ayrı düzey aynı Policy için kullanılmış, fakat resource tanımlarındaki metadata seviyeleri özelleştirilmiştir.

Bir diğer konu da oluşturulan bu Policy’lerin API Server’da tanımlanmasıdır. Master node üzerinde /etc/kubernetes/manifests/kube-apiserver.yaml dosyasını herhangi bir metin editörüyle açalım.

Bir önceki yazıda ekstra güvenlik önlemleri adına burada tanımlanabilecek parametrelerden söz etmiştik. Aynı şekilde Policy dosyasını tanımlamak için de —audit-policy-file parametresini kullanacağız. Mesela ben /root/ altında my-policy.yaml dosyasını oluşturdum. Audit loglarının yazılacağı dosyayı da —audit-log-path parametresiyle belirliyoruz.

Opsiyonel olarak logların ne kadar süreyle tutulacağını belirlemek, retain policy’sini düzenlemek için de —audit-log-maxage parametresini kullanabiliriz. Burada iki kullanım daha mümkündür:

  • —audit-log-maxsize: Bir Audit log dosyasının maksimum boyutunu belirler. Set edilmiş boyuttan sonra yeni dosya oluşturulur.
  • —audit-log-maxbackup: Retain edilmeden önce maksimum log dosyası sayısını belirler.

Log dosyaları varsayılan olarak JSON formatında tutulur.

Şayet API Server bir Pod olarak çalıştırılıyorsa, Policy ve log dosyaları bir volume üzerinden hostPath kullanılarak mount edilmelidir. Bir örneği şu şekildedir:

volumeMounts:
  - mountPath: /etc/kubernetes/audit-policy.yaml
    name: audit
    readOnly: true
  - mountPath: /var/log/audit.log
    name: audit-log
    readOnly: false
volumes:
  - name: audit
    hostPath:
      path: /etc/kubernetes/audit-policy.yaml
      type: File
  - name: audit-log
    hostPath:
      path: /var/log/audit.log
      type: FileOrCreate

Worker node ve container loglanması

Kubernetes mimarisinde logging yapılandırmalarının farklı yöntemleri vardır. kubelet, her bir node üzerindeki logging eylemlerini yönetir ve komut satırı aracılığıyla kontrol edilebilir.

Spesifik bir Pod özelinde bir container’ın logları:

kubectl logs -f -p pod_adı -c container_adı

komutuyla görüntülenebilir. Şayet loglar akmaya devam ediyorsa ‘-f’ parametresi kullanılır, mevcut Pod üzerinde birden çok container bulunması durumunda da ‘-c’ parametresiyle container adı seçilebilir. Container, Pod veya node düzeyinde bir hata olması ve bu hata nedeniyle de logların kesilmesi durumunda Kubernetes herhangi bir built-in çözüm sunmamaktadır, yani böyle bir senaryoda mevcut loglar da kaybedilebilir. O yüzden NSA ve CISA merkezi bir loglama sunucusunun yapılandırılmasını önermektedir ki ben de bu görüşe katılmaktayım.

Bu aşamada dört opsiyon sunulur.

1. seçenek: Her bir node üzerinde logların bir backend’e aktarılması için bir agent kullanılır. Bu şekilde node içerisinde yaşanacak bir problemin minimum hataya yol açması hedeflenir.

Bu senaryonun implementasyonunda ise bağımsız bir container oluşturularak, agent’ın da bu container üzerinde çalışması, her bir node’un log dosyalarına erişiminin olması hedeflenir. Böylelikle loglar bir SIEM ürününe veya farklı bir logging mekanizmasına iletilebilir.

2. seçenek: Her bir Pod üzerinde bir sidecar container’ı oluşturulur. Şayet farklı formatlarda aynı anda birden çok log dosyası yazılıyorsa, bu seçeneğin kullanılması önerilir. Aynı şekilde sidecar container vasıtasıyla loglar dış kaynağa aktarılacaktır.

3. seçenek: Her bir Pod’da sidecar agent’ı kullanılır. Node düzeyindeki loglamadan ziyade daha fazla esneklik gerekirse bu seçenek önerilir. Her bir Pod konfigüre edilerek logların backend’e aktarılması sağlanır. Daha çok 3. parti logging agent’larının implementasyonunda kullanılır.

4. seçenek: Bir uygulama vasıtasıyla loglar doğrudan backend’e gönderilir. Uygulama loglarının toplanmasında Kubernetes doğrudan bir seçenek sunmadığı için aynı şekilde üçüncü parti bir uygulama konfigüre edilerek log aktarımı sağlanır.

Logları bir dosyaya veya bir backend kaynağına aktarabilmesi haricinde, sidecar container‘lar bir proxy olarak da konfigüre edilebilir.

Logging bileşenleri Kubernetes ortamında genellikle DaemonSet‘ler aracılığıyla yürütülür, bu şekilde her bir node için logging agent’larının bir kopyasının tutulması sağlanır.

Seccomp

Node ve container tabanlı loglamanın haricinde, sistem çağrılarının loglanması da önemlidir. Bunun için Kubernetes ile entegre çalışan seccomp modülü kullanılabilir. Varsayılan olarak pasif durumdadır.

Seccomp, audit profil dosyaları aracılığıyla sistem çağrılarını loglayabilir. Profil dosyaları özelleştirilerek, hangi sistem çağrılarına izin verilip verilmeyeceği belirtilebilir. Profillerin Pod üzerinden kullanılabilmesi için, seccompProfile parametresi tanımlanır ve bu parametre iki argümana sahiptir:

  • Type
  • localhostProfile

Şimdi bununla ilgili bir uygulama yapalım.

İlk olarak yeni bir dizin oluşturarak Kubernetes’in örnek olarak verdiği 3 profil dosyasını oluşturalım.

mkdir /home/deniz/profiles

İlk dosya audit.json:

{
"defaultAction": "SCMP_ACT_LOG"
}

İkinci dosya violation.json:

{
"defaultAction": "SCMP_ACT_ERRNO"
}

Üçüncü dosya fine-grained.json:

{
"defaultAction": "SCMP_ACT_ERRNO",
"architectures": [
"SCMP_ARCH_X86_64",
"SCMP_ARCH_X86",
"SCMP_ARCH_X32"
],
"syscalls": [
{
"names": [
"accept4",
"epoll_wait",
"pselect6",
"futex",
"madvise",
"epoll_ctl",
"getsockname",
"setsockopt",
"vfork",
"mmap",
"read",
"write",
"close",
"arch_prctl",
"sched_getaffinity",
"munmap",
"brk",
"rt_sigaction",
"rt_sigprocmask",
"sigaltstack",
"gettid",
"clone",
"bind",
"socket",
"openat",
"readlinkat",
"exit_group",
"epoll_create1",
"listen",
"rt_sigreturn",
"sched_yield",
"clock_gettime",
"connect",
"dup2",
"epoll_pwait",
"execve",
"exit",
"fcntl",
"getpid",
"getuid",
"ioctl",
"mprotect",
"nanosleep",
"open",
"poll",
"recvfrom",
"sendto",
"set_tid_address",
"setitimer",
"writev"
],
"action": "SCMP_ACT_ALLOW"
}
]
}

Şimdi kind ile yeni bir cluster oluşturalım. kind, Docker container node’larını kullanarak local bir Kubernetes cluster’ı oluşturmayı sağlamaktadır. Binary formatında basitçe kurabilirsiniz:

curl -Lo ./kind "https://kind.sigs.k8s.io/dl/v0.11.1/kind-$(uname)-amd64"
chmod +x ./kind
mv kind /usr/bin

Şimdi, seccomp profillerini de tanımlayacak şekilde tek node’lu bir cluster kuralım.

apiVersion: kind.x-k8s.io/v1alpha4
kind: Cluster
nodes:
- role: control-plane
  extraMounts:
    - hostPath: "/home/deniz/profiles"
      containerPath: "/var/lib/kubelet/seccomp/profiles"

Not: Ben profiles dizinini kullanıcının ev dizini altında oluşturdum, hostPath‘i uygun şekilde değiştirdiğinize dikkat edin.

kind create cluster --config=kind.yaml

2

Cluster’ın çalıştığını docker ps ile kontrol edebiliriz.

3

containerPath parametresiyle profiles/ dizinini container’a bağladığımız için, oluşturduğumuz profil dosyalarını /var/lib/kubelet/seccomp/profiles altında görmemiz gerekmektedir, kontrol edelim.

4

Şu ana kadar herhangi bir problemle karşılaşmadık. Şimdi, bir Pod özelinde tüm sistem çağrılarını loglayacak şekilde yeni bir Pod oluşturalım.

apiVersion: v1
kind: Pod
metadata:
  name: sec-pod
  labels:
    app: sec-pod
spec:
  securityContext:
    seccompProfile:
      type: Localhost
      localhostProfile: profiles/audit.json
  containers:
  - name: test-container
    image: hashicorp/http-echo:0.2.3
    args:
    - "-text=sistem çağrısı yapıldı!"
    securityContext:
      allowPrivilegeEscalation: false

k apply -f sec-pod.yaml

Mevcut profil herhangi bir sistem çağrısını kısıtlamadığı için, Pod’un sorunsuz bir şekilde başlaması gerekmektedir. kubectl get po ile kontrol sağlayabilirsiniz.

Bu container’la etkileşime girebilmek için bir NodePort servisi oluşturalım.

kubectl expose pod/sec-pod --type NodePort --port 5678

5c

Benim ortamımda 32344 port’una bind etti. curl ile deneme yapalım:

docker exec 741 curl -s localhost:32344

6c

Sistem çağrısının yapıldığını görebildik.

syslog kayıtları kontrol edildiğinde, syscall logları hakkında daha detaylı bilgi edinebilirsiniz.

Syslog

Syslog, Linux dağıtımları üzerinde olan biten eylemlerin kayıtlarının tutulmasını sağlayan bir yapıdır.

Kubernetes ortamında varsayılan olarak kubelet ve CR logları journald servisine yazılır. Bunun yanısıra toplanan logların syslog servisine aktarılması da mümkündür. Syslog kayıtları şu bilgileri taşıyan bir header’a sahiptir:

  • Zaman damgası (timestamp)
  • Host adı (hostname)
  • Uygulama adı
  • Süreç ID’si (PID - Process ID)
  • Açık metin şeklinde bir mesaj kaydı

Syslog protokolü, syslog-ng, rsyslog ve journald araçlarıyla kolaylıkla kullanılabilir ki günümüzde birçok güncel Linux dağıtımı rsyslog ve journald’yi kullanmaktadır. Syslog’a bağlı olan bu loglar journalctl aracıyla komut satırında denetlenebilir.

Bir başka logging mekanizmasının kullanılmadığı bir durumda, container ortamları için syslog araçlarıyla yerel dosya sisteminde tutulur.

SIEM Platformları

SIEM ürünleri, tüm organizasyon ağındaki sistemlerin ve uç birimlerin ürettiği logları toplayıp bunları analiz etmeyi sağlar. Lakin iş sadece text based logging ile sınırlı değildir; firewall ve uygulama logları da aynı şekilde toplanır.

Kubernetes yapısında servis, container, Pod ve node’lar arasında karşılıklı bağımlılıklar olabilir. Servislerin, container’ların veya Pod’ların sürekli yeniden başlatılabildiği veya konfigüre edildiği bir ortamda geleneksel SIEM ürünlerinin bu eylemler nedeniyle tüm ortamı takip edebilmesi zorlaşabilmektedir. Zira statik bir IP atamasının olmadığı bir senaryo Kubernetes ortamı için oldukça sıradan bir durumdur.

Elbette eski SIEM ürünlerinin daha çok on-premise ortamların mimarilerine göre dizayn edildiğini düşünürsek, yeni jenerasyondaki Cloud tabanlı SIEM ürünlerinin container teknolojilerine yönelik çözümler ürettiğini de belirtmek gerekir.

Alerting

Logging konusunda olduğu gibi, alerting tarafında Kubernetes bir built-in çözüm sunmamaktadır. Bunun yanında bazı popüler monitoring araçları alerting özellikleriyle Kubernetes’e entegre edilebilmektedir.

Cluster yöneticisi olarak burada hesaplanması gereken, monitor edilecek kaynakların ve alarm oluşturulması istenen eylemlerin metriklerinin uygun bir şekilde ayarlanmasıdır. Kubernetes üzerinde alarm oluşturabilecek bazı eylemler şu şekildedir:

  • Kullanılabilir disk boyutunun azalması
  • Disk üzerindeki performans problemleri
  • Log aktarımının durması
  • Bir Pod veya uygulamanın root haklarıyla çalışması
  • Kullanıcıların yetkisiz kaynaklara erişimi veya erişmeye çalışması
  • Anonim kullanıcıların yetkisiz eylemleri
  • API veya sistem çağrılarındaki anomaliler

vs.

Alarm mekanizması her ne kadar tek başına bir güvenlik katmanı oluşturmuyor olsa da, olası senaryolara baktığımızda aslında olayın vehametini daha iyi anlıyoruz. Misal, kaynak kullanımı beklenmedik bir limiti geçtiğinde “acaba bir cryptojacking saldırısı mı oldu?” sorusu sorulabilir ki bu aşamada disk kullanımının da %100’e gelmesi durumunda loglamanın da durması işleri iyice zor bir noktaya getirebilir veya zararlı bir uygulamanın çalışacağı bir container’ın deploy edilmesi durumunda beklenmedik bir ağ trafiğinin oluştuğunu ilk saniyelerde tespit edip önlem almak, alerting konusunun aslında ne kadar mühim olduğunu gösterebilen ve birçok senaryonun üretilebileceği bir konudur.

Bir başka yazı dizisinde uygulamalı olarak sidecar container’ı ve ELK üzerinden alerting işlemlerini de göstereceğim.

Service Meshes

Service Meshes, ya da Türkçe haliyle Hizmet Ağları, Kubernetes dünyasında uygulamalardaki microservice iletişimlerini kolaylaştıran yapılardır. Popüler olarak:

  • Istio
  • Linkerd
  • Traefik
  • Consul
  • Kong

gibi uygulamaları örnek verebiliriz. Normalde microservice düzeyinde bir problem yaşandığında özellikle kompleks mimarilerde debug işlemi yapmak gerçekten baş ağrıtabilen bir eyleme dönüşebilir. Hizmet ağları bu noktada bizlere yerine göre kolaylık sağlamaktadır. Yukarıda verdiğimiz uygulamalar özelinde de özetleyecek olursak, hizmet ağları şu eylemleri yapabilir:

  • Bir serviste herhangi bir nedenden dolayı hizmet kesintisi olduğunda trafik yönlendirilebilir.
  • Performans metrikleri toplanarak optimizasyon yapılabilir.
  • Service-to-Service iletişimindeki şifreleme yönetilebilir.
  • Service-to-Service iletişimindeki loglar toplanabilir.
  • Her bir servise ait loglar toplanarak sorunların çözümünde developerlara debug işlemlerinde kolaylık sağlayabilir.

Hizmet ağları, servislerin hibrit veya çoklu bulut platformlarına migrate edilmesine de olanak sağlayabilir. Yine de hizmet ağları Kubernetes ortamı için bir zorunluluk değildir, bu daha çok mimari yönetiminin nasıl sağlanacağıyla alakalı bir konudur. Elbette özellikle ağ trafiğinin sertifika otoriteleri aracılığyla güvenli hale getirilmesi, TLS gibi kimlik doğrulama seçenekleri göz önünde bulundurulduğunda, cluster yapısını daha güvenli hale getirebilmek adına hizmet ağlarının önemli bir rol oynadığını söylemek yanlış olmayacaktır.

Fault Tolerance

Logging servislerinin erişilebilirliğinin sağlanması adına fault tolerance policy’lerinin uygulanması önerilir. Logların mümkün mertebe harici bir sunucuya veya merkezi bir logging mekanizmasına gönderilmesi gerektiğini söylemiştik. Fault tolerance’ın bu olaydaki önemi, logların aktarımında herhangi bir problem yaşanması durumunda ikinci bir kaynağın devreye girmesi veya localde bir yedek loglamanın başlatılmasına yöneliktir. Asıl kaynakla olan iletişim yeniden kurulduğunda ikinci kaynakta veya localde depolanmış bu logların tekrar merkeze iletilebilmesi için bir aksiyon planı oluşturulmalıdır.

Kubernetes açısından baktığımızda, kullanıcı eylemlerinin olumsuz etkilenmemesi ve hataların minimum düzeyine indirgenmesi adına fault tolerance ilkesi önem taşır, yani bu konu sadece logging üzerinden düşünülmemelidir.

Araçlar

Kubernetes, tam kapsamlı bir denetleme çözümü sunmamaktadır fakat mimarisi farklı tipte uygulamaları konfigüre etmeye ve kullanıcıların kendi plugin / çözümlerini oluşturmalarına olanak sağlar. Siber güvenlik takımlarının gözünden baktığımızda, popüler SIEM platformlarının Kubernetes ile entegre edilebildiğini söyleyebiliriz. Açık kaynak dünyasındaki monitoring araçları olarak ise:

  • ELK
  • Prometheus
  • Grafana

üçlüsünü mevcuttaki en popüler uygulamalar olarak örnek verebiliriz. Bu uygulamalar kaynak kullanımı, anomali tespiti, ağ trafiği, metrik ölçümü gibi farklı alanlarda monitoring ve alerting çözümleri sunmaktadır.

RHEL ve Prometheus’a yönelik yazımı da inceleyebilirsiniz:

Prometheus & RHEL

Belki bir başka yazıda ELK - Prometheus / Grafana’ya yönelik bir uygulama da yaparız, şimdilik yazı dizisini burada noktalıyorum.



Written by Deniz Parlak