Style Guide of Falco Rules
In addition, the resources under references/rules provide complementary information. We highly recommend regularly revisiting each guide to stay up-to-date with the latest advancements of Falco.
A rule declaration is represented as a YAML object consisting of multiple keys. The suggested order for these keys is as follows:
- rule: desc: condition: output: priority: tags: ... other keys if applicable in no particular order
Choose a concise title that summarizes the essence of the rule's purpose. Rule's name must start with an upper-case letter. For
list, use lowercase_separated_by_underscores, for example,
Aligning with Falco's rules maturity and adoption framework, it is encouraged to not just include a longer description of what the rule detects but also to give advice on how to tune this rule and reduce possible noise. If applicable, elaborate on how to correlate the rule with other rules or data sources for incident response. However, keep them concise. The description should end with a period.
These recommendations prioritize performance impact while maintaining a consistent style for better understanding and easier customization. This approach ensures more manageable maintenance of the rules in the long run.
We explain the high-level principles using example rules or snippets.
- Each upstream Falco rule must include an
evt.typefilter; otherwise, you will get a warning.
Rule no_evttype: warning (no-evttype): proc.name=foo did not contain any evt.type restriction, meaning that it will run for all event types. This has a significant performance penalty. Consider adding an evt.type restriction if possible.
- Prioritize the
evt.typefilter first; otherwise, you will get a warning. Falco buckets filters per
evt.typefor efficient rules matching through applying the rule's Abstract Syntax Tree (AST) to relevant event types only. A nice side effect is better readability as well.
Rule evttype_not_equals: warning (trailing-evttype): evt.type!=execve does not have all evt.type restrictions at the beginning of the condition, or uses a negative match (i.e. "not"/"!=") for some evt.type restriction. This has a performance penalty, as the rule can not be limited to specific event types. Consider moving all evt.type restrictions to the beginning of the rule and/or replacing negative matches with positive matches if possible.
To maintain performance, avoid mixing unrelated event types in one rule. Typically, only variants should be mixed together, for example:
evt.type in (open, openat, openat2).
The best practice and requirement for upstream rules are to only define positive
evt.type!=open, for example, would imply each of the supported syscalls, resulting in a significant performance penalty. For more information, read the Adaptive Syscalls Selection in Falco blog post.
evt.typefilter, place your mainly positive filters to efficiently eliminate the most events step by step. An exception to this rule is the
containermacro, which can quickly eliminate many events. Therefore, the guiding principle of "divide and conquer" commonly used in database query recommendations, also applies to Falco's filter statements.
- macro: open_write condition: (evt.type in (open,openat,openat2) and evt.is_open_write=true and fd.typechar=`f` and fd.num>=0) ... - macro: container condition: (container.id != host) ... - rule: Detect release_agent File Container Escapes ... condition: > open_write and container and fd.name endswith release_agent and (user.uid=0 or thread.cap_effective contains CAP_DAC_OVERRIDE) and thread.cap_effective contains CAP_SYS_ADMIN ...
- Effective Falco rules should now already be in a good state. Additionally, use exclusionary statements mostly to filter out common anti-patterns and noise. Often, these statements are based on profiling. You will notice that many upstream rules provide an empty template macro for this purpose, which you can customize.
- macro: spawned_process condition: (evt.type in (execve, execveat) and evt.dir=<) ... - list: known_drop_and_execute_containers items:  - rule: Drop and execute new binary in container ... condition: > spawned_process and container and proc.is_exe_upper_layer=true and not container.image.repository in (known_drop_and_execute_containers) ...
- Use existing macros for reusability purposes, if applicable (e.g.
- Exercise caution when dealing with complicated nested statements in Falco rules, and ensure you use parentheses consistently to achieve the desired correct behavior. Remember, using too many parentheses does not cause any harm.
- To avoid grammatical syntax errors or sub-optimal performance, refrain from combining
orstatements with negation. Instead, use
orstatements only for positive filters.
- macro: minerpool_https condition: (fd.sport="443" and fd.sip.name in (https_miner_domains)) ... condition: ... and ((minerpool_http) or (minerpool_https) or (minerpool_other))
- Furthermore, it is preferred to use
and notto consistently negate a positive sub-expression.
- Avoid double-negation.
condition: ... and not fd.snet in (rfc_1918_addresses)
- For operations involving string comparison,
endswithshould be preferred over
containswhenever possible, as they are more efficient.
- Whenever possible, try to avoid making a rule expression too long.
- Upstream rules shall not contain any
exceptionsto ensure simpler rules and facilitate better adoption.
Each rule must include output fields.
For the output fields, expect that each Falco release typically exposes new supported output fields that can help you write more expressive rules and/or add more context to a rule for incident response.
Building upon the guide around writing rules with respect to output, when considering upstreaming your rule, core output fields relevant for this rule must be included. At the same time, we try to keep them to a minimum, and adopters can add more output fields as they see fit.
For each rule include the critical fields listed below related to process and user information, as well as the actual event type. For example, the
tty field (terminal) can help determine if the process ran in an interactive shell. The exe flags are valuable in identifying new binaries in your container, such as
EXE_UPPER_LAYER. Additionally, examining the exepath alongside the process name provides insights into whether the binary might be located in more suspicious folders like
tmp. Understanding the direct parent process is vital for basic process lineage analysis.
evt_type=%evt.type user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty exe_flags=%evt.arg.flags
For network-related rules include:
connection=%fd.name lport=%fd.lport rport=%fd.rport fd_type=%fd.type fd_proto=fd.l4proto
For file-related rules include:
... and additional specialized fields from the raw args if applicable, such as
newpath=%evt.arg.newpath for non-file descriptor events like symlinking or renaming. Alternatively, you can explore more recent
fs.path.* variants to simplify the consistent logging of file or directory paths, even for non-file descriptor events. Previously, tapping into the raw args as described above was required for such events.
When writing a rule for container workloads, you should include the fields we automatically fetch from the container runtime. Falco supports a special placeholder for this purpose:
This special placeholder will be automatically replaced with common container-related fields and, when used in conjunction with
-pk (see the output formatting page for more details) it will also add basic Kubernetes fields (this does not require the Kubernetes Metadata Enrichment, i.e.
-k/-K, functionality to be enabled). Using
%container.info in conjunction with
-pk is equivalent to:
container_id=%container.id container_image=%container.image.repository container_image_tag=%container.image.tag container_name=%container.name k8s_ns=%k8s.ns.name k8s_pod_name=%k8s.pod.name
All of these fields are incredibly crucial for effective incident response and play a vital role in determining the workload owner.
For specialized use cases, generic fields such as
%container.cni.json can be further helpful for incident response, especially concerning non-network syscall related alerts in Kubernetes. These fields can be correlated, for example, with network proxy logs. Additionally, for certain rules, it can be important to traverse the parent process lineage for up to 7 levels. In some cases, instead of relying solely on the process name, it might be more effective to traverse the exepath, for example,
proc.aexepath. The process name and executable of the session leader (
%proc.sid.*) and process group leader (
%proc.vpgid.*) can also be of high value.
We kindly ask you to add fields related to IDs later in your customization process to keep the upstream Falco output fields to a minimum. This is because there are many ID-related fields, such as
%proc.pid %proc.ppid %proc.vpid %proc.pvpid %proc.sid %proc.vpgid .... You can explore the
-p option for this purpose and add these fields to each rules' output fields.
Falco also supports outputting the output as a resolved string. Therefore, use a sentence style, first concisely re-iterating the rule's purpose, and then including the output field in parentheses after the
= character, with its meaning explained before the
= character, adhering to the snake_case variable naming convention.
output: > Read monitored file via directory traversal (file=%fd.name fileraw=%fd.nameraw gparent=%proc.aname ggparent=%proc.aname gggparent=%proc.aname user=%user.name user_uid=%user.uid user_loginuid=%user.loginuid process=%proc.name proc_exepath=%proc.exepath parent=%proc.pname command=%proc.cmdline terminal=%proc.tty exe_flags=%evt.arg.flags %container.info)
When considering upstreaming the rule to The Falco Project, the
priority level shall not be set to
DEBUG and instead shall be at a minimum of
Tags include various categories to convey relevant information about the rule.
According to the Falco rules maturity framework, the first tag in the tags list must always indicate the maturity of the rule. The rules repo contains concrete guidance on how to categorize a rule when considering upstreaming the rule to The Falco Project.
maturity_stable maturity_incubating maturity_sandbox maturity_deprecated
Next, the tags must indicate for what workloads this rule is relevant. Add
container if the rule works for any event. You can include additional tags to specify the rule's type, such as
When considering upstreaming your rule, we expect the Mitre Attack phase followed by the best Tactic or Technique, whichever is the best fit. This information is used to create an overview document of Falco's predefined rules and also help the Falco adoption process.
Lastly, if the rule is relevant for a compliance use case, please add the corresponding
NIST_* tag, referring to the Validating NIST Requirements with Falco and PCI/DSS Controls with Falco blog posts and rules contributing criteria outlined in the rules repo.
tags: [maturity_incubating, host, container, filesystem, mitre_defense_evasion, NIST_800-53_AU-10]
Rule Types and Robustness
Some rules are more specific signatures, while others focus on behavior-based detection. When testing rules, it's essential to consider not only if the rule catches the intended attack or how much noise it could generate but also its robustness. Robustness refers to how easily an attacker can bypass the detection by making minor changes to their payload or approach. Exploring different approaches to catch an attack can help identify the most effective detection method.
Refer to the up-to-date description in the falco.yaml file for
rules_file to understand in which order rules are loaded. Keep in mind that Falco applies rules per event type on a "first match wins" basis.
Contributing Your Falco Rules
Was this page helpful?
Let us know! You feedback will help us to improve the content and to stay in touch with our users.
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.