Falco ile Runtime Security Kuralları
Selamlar, bu yazıda Falco ile runtime security kurallarını cluster üstünde nasıl ayağa kaldırdığımıza birlikte bakacağız. Image scan'ler, admission controller'lar derken konteyner güvenliğinin önemli bir parçasını çoğu zaman atlıyoruz: konteyner ayağa kalktıktan sonra içeride neler olup bittiği. Statik tarama saldırganı kapıdan içeri girmeden yakalar; Falco ise içeri girdikten sonraki ilk hareketinde yakalamaya çalışır. Lafı uzatmadan başlayalım.
Falco ne yapar, nasıl dinler?
Falco, CNCF'in graduate ettiği bir runtime güvenlik aracı. İşin özü şu: node üzerinde syscall'ları dinliyor, bir kural setine göre değerlendiriyor ve eşleşince alert üretiyor. Dinleme kısmı için iki seçenek var, eBPF probe ya da kernel modülü. Bence yeni kurulumlarda eBPF tercih edilmeli, hem kerneli daha az kirletiyor hem de modern dağıtımların çoğunda doğrudan çalışıyor. Modern olmayan, eski 4.x kerneller varsa kernel modülü hâlâ yedek planınız olabilir.
Bunun yanında Falco, Kubernetes audit log'unu da kaynak olarak okuyabiliyor. Yani sadece syscall seviyesinde değil, API server'a düşen get secret veya exec istekleri gibi olayları da yakalayabiliyorsunuz. İki kaynağı birlikte kullanmak, hem node hem control-plane tarafını kapsıyor.
Helm ile cluster'a kurulum
Kuruluma resmi Helm chart ile başlamak en pratiği. eBPF sürücüsünü ve falcosidekick'i tek seferde aktif ediyoruz:
helm repo add falcosecurity https://falcosecurity.github.io/charts
helm repo update
helm install falco falcosecurity/falco \
--namespace falco --create-namespace \
--set driver.kind=ebpf \
--set falcosidekick.enabled=true \
--set falcosidekick.webui.enabled=true
Pod'lar ayağa kalkınca akan log'a bakıp ortamın sağlıklı olduğundan emin olun:
kubectl logs -n falco -l app.kubernetes.io/name=falco -f
İlk açılışta default rule set'ten gelen birkaç event görmek normaldir; gerçek alarmlar değil, çoğu zaman cluster'ın kendi günlük telaşıdır.
Kural yazmanın üç parçası: rule, macro, list
Falco kuralları YAML üç temel kavram üstüne kurulu. Rule asıl tespit cümlesidir, macro tekrar kullanılan koşul parçasıdır, list ise isimlendirilmiş bir öğe kümesidir. Şu örnekte üçü birden var:
- list: shell_binaries
items: [bash, sh, zsh, ksh]
- macro: container
condition: container.id != host
- macro: spawned_shell
condition: evt.type = execve and proc.name in (shell_binaries)
- rule: Shell Spawned in Container
desc: Konteyner icinde shell calismasi (olasi compromise)
condition: spawned_shell and container
output: >
Shell spawned (user=%user.name container=%container.name
image=%container.image.repository cmdline=%proc.cmdline)
priority: WARNING
tags: [container, shell]
Bu kuralın güzel yanı şu: konteynerin içine kabuk düşüren her execve çağrısında bir alert üretiyor. Production konteynerinin içinde kabuk açılmasının meşru bir nedeni nadiren olur; bu yüzden bu kural neredeyse her cluster'da bulunmalı bence.
Hassas dosya erişimi ve privilege escalation
Listeleri biraz daha iddialı kullanalım. Aşağıdaki kural /etc/shadow ve SSH anahtarları gibi dosyalara konteyner içinden okuma denenmesini yakalıyor:
- list: sensitive_files
items:
- /etc/shadow
- /etc/sudoers
- /root/.ssh/authorized_keys
- rule: Read Sensitive File
desc: Konteynerden hassas dosya okuma
condition: open_read and fd.name in (sensitive_files) and container
output: >
Sensitive file read (file=%fd.name user=%user.name
container=%container.name cmd=%proc.cmdline)
priority: WARNING
Privilege escalation kuralı da setuid/setgid syscall'larını izliyor. Burada not user.name = root koşulu kritik, çünkü zaten root olan bir process'in setuid çağırması anlamlı bir bulgu sayılmaz.
Falcosidekick ile alert routing
Falco tek başına sadece stdout'a yazar; alert'i bir yere ulaştırmak için falcosidekick kullanıyoruz. 50'den fazla hedefi destekliyor; biz tipik olarak Slack'e WARNING üstünü, PagerDuty'ye CRITICAL'ı yönlendiriyoruz:
falcosidekick:
enabled: true
config:
slack:
webhookurl: 'https://hooks.slack.com/services/XXX/YYY/ZZZ'
channel: '#security-alerts'
minimumpriority: 'warning'
pagerduty:
apikey: 'pd-api-key'
minimumpriority: 'critical'
elasticsearch:
hostport: 'https://es.example.com:9200'
index: 'falco'
minimumpriority ayarını dikkatli seçin. Hepsini Slack'e bağlarsanız iki gün sonra kanal sessize alınır ve gerçek olay kaçar.
Kubernetes audit log kaynağı
Audit log entegrasyonu için kube-apiserver'a bir webhook config'i veriyorsunuz, falco bunu k8s_audit source'u olarak okuyor. Örnek bir kural:
- rule: K8s Secret Get
desc: Secret okuma denemesi
condition: >
ka.verb = get and ka.target.resource = secrets and
not ka.user.name in (allowed_service_accounts)
output: >
Secret accessed (user=%ka.user.name ns=%ka.target.namespace
secret=%ka.target.name)
priority: WARNING
source: k8s_audit
allowed_service_accounts listesini doğru tutmak işin püf noktası. Buraya operator'lerinizi, controller'larınızı koymazsanız kısa sürede gürültüye boğulursunuz.
Sık karşılaşılan tuzaklar
- Default kuralları olduğu gibi açık bırakmak: Cluster'ınıza özel beyaz liste yapmadan default set'i bırakırsanız ilk gün yüzlerce false positive alırsınız. Önce iki üç gün stats ile gözleyin, sonra exception ekleyin.
- eBPF probe'u eski kernelde zorlamak: 4.18 altı kerneller eBPF'in ihtiyacı olan özelliklere sahip değil.
driver.kind=moduledeneyin ya da kerneli güncelleyin. - falcosidekick'i hedefsiz açmak: Sadece UI'ya bağlı kalırsa olay olduğunda kimsenin haberi olmaz. En az bir 'insanın bakacağı' kanal şart.
- Gürültülü kuralı susturmak yerine kaldırmak: Gürültüden kurtulmak için kuralı silmek kolay ama riskli; doğrusu macro'larla istisna eklemek.
Kapanış
Bu yazıda Falco'yu eBPF probe ile cluster'a kurduk, rule/macro/list üçlüsüyle kendi kurallarımızı yazdık ve falcosidekick üzerinden alert akışını şekillendirdik. Bana sorarsanız runtime security'yi 'image scan yaptık, yeter' diye bırakmamak gerekiyor; Falco ile en azından konteynerin içinde olup biteni görmek elinizdeki en iyi pratiklerden biri. Umarım faydalı olur, görüşmek üzere.
