BlogDetection

Falco rules that catch real attacks (and skip the noise)

Default rulesets page you for everything. Tune Falco to the syscalls that matter and route the rest to a dashboard.

Nov 25, 2025·4 min readAdvanced·By the SecOpsLog team · command-tested

Falco watches the kernel's syscall stream and fires when something matches a rule — a shell in a container, a write under /etc, an outbound connection from a database pod. The problem is not detection; it is that the default ruleset alerts on so much benign activity that the real signal drowns. A pager that cries wolf gets muted, and a muted detector is no detector at all. Tuning is not optional — it is the difference between runtime security and runtime spam.

This note builds one high-confidence rule, carves a narrow exception for known-good debug images, and routes alerts by priority so Notice events do not page on-call. For admission and runtime hardening together, see Kubernetes security & hardening.

Syscall → rule → alert

Untuned default rules are a pager firehose. Tune before you page.

Syscall eBPF / driver Falco rules engine condition → priority → output Alert SIEM / pager 1 Observe open, execve, connect… container + k8s metadata high volume, low context alone 2 Tune rules exceptions for known noise priority: NOTICE → CRITICAL custom rules for your apps 3 Route alerts stdout → sidecar → SIEM page only on CRITICAL noise kills the program Default rules are a starting point. Untuned Falco is a pager firehose. syscall → rules → alert → response
Observe — syscallsTune — rulesAlert — route
From syscall to pager (without noise)

Start with one rule you trust. Expand coverage only after routing and exceptions are disciplined.

1SyscallseBPF probe captures2Rule matchFalco engine evaluates3Priority tagWARNING vs NOTICE4Exception filterallowlist debug images5Falcosidekickfan-out to channels6Routepage vs dashboard vs SIEM7Review allowlistsame cadence as firewall rules
bash — falcolive
falco -r /etc/falco/rules.d/
Falco initialized with 89 rules
watching syscalls via modern eBPF probe ...
 
10:42:01 Warning Shell spawned in a container
(user=root container=api-7f9c shell=bash parent=node)
10:42:07 Notice Package mgmt launched in container (apk add)
 
signal, not noise — nobody should be typing in prod containers

A rule that catches what matters

Good rules describe behavior, not strings. 'A shell process, with a TTY, inside a container' is robust — it does not care whether the attacker used bash, sh, or busybox. Match on process lineage and container context, and emit fields you will actually need during triage: user, container name, parent process, full cmdline.

rules.d/shell.yaml
- rule: Terminal shell in container
desc: An interactive shell was opened inside a container
condition: >
spawned_process and container
and shell_procs and proc.tty != 0
output: >
Shell spawned in a container
(user=%user.name container=%container.name
shell=%proc.name parent=%proc.pname cmdline=%proc.cmdline)
priority: WARNING
tags: [container, shell, mitre_execution]

Tune with exceptions, not the mute button

When a rule is noisy, the instinct is to disable it. Do not — that blinds you to the real event. Instead carve a narrow exception for the known-good source, so everything else still fires. Keep exceptions in version control with a comment naming who approved them and why; review them quarterly the same way you review firewall rules.

rules.d/shell.yaml
# a debug image your team uses on purpose — reviewed Q2 2026
- list: allowed_shell_containers
items: ["registry.io/ci-debug", "registry.io/toolbox"]
- rule: Terminal shell in container
append: true
condition: and not container.image.repository in (allowed_shell_containers)
An exception is a decision, log it
Every allowlist entry is a hole you opened on purpose. Keep them in version control with a comment saying who and why, and review them the same way you review firewall rules — otherwise the exception list quietly becomes the attack surface. Never disable a rule globally because one team complained; fix the exception scope instead.

Route by priority

Not every alert deserves a 3am page. Send Critical and Warning to the on-call channel, and stream Notice/Informational to a dashboard or your SIEM. Falcosidekick fans a single Falco output stream out to Slack, Loki, and object storage at once — configure routing rules on priority so the pager only fires on behavior you would actually investigate at night.

On Kubernetes, run Falco as a DaemonSet so every node sees the same syscall stream. Enable the modern eBPF driver where your kernel supports it — fewer false positives from legacy kernel-module probes, and no DKMS headaches on managed node images. Baseline your cluster in Audit for a week before you route WARNING to PagerDuty.

Where this goes next

Detection is only half the loop — the other half is response. Falco Talon can act on an alert automatically (kill the pod, isolate it with a NetworkPolicy) while a human catches up. Start by trusting one high-confidence rule enough to auto-respond; expand from there. The Kubernetes security & hardening course covers runtime detection, admission control, and the full CKS surface.

Go deeper in a courseKubernetes security & hardeningRuntime detection, admission control and the full CKS surface.View course

Related posts