Debugging in Produktion: Chaos Engineering mit eBPF

Seite 2: Observability mit eBPF

Inhaltsverzeichnis

Über die gängigen Observability-Datentypen mit Metriken, Logs und Traces hinaus gestalten sich die Anforderungen in verteilten Cloud-nativen Umgebungen noch deutlich komplexer. Um etwa den Netzwerkverkehr zwischen Containern in einem Kubernetes-Cluster zu messen, muss man schon etwas tiefer in die Trickkiste – und zu eBPF – greifen. Inspektor Gadget ist beispielsweise eine Tool-Sammlung zum Debuggen verschiedener Kubernetes-Probleme. Das reicht von System-Event-Tracing über Resource-Top-Monitoring bis hin zu Profiling und Auditing.

Einen Anwendungsfall für eBPF hat Cloudflare im Jahr 2019 gestartet: ein Prometheus-Exporter (ebpf_exporter), der mittels eBPF Low-Level-Kernel-Metriken sammeln und exportieren kann (siehe Listing 3 und Abbildung 3). Dadurch eröffnen sich tiefere Einblicke in die System-Performance, um beispielsweise Disk-IO oder TCP-Antwortzeiten in großen verteilten Umgebungen besser analysieren und optimieren zu können. Um Abhängigkeitsproblemen mit den Bibliotheken libbpf und libpbfgo auf dem Host aus dem Weg zu gehen (der ebpf_exporter greift auf libbpf zurück), sollten Entwicklerinnen und Entwickler ein Docker-Image erstellen, um die Binaries zu bauen und dann den Container starten. Dabei empfiehlt es sich, mindestens Kernel 6.1 zu verwenden und aus den Sources zu kompilieren.

Listing 3: Cloudflare Prometheus-Exporter

# Ubuntu 23 VM

git clone https://github.com/cloudflare/ebpf_exporter
cd ebpf_exporter

make build
cd examples && make build && cd ..

sudo ./ebpf_exporter --config.dir examples --config.names accept-latency,bpf-jit,cachestat,cgroup,llcstat,oomkill,raw-tracepoints,shrinklat,tcp-syn-backlog,tcp-window-clamps,timers,uprobe,usdt

# Open localhost:9543 in your browser
http://localhost:9435/metrics

Der Prometheus-Server sammelt die vom ebpf_exporter gelieferten Kernel-Metriken (Abb. 3).

Das CNCF-Projekt (Cloud Native Computing Foundation) OpenTelemetry investiert ebenfalls in eBPF: Der noch recht neue eBPF-Collector für Linux-Host-Systeme, Cloud und Kubernetes verspricht verschiedene Vorteile gegenüber einem Prometheus Exporter. Im sogenannten Reducer-Prozess lassen sich die Metriken mit Metadaten anreichern und direkt an den OpenTelemetry Collector schicken.

Ein weiterer Anwendungsfall für eBPF zielt auf die Effizienz von Entwicklerinnen und Entwicklern ab: Um Traces aus eigenen Applikationen an OpenTelemetry zu schicken, müssen sie den Code instrumentieren, Anpassungen vornehmen, das Software Development Kit (SDK) verstehen und unter Umständen komplexe Probleme der eigenen Architektur lösen. Das limitiert den Einsatz von OpenTelemetry, da viele Entwicklungsteams nicht das notwendige Wissen und die Ressourcen für Observability haben. Einige experimentierfreudige eBPF-Experten haben inzwischen einen Weg gefunden, um Kernel-Events und Funktionsaufrufe mit Debug-Symbolen zu korrelieren und Auto-Instrumentation mit eBPF anzubieten. Die Vorgehensweise ist dabei für jede Programmiersprache unterschiedlich, aber es haben sich bereits eine Reihe von Projekten und Anbietern gefunden, die Innovation in diesem Sektor vorantreiben – im Speziellen für Continuous Profiling.

Parca beispielsweise sammelt mit einem Agenten Performance-Profile von laufenden Applikationen, die sich anschließend für Debugging und Performance-Analyse verwenden lassen (siehe Abbildung 4). Parca steht als Open Source unter Apache-2.0-Lizenz auf GitHub frei zur Verfügung. Es stellt eine Server- und eine Agent-Komponente bereit, die sich als Binary, Docker-Container oder Kubernetes-Deployment starten lassen. Der Agent verwendet eBPF, um Performance-Profile zu sammeln. Die Firma Polar Signals, die Parca ursprünglich entwickelt hat, bietet eine Columnar-Datenbank namens FrostDB, mit der sich die Profile performant speichern lassen.

Parca stellt gesammelte Performance-Profile mit Icicle-Graphen und Tabellen dar. So lässt sich in ressourcenhungrige Callstacks hineinzoomen (Abb. 4).

