Két éve futtatom a Falcót runtime securityre a legtöbb klaszteremen. Tette a dolgát, de a kernelmodulos megközelítés mindig törékenynek érződött. Minden kernel frissítésnél benne volt a pakliban, hogy valami eltörik. Amikor a Cilium Tetragon elérte az 1.3-as stabil verziót, tisztán eBPF alapon, kernel modul nélkül, úgy döntöttem, élesben is adok neki egy esélyt.

Ez történt.

Miért váltottam a Falcóról

A Falco jó eszköz, félreértés ne legyen. De újra és újra ugyanazokba a problémákba futottam:

  • Kernel modul újrafordítás minden node frissítésnél (az eBPF probe-bal is volt kompatibilitási gond)
  • Magas CPU használat 80+ podos node-okon
  • Szabály szintaxis, amihez senki nem akart hozzányúlni a csapatban
  • False positive-ok, amiktől mindenki ignorálta az alerteket

A Tetragon alacsonyabb overheadet ígért, mert közvetlenül eBPF-en keresztül kapcsolódik a kernelhez. Emellé TracingPolicy CRD-ket ad a finom szabályozáshoz és natív Kubernetes awareness-t. Szkeptikus voltam, de a teljesítményszámokat látva úgy voltam vele, ezt ki kell próbálni.

Tetragon telepítése

Nálam Cilium fut mint CNI, szóval a Tetragon hozzáadása egyszerű volt. Ha nem Cilium a CNI-d, az sem gond. A Tetragon önállóan is működik.

helm repo add cilium https://helm.cilium.io/
helm repo update

helm install tetragon cilium/tetragon \
  --namespace kube-system \
  --set tetragon.enableProcessCred=true \
  --set tetragon.enableProcessNs=true \
  --set tetragon.exportRateLimit=200

Az enableProcessCred és enableProcessNs flagek fontosabbak, mint elsőre tűnik. Nélkülük nincs uid/gid infó és namespace kontextus az eventekben. Az első deploynál kihagytam őket, és elment egy órám arra, hogy kiderítsem, miért nem matchelnek a policy-k.

Ellenőrzés:

kubectl get pods -n kube-system -l app.kubernetes.io/name=tetragon

Minden node-on látnod kell egy tetragon podot (DaemonSet).

Az első TracingPolicy

Alapból a Tetragon process lifecycle eventeket ad (exec, exit). Hasznos, de nem ez benne az igazán izgalmas rész. Az ereje a TracingPolicy-ban van.

Itt az első, amit írtam. Elkap minden containert, ami megnyitja az /etc/shadow-t:

apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
  name: sensitive-file-access
spec:
  kprobes:
    - call: fd_install
      syscall: false
      args:
        - index: 0
          type: int
        - index: 1
          type: "file"
      selectors:
        - matchArgs:
            - index: 1
              operator: Equal
              values:
                - "/etc/shadow"
                - "/etc/passwd"
                - "/etc/kubernetes/pki"
          matchNamespaces:
            - namespace: Pid
              operator: NotIn
              values:
                - "host_ns"

Alkalmazás:

kubectl apply -f sensitive-file-access.yaml

Tesztelés: lépj be bármelyik podba és próbáld olvasni az /etc/shadow-t:

kubectl exec -it some-pod -- cat /etc/shadow

Nézd meg a Tetragon logokat:

kubectl logs -n kube-system -l app.kubernetes.io/name=tetragon -c export-stdout --tail=20 | \
  jq 'select(.process_kprobe != null)'

Egy JSON eventet kell látnod teljes process fával, container ID-val, pod névvel, namespace-szel és labelekkel. Ebben ez a jó. Nincs külön korreláció, nincs sidecar, nincs törékeny log parsing. A kernel megmondja, mi történt, a Tetragon pedig hozzáadja a K8s kontextust.

A policy, ami megmentett minket

Két héttel a Tetragon deploy után elkapott valamit, amit a Falco nem vett észre. Az egyik Java szolgáltatásunk shell subprocesst indított, hogy curl-lel csináljon health checket (igen, tényleg). Csúnya megoldás, de nem rosszindulatú. Viszont ugyanez a minta, Java processből indított shell, pont úgy néz ki, mint egy container escape.

Írtam egy TracingPolicy-t, ami alertel minden nem-init process exec-re konténereken belül:

apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
  name: unexpected-process-exec
spec:
  kprobes:
    - call: sys_execve
      syscall: true
      args:
        - index: 0
          type: "string"
      selectors:
        - matchArgs:
            - index: 0
              operator: NotIn
              values:
                - "/bin/sh"
                - "/usr/bin/java"
                - "/usr/bin/python3"
          matchNamespaces:
            - namespace: Pid
              operator: NotIn
              values:
                - "host_ns"
          matchBinaries:
            - operator: NotIn
              values:
                - "/pause"
                - "/usr/bin/tini"

Egy napon belül elkapott egy kompromittált npm dependenciát egy staging szolgáltatásban, ami le akart tölteni és futtatni egy binárist. A process fa így nézett ki: node -> sh -> curl -> suspicious-binary. Simán észrevétlen maradt volna, mert a szolgáltatás közben átment a health checkeken.

Teljesítmény: a számok

Mértem a CPU és memória overheadet egy 120 podos node-on, 3 aktív TracingPolicy mellett:

