Skip to content

Falco

Falco is a runtime security tool that detects unexpected behavior in containers and Kubernetes workloads by observing Linux system calls. Where admission controllers and policy engines prevent misconfiguration at deploy time, Falco catches active threats during runtime - a compromised container spawning a shell, a process reading credential files, or a container escaping to the host.

The core insight: most attacks, regardless of how they start, ultimately make recognizable system calls. A cryptominer executes binaries. An attacker exfiltrating data opens network connections. Privilege escalation touches /proc or suid binaries. Falco's detection model sits at this syscall layer.

Architecture

flowchart TD
    Kernel[Linux kernel\nsyscalls] --> Driver{Driver}
    Driver --> |kernel module| KernelModule[Falco kernel module]
    Driver --> |eBPF| EBPF[eBPF probe\nCO-RE / modern eBPF]
    KernelModule --> Falco[falco process]
    EBPF --> Falco
    Falco --> |evaluate| Rules[Rules engine]
    Rules --> |alert| Output[Alert outputs]
    Output --> Stdout[stdout / syslog]
    Output --> Webhook[HTTP webhook]
    Output --> Sidekick[Falcosidekick]
    Sidekick --> Slack[Slack]
    Sidekick --> SIEM[Splunk / Elastic]
    Sidekick --> PD[PagerDuty]
    Sidekick --> Lambda[AWS Lambda]

Driver options:

  • Kernel module (falco-driver): compiles against the running kernel headers. Highest compatibility. Requires privileged access.
  • eBPF probe (classic): loads a BPF program. More portable than kernel module, needs kernel 4.14+.
  • Modern eBPF (CO-RE): uses CO-RE (Compile Once, Run Everywhere) - no kernel headers needed at runtime. Recommended for kernels 5.8+. Minimal footprint, best performance.
helm install falco falcosecurity/falco \
  --namespace falco --create-namespace \
  --set driver.kind=modern_ebpf \
  --set falcosidekick.enabled=true \
  --set falcosidekick.webui.enabled=true

Rules language

A Falco rule has five parts: condition, output, priority, name, and optional list/macro references.

- rule: Shell in Container
  desc: A shell was spawned in a container that should not have one.
  condition: >
    spawned_process
    and container
    and not container.image.repository in (allowed_shell_containers)
    and proc.name in (shell_binaries)
  output: >
    Shell spawned in container
    (user=%user.name container=%container.name image=%container.image.repository
    shell=%proc.name parent=%proc.pname cmdline=%proc.cmdline)
  priority: WARNING
  tags: [container, shell, attack]

Macros

Macros are reusable condition fragments:

- macro: spawned_process
  condition: evt.type = execve and evt.dir = <

- macro: container
  condition: container.id != host

- macro: shell_binaries
  condition: proc.name in (bash, sh, zsh, dash, fish, ksh)

Lists

Lists are reusable sets of values:

- list: allowed_shell_containers
  items:
    - "my-debug-toolbox"
    - "ci-runner"

- list: shell_binaries
  items: [bash, sh, zsh, dash, fish, csh, ksh, tcsh]

Default rule categories

Falco ships with rules covering:

Category Examples
Container escape mount of host paths, namespace join, ptrace on host pids
Credential access reading /etc/shadow, AWS credentials files, service account tokens
Execution shell in container, unexpected binary execution, execve of scripting engines
Network unexpected outbound connections, reverse shells (connect+write to stdout)
Filesystem writing to /etc, /usr, binary directories
Kubernetes kubectl exec on a pod, API server anomalies
Cryptomining known miner process names, CPU-intensive process names

Writing custom rules

Detect unexpected outbound connections

- rule: Unexpected Outbound Connection
  desc: Container established an outbound connection to an unexpected destination.
  condition: >
    outbound
    and container
    and not fd.sip in (allowed_outbound_ips)
    and not fd.sport in (allowed_outbound_ports)
    and container.image.repository != "legitimate-egress-service"
  output: >
    Unexpected outbound connection
    (user=%user.name container=%container.name image=%container.image.repository
    connection=%fd.name)
  priority: WARNING
  tags: [network, container]

Detect reading of Kubernetes secrets from disk