Continuous Profiling gilt als der neue Weg, automatische Performance-Profile zu sammeln. Viele Anbieter folgen diesem Ansatz, wie unter anderen Grafana Pyroscope, Datadog Profiler und Elastic Profiling zeigen. Beim Continuous Profiling dienen Flamegraphs dazu, Callstacks zu visualisieren – den bisher notwendigen Umweg über perf oder vergleichbare Leistungsanalyse-Tools können sich Developer sparen.

Die Anbieter von Observability-Plattformen setzen zunehmend auf eBPF. Datadog hat unter anderem einen Agenten mit eBPF ausgestattet, um per Auto-Discovery mehr Einblicke sammeln zu können. Das neue Open-Source-Projekt Coroot hat sich Kubernetes-Observability auf die Fahnen geschrieben. Ein innovatives Feature sind die Service Maps, die Coroot anhand der Container-Kommunikation mit TCP-Paketen sowie den Kubernetes-API-Metriken dynamisch erstellt und visualisiert. Nutzerinnen und Nutzer erhalten somit auch ohne Konfigurationsaufwand direkt einen Überblick über Services und Deployments und können unmittelbar mit der Analyse und Fehlersuche beginnen.

Die Installation von Coroot gestaltet sich einfach: In der Dokumentation lassen sich die entsprechenden Features aus- oder abwählen. Anschließend steht wie in Listing 4 gezeigt ein Kommando zum Installieren des Helm-Charts parat. Nach erfolgreicher Installation präsentiert die Coroot-Oberfläche die Service Map mit der Möglichkeit, in die einzelnen Services und Nodes hineinzoomen zu können (siehe Abbildung 5). Über die klassischen Observability-Praktiken hinaus eröffnet Coroot auch die Option, Out-of-Memory-Probleme frühzeitig zu erkennen.

Listing 4: Coroot-Installation

# Helm, kubectl, Minikube muss installiert sein, damit Coroot in einem neuen Kubernetes Cluster installiert werden kann. Alternativ kann man auch bei einem Cloud-Anbieter einen Kubernetes-Cluster starten, beispielsweise Civo Cloud.

civo kubernetes create ix-ebpf-chaos
civo kubernetes config ix-ebpf-chaos -s
kubectl config use-context ix-ebpf-chaos

# Podtato-head demo https://github.com/podtato-head/podtato-head

git clone https://github.com/podtato-head/podtato-head.git && cd podtato-head
helm install podtato-head ./delivery/chart

# Coroot
helm repo add coroot https://coroot.github.io/helm-charts
helm repo update

helm install --namespace coroot --create-namespace --set pyroscope.enabled=false --set pyroscope-ebpf.enabled=false --set clickhouse.enabled=false --set opentelemetry-collector.enabled=false --set node-agent.otel.tracesEndpoint="" coroot coroot/coroot

kubectl --namespace coroot port-forward service/coroot 8080:8080
# http://localhost:8080

Coroot erkennt den Netzwerkverkehr zwischen Services, erstellt eine filterbare Map und erlaubt es, zum Debugging in Services und Nodes hineinzuzoomen (Abb. 5).

Neben Coroot beherrscht auch Caretta von Groundcover das Erstellen dynamischer Service Maps. Caretta sammelt die Metriken und visualisiert sie in Grafana-Dashboards. Pixie bietet auf Entwicklerinnen und Entwickler zugeschnittene Kubernetes-Observability, inklusive einer High-Level-Abfragesprache, Weathermaps und detaillierteren Einblicken in Applikations-Deployments, ohne dass Code-Änderungen notwendig sind.

Der Anspruch, nicht nur System-Performance-Daten zu verarbeiten, sondern auch Schwachstellen und potenziell bösartige Events zu überwachen, ist nicht grundsätzlich neu. Doch die Kombination von Security-Überwachung mit Observability-Praktiken eröffnet dabei neue, innovative Wege. Der Begriff Observability meint zunächst das Sammeln von Daten und Events, anhand derer sich jederzeit Fragen zum Zustand der Systeme (Health) stellen lassen. Durch Korrelieren der gesammelten Daten eröffnen sich zudem Einblicke in sogenannte Unknown Unknowns (Dinge, von denen man nicht weiß, dass man sie nicht kennt), die insbesondere auch im Hinblick auf sicherheitsrelevante Vorkommnisse und Abhängigkeiten wertvolle Informationen liefern können.

Sicherheit in Kubernetes-Clustern zu gewährleisten ist ein langwieriges Unterfangen, eine vom Start weg 100-prozentig sichere Konfiguration kaum möglich. Unterstützung erhalten Entwicklerinnen und Entwickler dabei durch einige Open-Source-Projekte, die geeignete Werkzeuge an die Hand geben, um direkte Einblicke in Cloud-native Dienste und die Kommunikation im Cluster zu erhalten.

