🎉 DevOps Interview Prep Bundle is live — 1000+ Q&A across 20 topicsGet it →
All Articles

Build an AI Runtime Anomaly Detector for Kubernetes with Claude API and eBPF

eBPF gives you a live syscall-level view of what's happening inside every container. Build a tool that feeds that stream through Claude API to catch anomalous process behavior — crypto miners, reverse shells, unexpected file access — that signature-based tools miss.

Shubham4 min read
Share:Tweet

Signature-based runtime security tools (rule "if process X spawns process Y, alert") catch known attack patterns and miss everything novel. eBPF gives you the raw syscall stream — process execs, network connections, file access — for every container in real time. This tool feeds behavioral summaries of that stream through Claude to catch what a signature can't: genuinely novel or subtly disguised anomalous behavior.

Architecture

Container syscalls → eBPF probe (Tetragon/Falco) → structured events
                                                          ↓
                                    Behavioral summarizer (batches events per pod)
                                                          ↓
                                    Claude reasons over the behavior pattern
                                                          ↓
                                    "normal" / "suspicious" / "malicious" + evidence

eBPF Event Collection (via Tetragon)

yaml
# TracingPolicy — capture process execs and network connections per pod
apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
  name: runtime-behavior-monitor
spec:
  kprobes:
    - call: "sys_execve"
      syscall: true
      args:
        - index: 0
          type: "string"
    - call: "tcp_connect"
      syscall: false
      args:
        - index: 0
          type: "sock"
python
import json
import subprocess
from collections import defaultdict
 
def stream_tetragon_events():
    """Consume the Tetragon gRPC/JSON event stream."""
    proc = subprocess.Popen(
        ["tetra", "getevents", "-o", "json"],
        stdout=subprocess.PIPE, text=True
    )
    for line in proc.stdout:
        yield json.loads(line)

Behavioral Summarization — Don't Send Raw Events to Claude

Raw syscall streams are enormous and mostly noise — the summarization step is what makes this tractable and cheap.

python
def summarize_pod_behavior(pod_name: str, events: list[dict], window_seconds: int = 60) -> dict:
    """Turn a raw event stream into a compact behavioral summary."""
    process_execs = [e for e in events if e.get("process_exec")]
    network_connects = [e for e in events if e.get("process_connect")]
 
    return {
        "pod": pod_name,
        "window_seconds": window_seconds,
        "unique_processes_spawned": list(set(e["process_exec"]["binary"] for e in process_execs)),
        "network_destinations": list(set(e["process_connect"]["destination"] for e in network_connects)),
        "unusual_exec_count": len(process_execs),
        "sample_exec_chain": [e["process_exec"]["binary"] for e in process_execs[:20]],
    }

Claude Behavioral Analysis

python
import anthropic
 
client = anthropic.Anthropic()
 
ANALYZE_PROMPT = """Analyze this container's runtime behavior for anomalies.
 
Pod: {pod}
Time window: {window_seconds}s
Processes spawned: {processes}
Network destinations contacted: {destinations}
Process exec chain sample: {exec_chain}
 
Baseline expected behavior for this pod (from its normal operating pattern
over the last 7 days): {baseline}
 
Flag as suspicious or malicious if you see patterns like:
- Process spawning a shell (sh, bash) when the app normally never does
- Network connections to destinations outside the known service mesh/allowlist
- Binaries in /tmp or unusual paths being executed
- Cryptomining indicators (sustained high CPU exec chains to mining-pool-like destinations)
- Reconnaissance patterns (whoami, id, cat /etc/passwd, uname -a in sequence)
 
Respond with ONLY valid JSON:
{{"verdict": "normal"|"suspicious"|"malicious", "confidence": "high"|"medium"|"low",
  "evidence": "specific reasoning", "recommended_action": "..."}}"""
 
 
def analyze_behavior(summary: dict, baseline: dict) -> dict:
    response = client.messages.create(
        model="claude-sonnet-5",
        max_tokens=500,
        messages=[{
            "role": "user",
            "content": ANALYZE_PROMPT.format(
                pod=summary["pod"], window_seconds=summary["window_seconds"],
                processes=summary["unique_processes_spawned"],
                destinations=summary["network_destinations"],
                exec_chain=summary["sample_exec_chain"],
                baseline=baseline,
            )
        }]
    )
    text = response.content[0].text.strip()
    if text.startswith("```"):
        text = text.split("```")[1].replace("json", "", 1).strip()
    import json
    return json.loads(text)

Response Loop

python
def monitor_and_respond():
    events_by_pod = defaultdict(list)
 
    for event in stream_tetragon_events():
        pod = event.get("pod_name", "unknown")
        events_by_pod[pod].append(event)
 
        if len(events_by_pod[pod]) >= 50:    # Batch, don't analyze every single event
            summary = summarize_pod_behavior(pod, events_by_pod[pod])
            baseline = get_pod_baseline(pod)
            verdict = analyze_behavior(summary, baseline)
 
            if verdict["verdict"] == "malicious" and verdict["confidence"] == "high":
                isolate_pod(pod)    # NetworkPolicy that blocks all egress
                page_security_oncall(pod, verdict["evidence"])
            elif verdict["verdict"] in ("suspicious", "malicious"):
                notify_security_channel(pod, verdict)
 
            events_by_pod[pod] = []    # Reset window

Why Behavioral Baselines Matter More Than Generic Rules

A payments service that never spawns a shell process in its normal operation, suddenly spawning sh -c after a request, is a strong signal regardless of what that shell does next. A generic rule-based tool without per-pod baselines either misses this (too permissive) or false-positives constantly on services that legitimately shell out (too strict). Building and maintaining per-pod behavioral baselines — what's normal for this specific workload — is the actual hard engineering problem here; the Claude analysis step is only as good as the baseline it's comparing against.

python
def build_baseline(pod_label: str, days: int = 7) -> dict:
    """Run this on a schedule against historical event data to keep
    baselines current as legitimate app behavior evolves."""
    historical_events = query_historical_events(pod_label, days=days)
    return {
        "normal_processes": extract_common_processes(historical_events),
        "normal_destinations": extract_common_destinations(historical_events),
    }

More AI security tooling? Read our Falco vs Tetragon runtime security comparison and Autonomous security patch triage agent.

🔧

Today I Fixed

Short real fixes from production — posted daily

Browse fixes
Newsletter

Stay ahead of the curve

Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam — just practical engineering content.

Related Articles

Comments