Image scanning tells you a container includes a library with a known CVE. It does not tell you that a process in that container just spawned a shell, read a service account token, and opened a connection to an address nobody has ever contacted before. The first is a list of possibilities; the second is an event.
eBPF closed that gap by making kernel-level observation practical: programs attached to syscalls and kernel functions, verified for safety before loading, running with low overhead and no kernel modules. Every container on the node becomes observable from below, regardless of language, runtime, or whether the application was instrumented.
Why the vantage point matters
Application-level logging can be disabled, bypassed, or simply absent in the code path an attacker uses. A sidecar sees network traffic but not process activity. The kernel sees everything that actually happened — every exec, every file open, every socket — and an attacker operating inside a container cannot avoid it without compromising the node itself.
Equally important, the identity context is right there. The same event carries the pod, namespace, service account and image, so a detection can be expressed in the terms your platform uses rather than in PIDs and inode numbers that mean nothing at three in the morning.
# The highest-signal detection in a container environment, by some distance:
# an interactive shell in a service container. Legitimate code does not do this.
- rule: Shell spawned in service container
desc: An interactive shell was executed inside a workload container
condition: >
spawned_process and container
and proc.name in (bash, sh, zsh, dash, busybox)
and not container.image.repository in (debug_images)
output: >
Shell in container (user=%user.name pod=%k8s.pod.name ns=%k8s.ns.name
image=%container.image.repository cmd=%proc.cmdline parent=%proc.pname)
priority: CRITICAL
# Second highest: reading the service account token from a process that is not
# the application's own startup path. This is lateral movement, mid-flight.
- rule: Service account token read by unexpected process
condition: >
open_read and container
and fd.name startswith /var/run/secrets/kubernetes.io/serviceaccount
and not proc.name in (app_entrypoints)
priority: WARNINGNoise is the reason these programmes fail
Default rule sets generate thousands of alerts a day in a busy cluster, every one of them technically correct and almost all of them your own tooling. Within two weeks the channel is muted, and you have bought an expensive way to not notice a breach. The discipline is to start with three detections you would genuinely wake up for, tune them to near-zero false positives, and only then add the fourth.
- Start with exec of a shell, reads of credential paths, and outbound connections to addresses outside your egress allowlist.
- Tune by workload identity, not by process name. Excluding bash globally deletes the detection; excluding it for one known job does not.
- Send detections to the incident pipeline, not to a chat channel. An alert nobody owns is a log line with ambitions.
- Test your detections with an actual attack simulation. An untested rule is an assumption about syscalls.
- Watch the overhead on high-throughput nodes; it is low but not zero, and a badly scoped program on a busy syscall is measurable.
A detection that fires forty times a day is not a detection. It is a filter someone will eventually write, and it will filter out the real one.
Where this pays off beyond alerting is forensics. When something does happen, the difference between having a kernel-level record of every process and connection in that pod and reconstructing events from application logs written by the code the attacker was abusing is the difference between an afternoon and a fortnight. Even if you never enable a single blocking policy, the recording alone justifies the deployment.