Das auf die Firma Isovalent zurückgehende OSS-Projekt Cilium zählt hier zu den Vorreitern, die auch den Weg für eBPF geebnet haben. Damit lassen sich viele Anwendungsfälle abbilden, von Visibility über Security bis zur Kontrolle von Netzwerken. Cilium lässt sich unter anderem als Load-Balancer im High-Performance Computing einsetzen, bietet transparente Verschlüsselung, Routing und nicht zuletzt auch Observability (mit dem Hubble-Interface). Letzteres eignet sich für den Einstieg in die Arbeit mit Cilium, bevor es an fortgeschrittenere Themen wie Traffic-Routing und Load-Balancing oder gar Service-Meshes in Kubernetes-Clustern geht.

Mithilfe der Dokumentation lässt sich Cilium einfach in Kubernetes-Clustern installieren, zur Validierung ist lediglich das Cilium CLI (Command Line Interface) nötig. Anstelle des CNI (Container Network Interface) Flannel lässt sich im speziellen Fall eines Civo-Kubernetes-Clusters Cilium auch direkt mit dem Kommando civo kubernetes create ix-ebpf-chaos --cni-plugin cilium als CNI-Plug-in installieren.

Im Rahmen des Cilium-Projekts steht seit Sommer 2022 mit Tetragon ein Werkzeug zur Verfügung, das auf Basis von eBPF bestimmte Syscalls überwachen und bösartige Kommandos unterbinden kann. Mittlerweile hat das Cilium-Team sehr viele Beispielkonfigurationen für Tracing-Policies gesammelt, die sich am Kubernetes-Konfigurationsformat orientieren, wie Listing 5 zeigt. Sie ermöglichen es, bestimmte Typen – etwa den Syscall openat() für Dateizugriffe – oder auch bestimmte Befehle wie cat hinzuzufügen (siehe Listing 6 und Abbildung 6). Mit diesem Wissen lassen sich weitere Policies definieren. Darüber hinaus erlaubt es Tetragon beispielsweise, mit SIGKILL auf Kernel-Ebene entsprechende Operationen direkt zu verhindern. Weitergehende Informationen zu Tetragon stehen in der Dokumentation für Linux und Kubernetes-Cluster bereit.

Listing 5: Tracing-Policy-Konfiguration

$ vim tracing_policy.yaml

apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
  name: "cat-open"
spec:
  kprobes:
  - call: "sys_openat"
    syscall: true
    args:
    - index: 0
      type: "int"
    - index: 1
      type: "string"
    - index: 2
      type: "int"
    selectors:
    - matchBinaries:
      - operator: In
        values:
        - "/usr/bin/cat"

Cilium Tetragon in Docker, mit einer Tracing Policy, die den cat-Befehl ausgibt, wenn Dateien geöffnet werden (Abb. 6).

Listing 6: Tetragon Collector

# Ubuntu 23 VM, mit Docker: https://docs.docker.com/engine/install/ubuntu/

# 1. Terminal: Tetragon Collector
docker run --name tetragon-container --rm --pull always \
    --pid=host --cgroupns=host --privileged             \
    -v $PWD/tracing_policy.yaml:/tracing_policy.yaml    \
    -v /sys/kernel/btf/vmlinux:/var/lib/tetragon/btf    \
    quay.io/cilium/tetragon-ci:latest                   \
    --tracing-policy /tracing_policy.yaml

# 2. Terminal: Tetragon CLI
docker exec tetragon-container tetra getevents -o compact

# 3. Terminal: cat-Befehl
cat /etc/passwd

Tracee von Aqua Security verfolgt einen ähnlichen Ansatz. Das CLI-Werkzeug bietet verschiedene Erkennungsmöglichkeiten in den Bereichen Runtime Security und Forensik. Die Dokumentation zu dem OSS-Tool hält neben Paketen für unterschiedliche Plattformen auch Docker-Container und Kubernetes-Deployments parat.

Falco eröffnet die Möglichkeit, Security-Threats in Echtzeit zu erkennen. Das OSS-Framework lässt sich auch zur Überwachung von Containern einrichten, um etwa darin installierte Paket-Abhängigkeiten zu überwachen. Attacken, die in der Praxis häufig zu finden sind, erfolgen beispielsweise über Manipulationen in der package.json-Konfigurationsdatei für Node.js. Dabei überschreiben Angreifer die script-Sektion mit einem harmlos wirkenden curl-Befehl, der ein Script aus dem Internet herunterlädt, das nach dem curl-pipe-bash-Muster ausgeführt wird. Dadurch öffnet der Befehl npm install ein Einfallstor für Angreifer. Je nach Art der Attacke werden bösartige Reverse-Shells, Rootkits oder auch Bitcoin-Miner im System – und unter Umständen auch der CI/CD-Infrastruktur – installiert. Diese Methode fällt in den Bereich der Software-Supply-Chain-Attacken. Um solchen Angriffen auf die Spur zu kommen, ist unter anderem der GitLab Package Hunter für Falco entstanden.