Metrika Falco (eBPF probe) Tetragon
CPU (átlag) 180m 45m
CPU (p99) 620m 110m
Memória 340Mi 95Mi
Event latency ~8ms ~1.2ms

A különbség nagyon látványos. A Tetragon nagyjából 4x hatékonyabb CPU-ban és a memória negyedét használja. A jobb event latency azt is jelenti, hogy a policy-k valós időben tudnak enforceolni úgy, hogy nem adnak érezhető késleltetést a syscallokhoz.

Enforcement mód: óvatosan

A Tetragon képes kilőni a policy-nak megfelelő processeket. Ez erős, de kockázatos funkció. Adj hozzá matchActions-t Sigkill-lel egy selectorhoz:

selectors:
  - matchArgs:
      - index: 0
        operator: Equal
        values:
          - "/usr/bin/wget"
    matchActions:
      - action: Sigkill

Egy hónapig teszteltem stagingen, mielőtt productionben bekapcsoltam. Kezdd Override actionnel (hibát ad vissza a syscallra) a Sigkill helyett (terminálja a processt). Az Override kevésbé durva, és ad időt a policy-k finomhangolására.

A javaslatom: futtasd observe-only módban legalább 2 hétig klaszterenként. Exportáld az eventeket a SIEM-be, építs dashboardokat, hangold ki a false positive-okat, és csak utána kapcsold be az enforcementet.

Eventek a stackedbe

A Tetragon JSON eventeket exportál, és ezt viszonylag egyszerű bejuttatni az observability stackedbe:

# Közvetlenül stdout-ra (az export-stdout konténer)
kubectl logs -n kube-system -l app.kubernetes.io/name=tetragon -c export-stdout -f

# Vagy használd a tetragon CLI-t szűrt, olvasható kimenethez
kubectl exec -n kube-system ds/tetragon -c tetragon -- \
  tetra getevents -o compact

Productionben az export-stdout konténer logjait Fluent Biten keresztül tolom Lokiba. Egy egyszerű Fluent Bit szűrő parse-olja a JSON-t, és hozzáad egy severity mezőt a policy név alapján. Ezután Grafana dashboardokon nézem a process exec eventeket namespace-enként, a fájlhozzáférési szabálysértéseket és a váratlan binárisokból jövő hálózati kapcsolatokat.

A fő panelek nálam:

  • Process exec heatmap namespace és bináris név szerint
  • Policy violation-ök időben, TracingPolicy név szerint csoportosítva
  • Top talkerek: a legtöbb security eventet generáló podok

Buktatók, amikbe belefutottam

1. ARM64 node-oknak friss kernel kell. Ha vegyes amd64/arm64 klasztert futtatsz (mint én Graviton node-okkal EKS-en), győződj meg róla, hogy az ARM node-ok legalább 5.15-ös kernelt futtatnak. Régebbi kerneleken eBPF verifier bugok vannak, amiktől a Tetragon podok CrashLoopolnak.

2. A TracingPolicy sorrend számít. Ha két policy matchel ugyanarra a syscallra, mindkettő tüzel. De ha az egyikben Sigkill action van, a másikban Override, a Sigkill nyer. Dokumentáld a policy-jaidat alaposan.

3. Az export rate limiting a barátod. exportRateLimit=200-at állítottam be a Helm values-ban. Enélkül egy zajos workload (rád nézek, PHP-FPM) másodpercenként ezres nagyságrendben dobja az eventeket, és elárasztja a log pipeline-t.

4. A tetra CLI nélkülözhetetlen a debuggoláshoz. Telepítsd lokálisan:

curl -LO https://github.com/cilium/tetragon/releases/latest/download/tetra-linux-amd64.tar.gz
tar xzf tetra-linux-amd64.tar.gz
sudo mv tetra /usr/local/bin/

Használd a tetra getevents-et --namespace és --pod szűrőkkel, hogy policy-t tudj debugolni anélkül, hogy belefulladnál a klaszterszintű eventekbe.

5. Ne felejtsd ki a kube-system-et. Az első heted tele lesz alertekkel a kubelet-től, kube-proxy-tól és más rendszer komponensektől, amik teljesen legit dolgokat csinálnak. Adj hozzá namespace kizárásokat a policy-jaidhoz korán.

Megérte?

Igen, abszolút. A Tetragon lecserélte a Falcót három klaszteremen a négyből. A negyediken régebbi kernel fut, ami nem támogat minden eBPF feature-t, amit a Tetragon igényel, szóval ott egyelőre marad a Falco.

Az alacsonyabb erőforráshasználat, a Kubernetes-natív policy-k és a valós enforcement együtt a Tetragont teszik jobb választássá a legtöbb 5.10+ kernelt futtató klaszterhez. A TracingPolicy CRD megközelítés pedig azt jelenti, hogy a security policy-kat ugyanúgy kezelem, mint bármi mást Kubernetesben, GitOps-szal, ArgoCD-vel.

Ha már Cilium a CNI-d, a Tetragon hozzáadása szerintem egyszerű döntés. Ha nem, akkor is érdemes önállóan kiértékelni. Az eBPF alapú runtime security egyértelműen az az irány, amerre az iparág tart, és a Tetragon a leginkább production-ready implementáció, amit eddig használtam.