Falco
Falco is the tool the previous page kept pointing at without ever opening up: a CNCF-graduated, eBPF-based runtime security engine that watches every syscall on a node, matches each one against a loaded set of detection rules, and raises a priority-scored alert the instant something matches — a shell spawned where one has never run before, a container mounting /var/run/docker.sock, a process reading a ServiceAccount token it has no business touching. Sysdig built it, donated it to the CNCF in 2018, and it graduated the foundation in early 2022 as the project's first runtime security tool to reach that tier. By the end of this page you should be able to install Falco with its driver correctly matched to a node's kernel, read and write a rule in its actual syntax, explain precisely what its default ruleset already catches around shell-in-container and sensitive-mount patterns, extend that ruleset without forking it, and route its alerts somewhere a human — or a SIEM — actually looks, instead of a stdout stream nobody tails.
A smoke detector that beeps into an empty room nobody's in isn't really protecting the house — it's just making noise where no one can hear it. Falco is a smoke detector built to sit right next to the thing it's watching, in every room at once, wired so it can actually ring a phone somewhere when it goes off — instead of quietly logging the beep to a notebook that only gets read after the fire's already out.
What Falco is, and the problem it solves
☺ Like you're 10: it's a program that sits right inside the kernel's own checkpoint and shouts the second something crosses a line you told it to watch for.
Falco started at Sysdig, built on the same syscall-capture library — libscap and libsinsp — that already powered Sysdig's own system-inspection tooling, and was open-sourced in 2016. In 2018 Sysdig donated the project to the Cloud Native Computing Foundation, where it became the foundation's first dedicated runtime security project; it moved from sandbox to incubating in 2020 and graduated in early 2022 — worth confirming against the current CNCF landscape if the exact date matters for a compliance narrative, but the tier itself puts Falco alongside Kubernetes, Prometheus, and containerd.
What it actually is, precisely: a rules engine that consumes a stream of enriched kernel events — syscalls captured via one of three driver generations covered next, plus, since the plugin framework landed, non-syscall sources like a Kubernetes audit log — and evaluates every single one against whatever ruleset is loaded, written in a small YAML-based condition language. A match produces a priority-scored alert carrying exactly the fields the rule's output template asked for: which container, which process, which command line, which file, which remote address. That's the whole job. Falco has no opinion about what happens to an alert once it's raised — routing, paging, and automated response are separate, composable layers this page covers later, and Falco stays useful specifically because it doesn't try to own all of them itself.
What it's not, worth stating plainly: it doesn't block anything by itself — every rule in the default set is a detection, not a prevention, unless it's paired with a response layer like Falco Talon (see Container Runtime Security for where that sits) or a write-time control like Kyverno that acts before the event Falco is watching for ever happens. And it's not a vulnerability scanner — Trivy and Syft & Grype already covered what's wrong with an image before it ships; Falco covers what a container actually does once it's running, which is the structurally different question the previous page's whole argument was built around.
Falco's entire value proposition compresses to one sentence: it turns "we have logs somewhere" into "we have a rule that fires the instant this specific pattern happens, and an alert that lands where a human is already looking." Everything else on this page — drivers, plugins, the rule syntax, Falcosidekick — exists to make that sentence hold up under a real production load without drowning whoever's on call.
Architecture: from syscall to alert
☺ Like you're 10: the previous page already drew the syscall-to-alert path in general terms; this section names the actual pieces inside Falco that do each step.
The three driver generations
Falco's driver — the component that actually intercepts raw syscalls — has evolved through three generations, and the previous page's comparison table applies directly to Falco's own history. The original driver was a custom out-of-tree kernel module, compiled per kernel version; it works everywhere but needs either a matching prebuilt module for the exact running kernel or kernel headers on the node to build one on the fly, and a bug in it can, in the worst case, crash the host. A legacy eBPF probe came next, trading that crash risk for the verifier's safety guarantees while still needing to be compiled against fairly specific kernel versions. The current default in recent releases is the modern eBPF driver, built with CO-RE (Compile Once – Run Everywhere) against BTF (BPF Type Format) — the same mechanism the previous page introduced — so one compiled probe adapts to whatever kernel it lands on instead of needing a per-node build step at all. Check which driver is actually selected on a given deployment; defaults have shifted release over release, and a managed node image with an unusual kernel can quietly fall back to a driver you didn't intend.
The plugin framework: beyond syscalls
Syscalls aren't the only signal worth alerting on, and Falco's plugin framework — introduced in the 0.29–0.31 release line — is what lets it ingest anything else without rewriting the core engine. A plugin is a shared library loaded in-process through a versioned C ABI (the plugin API version, tracked separately from Falco's own release version — a mismatch between the two is a real startup failure worth knowing to check for). Plugins come in two flavors, and one plugin can implement both: source plugins produce a whole new stream of events — the k8saudit plugin turns the Kubernetes API server's audit log into Falco events, which is exactly what lets a rule see kubectl exec, kubectl cp, or an anonymous API request as a first-class detection, attributed to the actual Kubernetes identity that issued it rather than inferred from a process tree; the cloudtrail plugin does the same for AWS's control-plane audit trail, giving Falco visibility into a root-account console login or an IAM policy change alongside whatever's happening inside the workloads it's already watching, which is exactly the kind of cross-layer correlation CNAPP & the unified cloud security stack covers at the platform level. Extraction plugins, by contrast, add new fields a rule's condition can filter on without producing events of their own — the json plugin, for instance, parses a JSON payload embedded in another event's data and exposes its fields for matching.
falcoctl and keeping rules current
Rules and plugins are distributed as versioned OCI artifacts — the same registry mechanism a container image uses — through falcoctl, a companion CLI (and, run continuously, a sidecar) that resolves an artifact from a configured index, pulls it, and can keep polling for a newer version and hot-reload it into a running Falco without a pod restart. That's a genuinely convenient way to keep a fleet's ruleset current without rebuilding an image on every update — and, as the gotchas section below gets into, a genuine supply-chain surface worth treating with the same scrutiny as any other artifact that lands on a node unreviewed.
Installing and running it as a fleet
☺ Like you're 10: one command puts a copy of Falco on every node, and one setting decides which driver each copy uses to actually see the syscalls.
The Helm chart and driver selection
The supported path is the falcosecurity/falco Helm chart, which deploys Falco as a DaemonSet — one Pod per node, because syscall visibility is inherently node-scoped, and a single centralized Falco can't watch syscalls happening on a machine it isn't running on.
$ helm repo add falcosecurity https://falcosecurity.github.io/charts
$ helm repo update
$ helm install falco falcosecurity/falco \
--namespace falco --create-namespace \
--set driver.kind=modern_ebpf \
--set falcosidekick.enabled=true \
-f values-overrides.yaml# values-overrides.yaml
driver:
kind: modern_ebpf # module | ebpf | modern_ebpf
falcoctl:
artifact:
install: { enabled: true }
follow: { enabled: true } # keep the ruleset current from the configured OCI index
customRules:
rules-acme-overrides.yaml: |-
- list: falco_sensitive_mount_images
items: [registry.acme.io/platform/debug-toolbox]
append: true
falcosidekick:
enabled: true
config:
slack:
webhookurl: "${SLACK_WEBHOOK_URL}"
minimumpriority: criticalCapability footprint: privileged vs. scoped
Falco needs to load a driver and read raw syscalls across the entire node, which shows up in the DaemonSet spec as hostPID: true and mounts into /proc, the kernel's debug filesystem, and — for the kernel-module and legacy-probe drivers — a genuinely privileged security context. That's an unusually large footprint for the very tool that's supposed to be watching for privilege escalation, and it's worth treating as a deliberate tradeoff rather than an oversight: pin the image by digest, keep it current, and give its RBAC and mounts the same review anything else carrying that grant would get. The modern eBPF driver narrows this somewhat — on a kernel new enough to expose the right BPF capabilities (generally 5.8 and newer), it can run with a scoped set of Linux capabilities — CAP_BPF, CAP_PERFMON, CAP_SYS_RESOURCE — instead of the blanket privileged: true the older drivers require, which is one more concrete reason it's the default to reach for on a modern fleet.
Writing detection rules against live syscalls
☺ Like you're 10: three kinds of building blocks — a reusable "this counts as X" shortcut, a reusable list of values, and the rule that actually fires — combine into every check Falco runs.
Rule, macro, list — the three building blocks
A Falco rules file is a YAML list of three entry types, and nearly every rule leans on all three. A list is a named array of values — binary names, file paths, image references — reused across multiple rules. A macro is a named, reusable fragment of a condition, so a phrase like "this event happened inside a container" doesn't get retyped in every single rule. A rule is the thing that actually evaluates and fires: it names a condition (a boolean expression over live event fields, commonly built from macros and lists), an output template interpolated from the same fields at alert time, a priority, and a set of tags, often carrying a MITRE ATT&CK technique ID the way the previous page's shell-detection rule carried T1059.
The condition language supports and, or, and not; comparison operators =, !=, and their numeric siblings; in and contains/icontains for membership and substring matching; startswith for path prefixes; and exists for checking whether a field is even populated on a given event type. That's a small enough surface to read fluently within an afternoon, which is deliberate — Falco's own defaults are meant to be auditable by the team running them, not a black box.
Fields you'll reach for constantly
| Prefix | Holds | You reach for it when |
|---|---|---|
proc.* | name, pname, cmdline, tty, aname[n] (nth ancestor) | Anything about the process that made the syscall, or its parent chain |
fd.* | name, sip/sport, rip/rport, type | File paths opened, or a network connection's source/remote address |
container.* | id, name, image.repository, privileged, mounts | Scoping a rule to "inside a container" or checking image/mount facts |
k8s.* | pod.name, ns.name, pod.label.<key> | Kubernetes-native alerts — needs the k8s metadata enrichment running |
user.* | name, uid | Which user (inside the container's own namespace) made the call |
evt.* | type, dir (> entry / < exit), time | Matching a specific syscall by name, or entry vs. exit |
Run falco --list against your installed version rather than trusting a table pinned to whichever release wrote it — new fields land with new Falco versions, and the exact set available depends on which driver and plugins are loaded.
Priorities: eight syslog-style levels
| Priority | Typical use |
|---|---|
EMERGENCY / ALERT | Confirmed active compromise — page immediately, no dedup delay |
CRITICAL | High-confidence malicious pattern — a container escape technique, a docker.sock connection |
ERROR / WARNING | Suspicious but not conclusive on its own — a sensitive mount, an unexpected shell |
NOTICE | Worth recording for correlation, rarely worth a page by itself |
INFORMATIONAL / DEBUG | Background signal — usually filtered out of anything but a raw event archive |
A rule built from scratch
The previous page's "Terminal shell in container" rule showed the shape; here's a rule this course hasn't shown yet, built around a genuinely common lateral-movement technique — a process reading the pod's own mounted Kubernetes ServiceAccount token to talk to the API server with more authority than it should have, the exact credential-access concern workload identity & pipeline IAM covers from the identity side.
# custom_rules.d/20-sa-token-read.yaml
- macro: sa_token_path
condition: fd.name startswith /var/run/secrets/kubernetes.io/serviceaccount
- list: expected_sa_token_readers
items: [kubelet, envoy, istio-proxy] # adjust to what YOUR workloads legitimately do
- rule: Unexpected ServiceAccount Token Read
desc: >
A process outside the expected list read the pod's own mounted
ServiceAccount token — a common first step in lateral movement
against the Kubernetes API from inside a compromised pod.
condition: >
open_read and container
and sa_token_path
and not proc.name in (expected_sa_token_readers)
output: >
ServiceAccount token read by unexpected process
(user=%user.name command=%proc.cmdline container=%container.name
pod=%k8s.pod.name ns=%k8s.ns.name file=%fd.name)
priority: WARNING
tags: [k8s, credential_access]Read it the same way the previous page read its rule: condition narrows to exactly the syscall pattern that matters — a read, inside a container, of that specific path, by a process not on the allowlist — and output is a template, so the alert that lands already carries the Pod, namespace, and exact command line, not a bare "something happened" someone still has to go dig context for.
The default ruleset in depth: shell-in-container and sensitive mounts
☺ Like you're 10: Falco ships with a working set of these rules already written — knowing what's already covered is most of the job before you write a single new one.
Terminal shell in container, revisited
The previous page's rule wasn't a simplified teaching example — it's modeled closely on the actual default rule of the same name in falco_rules.yaml. The macros doing the real work there are container (container.id != host, meaning the event happened inside a container rather than on the bare node), spawned_process (an execve event on entry), and shell_procs (proc.name in a bundled list of shell binaries — bash, sh, zsh, dash, and neighbors). Combined with proc.tty != 0, that's precisely "an interactive shell, inside a container, with a terminal attached" — the shape of an attacker's session, not a service's own non-interactive startup shell.
Launch Sensitive Mount Container
This is the default rule the content brief's "sensitive-mount-access" phrase points at directly, and it's worth reading closely because it's a write-time check smuggled into the runtime ruleset — it fires the moment a container with a dangerous bind mount actually starts, which is often before Kubernetes admission control gets a chance to weigh in, or as a second layer behind it. The macro sensitive_mount checks whether container.mount.dest matches a bundled list of host paths that should essentially never be handed to a container — /, /etc, /proc, /root, and the container runtime's own control socket, /var/run/docker.sock or its containerd equivalent, among others.
# Modeled closely on Falco's own default ruleset — exact list contents
# and field names shift release to release; check falco_rules.yaml
# in your installed version for the current wording.
- rule: Launch Sensitive Mount Container
desc: >
Detect launching a container with a sensitive mount, such as the
Docker daemon socket, that gives that container broad access to
the host.
condition: >
container_started and container
and sensitive_mount
and not falco_sensitive_mount_containers
output: >
Container with sensitive mount started
(user=%user.name command=%proc.cmdline image=%container.image.repository
mounts=%container.mounts)
priority: WARNINGNotice the shape of not falco_sensitive_mount_containers at the end — that's an allowlist macro, backed by a falco_sensitive_mount_images list, so a team can except a specific known-legitimate image (a debug toolbox, a node-level monitoring agent that genuinely needs the runtime socket) without touching the rule's actual condition. That pattern — a rule written broad, with a named allowlist carved out for the deliberate exceptions — is the same shape covered in the next section, generalized past this one rule.
Read sensitive file trusted after startup
A third default worth knowing by name: a read of /etc/shadow, /etc/sudoers, an SSH private key path, or a similar credential-shaped file, by a process not on a bundled list of binaries trusted to legitimately touch those files, occurring after the container's own startup window has passed. The "after startup" qualifier matters — an image's own entrypoint script often legitimately reads configuration during the first second of a container's life, and a naive rule without that qualifier would either miss real abuse hiding in that noise or fire constantly on normal boot behavior.
The instinct when "Launch Sensitive Mount Container" fires on a genuinely legitimate workload is to comment the whole rule out. Don't — that also removes detection for every illegitimate sensitive mount that shows up afterward, on any other Pod, forever. Add the specific image to the allowlist instead, in a reviewed override file, the way the next section covers. The rule staying broad and the exception staying narrow is what keeps this actually protective instead of decorative.
Extending it without forking: append and exceptions
☺ Like you're 10: two different tools for two different jobs — adding a name to an existing list, versus carving out one specific, narrow exception to an otherwise-unchanged rule.
Before these two mechanisms existed, extending Falco meant copying an entire macro or rule into a falco_rules.local.yaml file and redefining it wholesale — which worked, but meant every future upstream change to that rule had to be manually reconciled against your copy. Two additions changed that.
The first is append: true on a list or macro defined in a file loaded after the base ruleset: instead of replacing the original definition, Falco adds the new items to it. That's the mechanism behind the values-overrides.yaml example earlier on this page — one line adds a trusted debug image to the sensitive-mount allowlist without touching a single character of the rule itself.
The second, newer mechanism is a rule's exceptions block, which lets you carve out one specific combination of field values without writing a macro or a list at all:
# rules.d/10-acme-overrides.yaml — loaded after the base ruleset
- rule: Launch Sensitive Mount Container
exceptions:
- name: known_debug_toolbox
fields: [container.image.repository, container.mount.dest]
comps: [in, in]
values:
- [registry.acme.io/platform/debug-toolbox, /var/run/docker.sock]Read as a sentence: except this rule when the container's image repository is registry.acme.io/platform/debug-toolbox and its mount destination is /var/run/docker.sock — both conditions, not either. Multiple value tuples can be added under the same exception name as the allowlist grows, each one reviewable as its own line in a pull request against a rules.d file, instead of a wholesale redefinition of the rule that has to be diffed against upstream by hand every time the base ruleset changes.
Run Falco locally with the default rules loaded, then deliberately trip "Launch Sensitive Mount Container" — start any container with -v /var/run/docker.sock:/var/run/docker.sock and watch the alert land. Now write a two-line exceptions override for that exact image and mount path in a second rules file, reload Falco with both files loaded, and confirm the alert stops firing for that one image while starting a different container with the same mount still trips it. That's the whole discipline this section describes, watched instead of read about.
From alert to SIEM — not a log file nobody reads
☺ Like you're 10: a detector that only whispers into an empty log file is barely better than no detector — the whole point of this section is making sure someone, or something, is actually listening.
Falco's default output is a JSON line written to stdout, captured by whatever's collecting container logs on that node. That's a real, functioning sink — and it's also precisely the "log file nobody reads" the content brief for this page names directly: a JSON line scrolling past in kubectl logs falco-xyz protects nothing if nobody's tailing that stream, and almost nobody tails a raw log stream continuously in practice. Two mechanisms turn that stream into something a team actually acts on.
The first is Falco's own gRPC output — a structured, subscribable stream a client can connect to directly rather than parsing log lines, which is what tools like falco-exporter (turning alerts into Prometheus metrics) build on. The second, and the one that matters most for the "pipe it into a SIEM" half of this page's brief, is Falcosidekick: a companion project that consumes that same alert stream once and fans it out to more than fifty destinations — Slack, PagerDuty, Splunk's HTTP Event Collector, Elasticsearch, Loki, Datadog, S3, a generic webhook — without Falco itself needing to know anything about any of them.
# falcosidekick config — via the Falco Helm chart's falcosidekick.config,
# or its own standalone config.yaml
splunk:
hecurl: "https://splunk.acme.internal:8088"
token: "${SPLUNK_HEC_TOKEN}"
minimumpriority: warning
slack:
webhookurl: "${SLACK_WEBHOOK_URL}"
minimumpriority: critical # only page-worthy findings reach chat
elasticsearch:
hostport: "https://es.acme.internal:9200"
index: falco-alerts
minimumpriority: notice # keep the full stream for later correlation, unfilteredThe minimumpriority field per destination is the detail that actually makes this usable rather than just louder: Slack gets paged on CRITICAL and above, the SIEM ingests everything from NOTICE up for later correlation, and nobody's phone buzzes for an INFORMATIONAL event. Falco's own falco.yaml adds a second layer of throttling on top of that priority split — an outputs.rate/outputs.max_burst token-bucket limiter, so a genuine incident that trips the same rule a thousand times in a second becomes one page and a summarized burst, not a thousand pages:
# /etc/falco/falco.yaml (excerpt) — exact key layout shifts release to release, # check falco.yaml on your installed version outputs: rate: 1 # tokens added per second to the limiter max_burst: 1000 # bucket size — absorbs a real burst without silently dropping it grpc: enabled: true grpc_output: enabled: true # lets Falcosidekick, or any gRPC client, subscribe directly
None of this is response — routing an alert to a SIEM still leaves a human (or a runbook) to act on it. Falco Talon, introduced briefly on the previous page, is the layer that closes that last gap by acting automatically on a match — cordoning a node, isolating a Pod's network, killing the offending process — for the subset of findings a team trusts enough to automate without a human in the loop. See Container Runtime Security for where that decision sits relative to everything covered here.
Operating it day to day
☺ Like you're 10: a handful of commands cover almost everything — check the rules are valid, see what's loaded, and try a rule against a recording before it ever meets live traffic.
# catch a syntax error before it ships to the fleet — this is the single most important # command on this page; see the gotchas section for exactly why $ falco --validate /etc/falco/falco_rules.yaml /etc/falco/rules.d/*.yaml $ falco -N # print every currently loaded rule and exit $ falco --list # print every field and operator available for conditions $ falco -r rules.d/10-acme-overrides.yaml -o json_output=true # run with an extra rules file layered on top $ falco -e captured-incident.scap # replay an offline capture through the current ruleset $ falcoctl artifact install falco-rules:3 # pull a specific rules artifact version from the index $ falcoctl artifact follow # keep polling the index and hot-reload on a new version
The replay flow is worth building into a habit: capturing a real incident's syscall trace once, then replaying it against every future rule change with falco -e, is the closest thing to a regression test this tool has — it proves a rule still fires on the exact pattern that mattered last time, before that rule ever reaches a live node.
Gotchas and failure modes
☺ Like you're 10: most surprises trace back to one of three things — a bad rules file, a driver that doesn't match the kernel underneath it, or a node too busy to keep up.
- Falco fails closed on a bad rules file — which is good, and also the reason to validate in CI. A syntax error or an unresolved macro/list reference in any loaded rules file causes Falco to refuse to start at all, rather than silently skip the broken file and run with a partial ruleset. That's the correct, safety-first default — but it also means a bad override pushed straight to a DaemonSet takes down runtime detection across the entire fleet until someone reverts it. Run
falco --validatein CI against every rules file before it's allowed to merge, the same way a Terraform plan gets validated beforeapply. - Driver/kernel mismatches on unusual managed-node images. The kernel-module driver needs either a prebuilt module for the exact running kernel or headers to build one locally; a heavily customized or minimal managed node image (a stripped GKE COS variant, an unusual Bottlerocket or Talos build) can have neither. The modern eBPF driver avoids the per-kernel-build problem entirely via CO-RE, but it still needs the kernel to expose BTF — an extremely old or aggressively trimmed custom kernel without BTF breaks even that path, and falls back to needing BTF supplied externally or the legacy probe instead.
- A busy node can drop events, and Falco tells you when it does — if you're watching for it. Under sustained high syscall volume, the ring buffer between the kernel and userspace can fill faster than Falco's userspace process drains it. Falco surfaces this itself as an internal "syscall event drop" notification rather than failing silently — treat that meta-alert as a canary worth its own dashboard panel, because a drop means the detection coverage for that window is incomplete, not just delayed.
- falcoctl's auto-follow is a supply chain, not just a convenience. Automatically pulling and hot-loading a newer rules artifact from an OCI index the moment it's published skips the same review a change to any other production config would normally go through. Pin to specific artifact versions and promote deliberately in anything resembling a regulated environment — the same "know exactly what's running and why" discipline software bills of materials applies to a dependency applies just as directly to a ruleset that decides what counts as an attack.
- A plugin API version mismatch is a startup failure, not a runtime warning. A plugin compiled against a newer or older plugin ABI than the running Falco core supports fails to load at all — worth checking explicitly when pinning plugin versions independently of the core Falco version in a Helm values file.
None of the tuning on this page replaces the false-positive-fatigue discipline the previous page already covered — start new or custom rules at a lower priority, shadow them against real traffic before wiring them into a paging path, and scope exceptions narrowly instead of disabling a whole rule cluster the first time it's noisy on one deployment. A detection system nobody trusts gets its alerts muted within a week, and a muted Falco is functionally identical to no Falco at all.
Falco vs. Tetragon, Tracee, Sysdig Secure, and KubeArmor
☺ Like you're 10: every alternative trades something specific against Falco's "detect broadly, from a mature, vendor-neutral CNCF project" position — knowing which axis is being traded is the whole comparison.
| Tool | Approach | Strength | Trade-off |
|---|---|---|---|
| Falco | eBPF syscall + plugin-sourced detection, YAML rule engine, CNCF-graduated | Vendor-neutral, widest community ruleset and integration ecosystem, mature governance | Detection only by default — response needs Falco Talon or another layer bolted on |
| Tetragon (Cilium / Isovalent) | eBPF process and network visibility, tightly integrated with Cilium's own dataplane | Can enforce, not just detect — an eBPF program can block a syscall in-kernel, not merely alert after it happened | Deepest value assumes Cilium as the CNI already; less of a natural fit dropped into an unrelated network stack |
| Tracee (Aqua Security) | eBPF runtime security and forensics, signature-based detections | Strong forensic event capture alongside detection; same vendor family as Trivy | Smaller community ruleset and integration surface than Falco's at present |
| Sysdig Secure | Commercial platform built on Falco's own open-source engine | Managed rule tuning, a UI, compliance mappings, and image-scanning correlation out of the box | The open-source engine underneath is free; the managed platform on top is a paid product |
| KubeArmor | LSM-based (AppArmor / SELinux / BPF-LSM) policy enforcement, not syscall-stream detection | Genuinely preventive — a policy can block a forbidden action outright, not just alert on it after the fact | A different mechanism entirely; less flexible ad-hoc rule authoring than Falco's condition language |
The practical rule of thumb: reach for Falco when the job is broad, auditable, vendor-neutral detection with a mature rule ecosystem behind it — which is most of the time, and why it's the default this course teaches. Reach for Tetragon or KubeArmor specifically when detection alone isn't enough and the requirement is genuinely blocking the action in-kernel before it completes, and reach for Sysdig Secure when the team wants the same open-source engine with a managed UI and support contract behind it rather than operating the YAML rulesets and Falcosidekick routing by hand.
Benny the Beaver: Falco's been paging me every night this week. Same rule, same image — my debug-toolbox container, the one with docker.sock mounted on purpose so I can actually debug the runtime.
Timmy the Turtle: Don't touch the rule itself. That rule is the only thing standing between us and every other container that mounts docker.sock without a good reason.
Recon the Robot: Write it as an exception instead — image repository plus mount path, both conditions, in a reviewed override file. Two lines. The rule stays exactly as broad as it was.
Foxy: Fine, but honestly — before this week, was anyone actually reading Falco's log stream? Or did it take three nights of pages to notice this rule even existed?
Recon the Robot: Fair hit. It's routed through Falcosidekick now — critical and above to Slack, everything from notice up into the SIEM for later correlation. Stdout was never the plan, it was just the default nobody had gotten to yet.
Pip the Hummingbird: One more thing while you're in there — falcoctl is set to auto-follow the rules index. Who's actually reviewing what gets pulled in before it hot-loads onto every node?
Recon the Robot: ...Pinning that to a specific artifact version starting today. Good catch.
1. Name the three generations of Falco's driver, and explain what CO-RE specifically removes from the deployment story that the older two generations both needed. 2. What are the three building blocks of a Falco rules file, and what does a rule's exceptions block let you do that editing the base condition directly doesn't? 3. Walk through what the default "Launch Sensitive Mount Container" rule actually checks, and name one legitimate reason a team might need to except a specific image from it rather than disable the rule outright. 4. Falco refuses to start on a rules file with a syntax error rather than silently skipping it — why does that matter operationally, and which command catches the problem before it ever reaches the fleet? 5. What does Falcosidekick add on top of Falco's own stdout output, and why is that specifically the difference between an alert and "a log file nobody reads"? 6. Name one thing a Kubernetes-audit-derived detection (via the k8saudit plugin) can see that a pure syscall-based rule cannot. 7. Pick one alternative to Falco from the comparison table and state the one axis it trades against Falco.
Check your answers
- A custom out-of-tree kernel module, compiled per kernel version; a legacy eBPF probe, similarly version-specific; and the modern eBPF driver, built with CO-RE against BTF. CO-RE removes the need for a per-node (or per-kernel-version) compilation step entirely — one compiled probe adapts to whatever kernel it lands on by reading that kernel's own BTF type information at load time.
list,macro, andrule. Anexceptionsblock carves out one specific combination of field values (e.g., a particular image plus a particular mount path) without writing a new macro or list and without touching the rule's ownconditionat all — the rule stays exactly as broad as it was for everything that isn't the named exception.- It fires when a container starts with a bind mount into one of a bundled list of sensitive host paths —
/,/etc,/proc, the container runtime's control socket, and similar — unless the image is on an allowlist macro. A legitimate reason to except an image: a debug-toolbox or node-level agent that genuinely needs the runtime socket mounted to do its job, where the fix is a narrow, reviewed exception rather than disabling detection for every other container in the cluster. - It matters because Falco fails closed — a bad rules file takes down runtime detection across the whole fleet the moment it's deployed, not just on the node where the mistake was made.
falco --validate, run in CI before a rules change is allowed to merge, catches the syntax or reference error before it ever reaches a live DaemonSet. - Falcosidekick fans Falco's single alert stream out to more than fifty destinations — Slack, PagerDuty, a SIEM's ingestion endpoint, and more — each with its own
minimumprioritythreshold, so critical findings actually page someone and lower-priority ones land in a SIEM for correlation instead of everything (or nothing) simply scrolling past in a container's stdout log that nobody is continuously tailing. - Any reasonable answer naming Kubernetes-identity attribution for an API-level action — for example, exactly which user or service account issued a
kubectl exec,kubectl cp, or an anonymous API request, which the audit log captures with the actual Kubernetes identity behind it, versus a syscall-only view that can see a process spawn but has no direct line to which API caller triggered it. - Any one of: Tetragon trades Falco's vendor-neutral breadth for in-kernel enforcement (blocking, not just detecting) tightly coupled to Cilium; Tracee trades Falco's larger community ruleset for the same vendor family as Trivy and a forensics focus; Sysdig Secure trades the free, self-operated engine for a managed UI and support contract on top of it; KubeArmor trades Falco's flexible condition language for genuine LSM-based prevention instead of stream-based detection.
Falco is the tool this course keeps coming back to whenever "what's running" needs an answer that a build-time scan can't give — see Container Runtime Security for the concepts this page assumed, Detection Engineering & Security Observability for how a Falco alert fits into a broader detection program, and Kubernetes Security Deep Dive for the write-time controls that sit above it. If runtime and container security is the syllabus you're actually studying toward, the CKS — Kubernetes Security Specialist exam leans heavily on exactly this material, and the container escape investigation drill is where you practice reading a Falco alert all the way through to root cause. Back on the tool landscape, Falco sits beside Kyverno and Kubernetes' own admission controls as the "watching" half of a Kubernetes security posture, opposite the "gating" half those two tools cover.