Your control-plane logs tell you who called an API. They tell you nothing about what happened inside the process that API call started.
The blind spot in cloud-native security logging
Most SIEM programs are built almost entirely on control-plane telemetry: cloud audit logs, identity provider events, load balancer access logs. That data answers “who did what to which resource.” It does not answer “what is running inside my containers right now.”
An attacker who gets remote code execution in a workload — through a vulnerable dependency, a misconfigured job, or a compromised CI pipeline — doesn’t need to touch the cloud API at all to do damage. They can read environment variables, scrape a mounted credential file, spawn a shell, or reach out to a command-and-control server, and none of it shows up in CloudTrail-style audit logs. By the time that activity produces an API call worth alerting on (assuming a stolen credential is even used), the attacker has already had the run of the container.
This is the gap runtime security closes: instrumenting the kernel itself so you see process execution, file access, and network activity as they happen, independent of what any application chooses to log.
Why eBPF changed what’s practical here
Kernel-level visibility used to mean a kernel module: custom C code running in kernel space, one crash away from taking down the host, and rebuilt for every kernel version you supported. That tradeoff kept most teams out of runtime monitoring entirely.
eBPF (extended Berkeley Packet Filter) changes the economics. It lets you run small, verified programs inside the kernel — attached to syscalls, network events, or scheduler hooks — without writing a kernel module. The in-kernel verifier rejects anything that could crash or hang the kernel before it loads, and the programs are portable across kernel versions far more easily than a compiled module ever was. That’s why eBPF has become the foundation for a wave of open-source runtime security tools over the last few years, and why it’s now practical for teams well outside the hyperscalers to run.
The open-source landscape
| Tool | What it’s good at | Maturity |
|---|---|---|
| Falco | Real-time rule-based detection on syscalls (CNCF graduated project) | Production-ready, large rule community |
| Tetragon | eBPF-based enforcement + observability, can block not just alert | Production-ready, tighter Kubernetes integration |
| Tracee | Syscall tracing with a built-in signature engine, lighter footprint | Actively developed, smaller community |
All three ship a default rule set covering common attacker behavior — shell spawned in a container, write to a sensitive path like /etc/shadow, outbound connection from an unexpected binary. Start with the defaults. Tune from there.
How to instrument a cluster: a walkthrough
The example below uses Falco’s rule syntax because it’s the most widely deployed, but the concepts (a rule against a stream of kernel events, tagged with process, container, and network context) apply to any of the three tools.
Step 1 — Deploy the agent. All three run as a DaemonSet, one instance per node, reading events directly from the kernel via eBPF probes. No sidecar per pod, no application changes required.
# conceptual daemonset shape — adapt to your tool of choice
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: runtime-sensor
spec:
template:
spec:
hostPID: true
containers:
- name: sensor
image: <runtime-security-tool>:latest
securityContext:
privileged: true # required for eBPF probe attachment
volumeMounts:
- name: kernel-headers
mountPath: /usr/src
Step 2 — Write a rule for a behavior you actually care about. Here’s a Falco-style rule that fires when a shell is spawned inside a container that has no business running one:
- rule: Unexpected shell in container
desc: A shell was spawned inside a container outside of the allowed exec list
condition: >
spawned_process
and container
and proc.name in (bash, sh, zsh)
and not container.image.repository in (allowed_shell_images)
output: >
Shell spawned in container
(user=%user.name container=%container.name image=%container.image.repository
command=%proc.cmdline parent=%proc.pname)
priority: WARNING
tags: [container, shell, mitre_execution]
Step 3 — Add process lineage, not just the event. A single syscall event is weak signal. The same event with full process ancestry — parent process, container image, pod name, namespace — is enough to triage without a follow-up query. Every mature runtime tool attaches this context automatically; make sure it’s actually being captured and not stripped before it leaves the node.
Step 4 — Get sensitive file access covered. Credential theft is one of the highest-value detections runtime tooling gives you that control-plane logs can’t touch at all:
- rule: Read of cloud credential file
desc: Process read a well-known cloud credential path
condition: >
open_read
and fd.name in (/root/.aws/credentials, /var/run/secrets/kubernetes.io/serviceaccount/token)
and not proc.name in (allowed_credential_readers)
output: >
Sensitive credential file read
(file=%fd.name process=%proc.name container=%container.name)
priority: CRITICAL
Getting runtime events into your detection pipeline
Runtime tools are noisy in isolation and most valuable correlated with everything else you already collect. The pattern that works well, described generically:
kernel event (eBPF probe)
-> runtime agent enriches with process/container/pod context
-> event shipped to your log pipeline in a normalized shape
-> stored alongside cloud audit and identity events
-> correlation layer: same principal, same time window, same workload
-> alert only on the correlated finding, not the raw kernel event
The correlation step is what turns “a shell was spawned in a pod” (happens constantly in normal ops, especially in dev) into “a shell was spawned in a pod immediately after that pod’s service account made an unusual API call” — which is a very different alert priority. Concretely:
if runtime_event.type == "shell_spawned"
and cloud_event.principal == runtime_event.service_account
and abs(cloud_event.timestamp - runtime_event.timestamp) < 5 minutes
and cloud_event.action in sensitive_api_actions:
raise_alert(severity=critical, correlated=true)
None of this requires a specific storage engine or query language — any pipeline that can normalize both event types into a common schema and join on principal + time window will do it. If you’re already normalizing multi-vendor logs into a common schema, add runtime events as just another source.
Detection ideas worth building first
| Behavior | MITRE ATT&CK | Why it’s high value |
|---|---|---|
| Shell spawned in a container with no interactive-use case | T1059 - Command and Scripting Interpreter | Near-zero false positives in most production workloads |
| Read of a cloud credential file by an unexpected process | T1552.001 - Credentials In Files | Catches credential theft before it becomes an API-based attack |
| Outbound connection from a binary that never makes network calls | T1071 - Application Layer Protocol | Flags C2 beaconing regardless of destination reputation |
| Write to a container’s read-only root filesystem path | T1611 - Escape to Host | Signals an active escape attempt, not just a misconfiguration |
New binary executed from /tmp or another writable, non-image path | T1105 - Ingress Tool Transfer | Catches malware dropped after initial access |
Start with these five. They map to common attacker behavior, generate low noise against a normal workload baseline, and don’t require you to already know what you’re looking for the way anomaly-based rules do.
Where it falls short
Runtime tooling isn’t a replacement for control-plane logging — it’s the layer underneath it. It won’t tell you an IAM policy was changed or a storage bucket was made public; that’s still audit-log territory. It also generates real operational load: expect to spend real tuning time getting a rule set from “technically correct” to “doesn’t page someone at 2 a.m. for a routine deploy.” Budget for a baselining period of two to four weeks per environment before you route these alerts anywhere that pages a human.
Final thought
Cloud-native attacks increasingly live entirely inside the workload, and workload-only visibility is exactly what most security programs are missing. eBPF-based runtime tooling is mature enough now, and cheap enough operationally, that there’s little excuse to leave that layer dark. Start with the shell-execution and credential-read rules above — they’re the highest signal-to-noise detections you can add this week.
If you want help wiring runtime detections into a pipeline that already correlates identity, cloud, and application logs, contact us.