Lesson 2 of 5 · 25 min read

eBPF for Security — Falco, Tetragon, and What Syscalls Don't Show

auditd captures kernel syscalls. But modern cloud workloads — containers, microservices, serverless functions — run in environments where auditd’s static rule model struggles. eBPF-based tools like Falco and Tetragon extend security visibility into these environments with behavioral assertions rather than static syscall rules.


Why eBPF Changes Security Monitoring

Traditional kernel tracing (auditd, strace) works by intercepting specific syscalls. eBPF goes further:

Syscall (execve, connect, open)


Kernel eBPF program ─── filters in-kernel ──→ drops if not interesting

        │ (only interesting events)

Ring buffer → userspace consumer (Falco, Tetragon, custom)


Security event with full context

Falco: Behavioral Runtime Detection

Falco is CNCF (Cloud Native Computing Foundation) project that uses eBPF (or a kernel module) to detect suspicious runtime behavior. Its rules are written in a YAML DSL:

# Falco rule: shell spawned inside a container
- rule: Terminal shell in container
  desc: A shell was used as the entrypoint/exec point into a container with an attached terminal.
  condition: >
    spawned_process
    and container
    and shell_procs
    and proc.tty != 0
  output: >
    A shell was spawned in a container with an attached terminal 
    (user=%user.name %container.info shell=%proc.name parent=%proc.pname 
    cmdline=%proc.cmdline terminal=%proc.tty container_id=%container.id 
    image=%container.image.repository)
  priority: NOTICE
  tags: [container, shell, mitre_execution]

Key Falco rule macros

# Built-in macros you'll use in custom rules

# Is this a container?
container: container.id != host

# Spawned a new process?
spawned_process: evt.type = execve and evt.dir = <

# Is this a shell?
shell_procs: proc.name in (ash, bash, csh, ksh, sh, tcsh, zsh, dash)

# Is this a known sensitive file?
sensitive_files: (
  fd.name startswith /etc/shadow or
  fd.name startswith /etc/sudoers or
  fd.name startswith /root/.ssh
)

Falco event output example

{
  "time": "2024-03-15T09:23:14.882Z",
  "rule": "Terminal shell in container",
  "priority": "NOTICE",
  "output_fields": {
    "container.id": "abc123def456",
    "container.image.repository": "company/webapi",
    "container.image.tag": "v2.1.3",
    "k8s.pod.name": "webapi-7d9b8c-xk2np",
    "k8s.ns.name": "production",
    "proc.name": "bash",
    "proc.cmdline": "bash",
    "proc.pname": "python3",
    "user.name": "www-data",
    "proc.tty": 34816
  }
}

What this tells you: bash was spawned inside the production webapi container, parent process was python3, user is www-data. The Kubernetes context (pod name, namespace) is attached automatically. This is a web shell execution in a container.


Tetragon: eBPF with Enforcement

Tetragon extends eBPF security from detection to enforcement. Where Falco sends an alert and waits for a human response, Tetragon can terminate the offending process in-kernel before the malicious action completes.

# Tetragon TracingPolicy: kill any process that reads /etc/shadow outside /usr/sbin/
apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
  name: protect-shadow
spec:
  kprobes:
    - call: "security_file_open"
      syscall: false
      args:
        - index: 0
          type: "file"
      selectors:
        - matchArgs:
            - index: 0
              operator: "Postfix"
              values:
                - "/etc/shadow"
          matchBinaries:
            - operator: "NotIn"
              values:
                - "/usr/sbin/unix_chkpwd"
                - "/usr/bin/passwd"
          matchActions:
            - action: Sigkill  # ← terminate the offending process

This policy kills any process that tries to open /etc/shadow unless it’s unix_chkpwd or passwd — both legitimate.

Falco Documentation— CNCF Falco Tetragon Documentation— Cilium Tetragon