Falco rules are powerful, but the default set is noisy and often misses things specific to your environment. You’re going to end up writing custom rules to catch what matters to you.
Here’s a real-world example of a custom Falco rule that detects when a privileged container tries to write to sensitive host directories.
- rule: Write to sensitive host directories from privileged container
desc: "Detects a privileged container attempting to write to sensitive host directories like /etc, /usr, or /var. This could indicate a compromise or an attempt to modify the host system."
condition: >
(evt.type = "open" or evt.type = "openat") and
fd.type = "file" and
container.privileged = true and
(
fd.name startswith "/etc/" or
fd.name startswith "/usr/bin/" or
fd.name startswith "/var/lib/" or
fd.name startswith "/var/log/"
) and
(fd.flags.w = true or fd.flags.creat = true)
output: >
Privileged container writing to sensitive host directory (user: %(user.name)
container: %(container.name) image: %(container.image.name)
file: %(fd.name) flags: %(fd.flags.w ? 'W' : '')%(fd.flags.creat ? 'C' : '')
priority: critical
source: syscall
Let’s break down what’s happening here and why it’s useful.
This rule targets a specific, high-risk behavior: a container that’s already running with elevated privileges (container.privileged = true) attempting to modify critical host system files. The evt.type = "open" or evt.type = "openat" condition ensures we’re looking at file access events. The fd.type = "file" filters out other file descriptor types. The core of the detection is the fd.name startswith checks, which are looking for writes into common system directories. We also check fd.flags.w = true (write) or fd.flags.creat = true (create new file), because simply opening a file isn’t as concerning as trying to change it.
The output field is crucial for actionable alerts. It provides context like the user and container name, the image used, the specific file being targeted, and the flags indicating a write or create operation. This allows you to quickly understand the scope of the potential incident.
Why is this important? A privileged container already has a significant amount of access to the host. If it then starts writing to directories like /etc (configuration files), /usr/bin (executables), or /var/lib (application data), it’s a strong indicator that something has gone wrong. An attacker might be trying to tamper with system configurations, inject malicious binaries, or disrupt application state. By detecting this early, you can potentially stop a breach before it escalates.
Consider the difference between this rule and a generic "write to file" rule. The generic rule would trigger on any file write, leading to an overwhelming number of false positives. By adding the container.privileged = true and the specific sensitive directory checks, we drastically reduce noise and focus on the most critical scenarios. This is the essence of writing effective custom rules: understanding your environment’s unique risks and translating them into specific, targeted Falco conditions.
You can add this rule to your Falco configuration by placing it in a separate .yaml file (e.g., custom-rules.yaml) and then including that file in your falco.yaml configuration under the rules_files directive:
rules_files:
- /etc/falco/json_output.yaml
- /etc/falco/falco_rules.yaml
- /etc/falco/falco_rules.local.yaml
- /etc/falco/custom-rules.yaml # <-- Add this line
After updating falco.yaml, restart the Falco process for the new rule to take effect.
The next step is often to refine these rules further based on your application’s normal behavior. You might find that certain legitimate operations also trigger this rule, requiring you to add exceptions or more specific conditions to avoid alert fatigue.