- rule: Container Reading K8s Secret File
  desc: A process in a container read a mounted Kubernetes secret file.
  condition: >
    open_read
    and container
    and fd.name startswith /var/run/secrets/kubernetes.io/serviceaccount/
    and not proc.name in (allowed_sa_readers)
  output: >
    Process reading service account token
    (user=%user.name container=%container.name proc=%proc.name file=%fd.name)
  priority: WARNING
  tags: [k8s, credentials]

Detect privilege escalation via SUID binary

- rule: SUID/SGID Binary Execution
  desc: An SUID or SGID binary was executed in a container.
  condition: >
    spawned_process
    and container
    and proc.is_suid_exe = true
  output: >
    SUID binary executed in container
    (user=%user.name proc=%proc.name exe=%proc.exe container=%container.name)
  priority: CRITICAL
  tags: [container, privilege-escalation]

Rule tuning and noise reduction

Out of the box, Falco is noisy. Production deployment requires tuning. The workflow:

  1. Start in -d (dry-run / stdout) mode. Do not route to alerting yet.
  2. Let it run for 24-48 hours. Collect all rule hits.
  3. For each noisy rule, either tighten the condition or add exceptions via lists.
  4. Promote to alerting only rules with a clear signal-to-noise ratio.
# Suppress a known-noisy rule for a specific image
- rule: Read sensitive file untrusted
  append: true       # extends the existing rule condition
  condition: >
    and not container.image.repository = "my-legacy-app"

Falcosidekick

Falcosidekick is a fan-out router for Falco alerts. Falco sends JSON events to Falcosidekick's webhook; Falcosidekick routes them to 50+ destinations simultaneously.

# Falcosidekick values for Helm install
config:
  slack:
    webhookurl: "https://hooks.slack.com/services/..."
    minimumpriority: WARNING

  pagerduty:
    routingKey: "your-routing-key"
    minimumpriority: CRITICAL

  elasticsearch:
    hostport: http://elastic:9200
    index: falco
    minimumpriority: DEBUG   # send everything to SIEM

  aws:
    lambda:
      functionname: auto-respond-falco
      minimumpriority: CRITICAL
    sns:
      topicarn: arn:aws:sns:us-east-1:123456789:falco-alerts
      minimumpriority: WARNING

The Lambda integration is particularly powerful: trigger automated response (quarantine a pod, revoke credentials, snapshot a volume for forensics) on critical Falco events.

Falco Talon - automated response

Falco Talon is a purpose-built response engine for Falco events. Define what to do when a rule fires:

# rules.yaml for Falco Talon
- action: Kill Pod
  match:
    rules:
      - "Shell in Container"
      - "Execution from /tmp"
  parameters:
    graceful_period_seconds: 5

- action: Label Pod
  match:
    rules:
      - "Unexpected Outbound Connection"
  parameters:
    labels:
      quarantine: "true"
      quarantine-reason: "unexpected-outbound"

Response actions: kill pod, label pod, network policy (isolate), Kubernetes delete, AWS Lambda invocation.

Kubernetes audit events

In addition to syscalls, Falco can ingest Kubernetes audit log events via the k8saudit plugin:

# Detect kubectl exec
- rule: Attach/Exec Pod
  desc: An exec or attach was performed on a pod.
  source: k8saudit
  condition: >
    ka.target.resource = pods
    and ka.verb in (exec, attach)
    and not ka.user.name in (allowed_exec_users)
  output: >
    Exec/Attach to pod
    (user=%ka.user.name pod=%ka.target.name ns=%ka.target.namespace
    container=%ka.req.pod.containers.image)
  priority: WARNING

This requires configuring the Kubernetes API server to send audit events to Falco's webhook.

Operational patterns

Priority mapping: Falco priorities map to NIST and ATT&CK severity. Use CRITICAL only for immediate containment scenarios (active shell in prod, crypto miner detected). WARNING for investigation-required events. INFORMATIONAL for baseline behavior logging to SIEM.

Metrics: Falco exposes Prometheus metrics at :8765/metrics - falco_events_total by rule and priority. Alert on sudden spikes in a rule's event count.

Rules versioning: manage rules as ConfigMaps in Helm, committed to Git. Never edit rules directly on a running Falco instance.

Kernel upgrades: the kernel module must be rebuilt for every kernel version. In managed Kubernetes (EKS, GKE, AKS), use the modern eBPF driver (CO-RE) to avoid rebuilds on node OS upgrades.