# Falco Cheat Sheet

> **Tool:** Falco (+ falcoctl)
> **Category:** Security & Compliance
> **Verified against:** Falco 0.44.1 / falcoctl 0.13.0, installed via the official apt repo and flags verified
> via `falco --help` / `falcoctl --help` run locally, 2026-08-29
> **Official docs:** https://falco.org/docs/

## What it is and where it fits 🎯

Every other tool in this Security & Compliance section is **prevention-focused**: it scans code, images, IaC,
or dependencies *before* something ships, looking for a known-bad pattern. Falco is different — it's
**behavior-based runtime security**, watching a live system's actual syscalls for anomalous activity: an
unexpected shell spawned inside a container, a sensitive file read, a privilege escalation. This is the
detection layer that catches a genuinely novel attack no scanner could have flagged in advance, because
there's no signature to match against — only "this doesn't look like how this workload normally behaves."
Falco runs as a long-lived daemon/DaemonSet, not a one-shot scan; `falcoctl` is its separate CLI for
managing rules, drivers, and OCI-distributed rule bundles.

## Where runtime detection fits relative to everything upstream

```mermaid
flowchart LR
    A["Semgrep / SonarQube<br/>(SAST)"] --> E["Deploy"]
    B["Trivy / Grype<br/>(image scan)"] --> E
    C["Checkov / tfsec<br/>(IaC scan)"] --> E
    D["OPA / Conftest<br/>(admission policy)"] --> E
    E --> F["Falco<br/>(runtime detection)"]
    F -->|"anomaly found"| G["🚨 Alert: investigate now"]

    classDef info fill:#e5f0fa,stroke:#1d6fb8,color:#10161c
    classDef crit fill:#fbe8e6,stroke:#b3261e,color:#10161c
    class A,B,C,D info
    class G crit
```

Everything to the left of `Deploy` is prevention — stop the bad thing from shipping. Falco is the only tool
here that assumes something might get through anyway and watches for it happening live.

## Installation (Debian/Ubuntu)

```bash
curl -fsSL https://falco.org/repo/falcosecurity-packages.asc \
  | sudo gpg --dearmor -o /usr/share/keyrings/falco-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/falco-archive-keyring.gpg] https://download.falco.org/packages/deb stable main" \
  | sudo tee /etc/apt/sources.list.d/falcosecurity.list
sudo apt-get update
sudo apt-get install -y falco

falco --version
falcoctl version
```

RPM-based distros use the equivalent yum/dnf repo (see the official docs); a Helm chart is the standard install
path for Kubernetes:

```bash
helm repo add falcosecurity https://falcosecurity.github.io/charts
helm install falco falcosecurity/falco -n falco --create-namespace
```

> [!IMPORTANT]
> Post-install, Falco auto-configures a driver type (`modern_ebpf` by default on recent kernels) via
> `falcoctl driver config` — a kernel-driver/eBPF probe is genuinely required for live syscall capture. A
> locked-down sandbox or container without kernel module access can install and configure Falco fine, but
> won't receive real live events — this isn't a bug, it's the fundamental mechanism Falco depends on to see
> syscalls at all.

## Running Falco

```bash
falco                                    # foreground, using the configured driver + default ruleset
falco -c /etc/falco/falco.yaml            # explicit config file
falco --dry-run                           # validate config and rules without processing events — good for CI-lint of a rules change
falco -M 60                                # stop after 60 seconds — useful for a bounded test run
```

## Inspecting rules and fields

```bash
falco -L                                  # list every loaded rule, name + description
falco -l "Terminal shell in container"     # show one rule's full detail
falco --list                               # list every field available for use in rule conditions
falco --list-events                        # list every syscall/tracepoint event source
falco --list-plugins                        # show loaded plugins
```

Real captured `falco --list-plugins` output from this cheat sheet's own verification run (a fresh apt install,
one plugin loaded by default):

```
1 Plugins Loaded:

Name: container
Description: Falco container metadata enrichment Plugin
Contact: github.com/falcosecurity/plugins
Version: 0.7.1
Capabilities:
  - Field Extraction
  - Event Parsing
  - Async Events
```

## Writing a custom rule

```yaml
# /etc/falco/rules.d/custom.yaml
- rule: Unexpected shell in checkout service
  desc: A shell was spawned inside the checkout container — should never happen
  condition: >
    spawned_process and container.name = "checkout" and proc.name in (bash, sh, zsh)
  output: >
    Shell spawned in checkout container (user=%user.name command=%proc.cmdline container=%container.name)
  priority: WARNING
```

```bash
falco --dry-run -r /etc/falco/rules.d/custom.yaml     # validate the new rule file before deploying it
```

> [!TIP]
> **Scope custom rules to a specific workload (`container.name = "checkout"`) rather than writing a global
> rule whenever possible.** A rule like "no shell spawned in any container, ever" sounds appealing but breaks
> the moment any team's legitimate debugging workflow (an `kubectl exec` into a pod during an incident) needs
> a shell — scoping to the specific workloads that genuinely should never see one avoids that false-positive
> fatigue, which is the fastest way a team starts ignoring Falco alerts entirely.

## falcoctl — managing rules and drivers as OCI artifacts

```bash
falcoctl artifact install falco-rules:latest          # pull the official rules bundle from the default OCI index
falcoctl driver config --type modern_ebpf              # (re)configure which driver type to use
falcoctl driver install                                  # install/load the configured driver
falcoctl registry pull oci://ghcr.io/org/falco-rules:latest    # pull a custom rules bundle from your own registry
```

## Output config (config.d snippets)

```yaml
# /etc/falco/config.d/output.yaml
json_output: true
stdout_output:
  enabled: true
```

Combine with `-o append_output.jsonoutput=true` at the CLI, or `-pk`/`-pc` to append Kubernetes/container
context onto every alert line — critical in a multi-tenant cluster where "a shell was spawned somewhere" is
meaningless without knowing which namespace/pod/workload it happened in.

## Real-world scenario: the checkout-service anomaly this series' own tutorial opens with

An on-call engineer gets paged for "Terminal shell in container" on the checkout service — a workload that
has no legitimate reason for anyone to open an interactive shell inside it. The temptation is to dismiss it as
noise from a routine debugging session.

> [!CAUTION]
> **Investigate before dismissing.** This is exactly the class of anomalous, behavior-based signal that
> prevention-focused tooling (scanning, RBAC) can't provide — it's specifically what runtime detection exists
> to surface. Check recent access/audit logs for who or what triggered it, and treat it as a potential active
> compromise until proven otherwise. Dismissing a genuinely novel, behavior-based security signal without
> investigation is exactly how real intrusions go undetected — the entire value of a tool like Falco is lost
> the moment a team trains itself to auto-dismiss its alerts.

## Common pitfalls

- **Assuming Falco "isn't working" in a sandboxed/CI environment** — see the driver requirement above; no
  kernel access means no live events, by design, not by bug.
- **Global, unscoped rules** causing alert fatigue — see the TIP above.
- **Treating a Falco alert as low-priority by default** — unlike a scan finding (which represents a
  *potential* issue caught before it shipped), a Falco alert represents something that **actually happened**
  on a running system, which generally warrants faster triage than a static-analysis finding.

## When to reach for something else

Falco is the only runtime-detection tool in this series — there's no direct substitute among the others here.
It's a complement to, never a replacement for, the prevention-focused scanners (Trivy/Grype/Checkov/tfsec/OPA)
earlier in the pipeline; a mature security posture runs both layers, not one instead of the other.
