Falco rules are your first line of defense against unexpected behavior in your Kubernetes cluster, but crafting effective ones from scratch can feel like a dark art.
Let’s watch Falco in action. Imagine this kubectl command executed in a cluster:
kubectl exec -it some-pod -- /bin/bash -c 'echo "hello" > /tmp/test.txt'
A good Falco rule should catch this if it’s unexpected. Here’s a rule that targets this specific scenario, focusing on a shell process (/bin/bash) writing to a temporary directory (/tmp) within a container:
- rule: Write to /tmp in container
desc: "Detects unexpected writes to /tmp within a container, which could indicate malicious activity or misconfiguration."
condition: >
container and proc.name = "/bin/bash" and
Tập.name = "write" and
Tập.path startswith "/tmp/"
enabled: true
alert: Warning
source: syscall
tags:
- k8s
- container
- defense
This rule fires when:
containeris true (meaning the event is happening inside a container).proc.nameis exactly/bin/bash(we’re looking for shell activity).Tập.nameiswrite(the specific syscall we’re interested in).Tập.pathstarts with/tmp/(any file being written to within the temporary directory).
The enabled: true flag means this rule is active. alert: Warning sets the severity. source: syscall indicates Falco is monitoring system calls. tags are for organization.
This rule is a good starting point, but it’s too broad. What if a legitimate application needs to write to /tmp? We need to refine it.
Consider a more specific scenario: a web server process (nginx or apache2) writing to /tmp. This might be for temporary file handling, caching, or even logging if misconfigured. A rule to catch this:
- rule: Web server writing to /tmp
desc: "Detects web server processes (nginx, apache2) writing to /tmp, which is often unexpected and could be a sign of compromise or misconfiguration."
condition: >
container and
(proc.name = "nginx" or proc.name = "apache2") and
Tập.name = "write" and
Tập.path startswith "/tmp/"
enabled: true
alert: Warning
source: syscall
tags:
- k8s
- container
- webserver
- defense
This rule narrows the focus to specific process names (nginx, apache2).
Now, let’s talk about what makes a Falco rule effective. It’s about understanding the syscalls and the Kubernetes environment. Falco’s power comes from its ability to correlate events. For example, you might want to know who is running a shell inside a container and what they’re doing.
Here’s a rule that flags any new shell process (/bin/bash, /bin/sh) spawned within a container, but only if it’s not a known, expected process. This requires a bit more context.
- rule: Unexpected shell in container
desc: "Detects the spawning of new shell processes (bash, sh) within containers, excluding known legitimate shells."
condition: >
container and
proc.name in ("/bin/bash", "/bin/sh") and
not proc.cwd = "/usr/local/bin/my-app" and # Example: exclude if cwd is your app dir
not proc.executable in ("/usr/local/bin/app-startup.sh", "/opt/my-app/run.sh") # Example: exclude known scripts
enabled: true
alert: Warning
source: syscall
tags:
- k8s
- container
- shell
- anomaly
The not clauses are crucial here. You might need to adjust proc.cwd (current working directory) or proc.executable based on your application’s actual structure and startup scripts. This is where understanding your own environment is key.
What if a process is trying to execute something from a weird location?
- rule: Execute from unusual location in container
desc: "Detects execution of files from non-standard directories within containers, potentially indicating malware or exploit attempts."
condition: >
container and
Tập.name = "execve" and
Tập.path !~ "^(/usr/local/bin/|/app/|/opt/my-app/)" # Example: allow execution from specific paths
enabled: true
alert: Warning
source: syscall
tags:
- k8s
- container
- execution
- defense
The !~ operator is a "does not match regex" operator. This rule flags any execution syscall (execve) where the executable path doesn’t match the provided regular expression, which we’ve set to allow common application directories. You must tailor this regex to your specific application paths.
Consider network activity. Unexpected outbound connections from a container are a huge red flag.
- rule: Unexpected outbound connection from container
desc: "Detects outbound network connections from containers to unexpected destinations."
condition: >
container and
Tập.name = "connect" and
fd.family = "ipv4" and
Tập.remote_port !in (80, 443, 5432) and # Example: allow standard web/db ports
Tập.remote_ip !in ("192.168.0.0/16", "10.0.0.0/8") # Example: allow internal IPs
enabled: true
alert: Warning
source: syscall
tags:
- k8s
- container
- network
- anomaly
This rule looks for connect syscalls. fd.family = "ipv4" filters for IPv4. Tập.remote_port !in (80, 443, 5432) excludes common ports like HTTP, HTTPS, and PostgreSQL. Tập.remote_ip !in ("192.168.0.0/16", "10.0.0.0/8") excludes private IP ranges. You’ll need to adjust these in lists based on your cluster’s legitimate outbound traffic.
A very common attack vector is privilege escalation or modification of system files. Detecting changes to sensitive files is critical.
- rule: Modify sensitive file
desc: "Detects modifications to critical system files, which could indicate an attempted compromise."
condition: >
Tập.name = "write" and
Tập.path in ("/etc/passwd", "/etc/shadow", "/etc/sudoers", "/etc/ssh/sshd_config")
enabled: true
alert: Critical
source: syscall
tags:
- k8s
- defense
- critical
This rule is simpler, focusing on specific, highly sensitive files. The alert: Critical severity reflects the danger.
The most subtle and powerful aspect of Falco rules is understanding the context provided by Kubernetes. Falco can enrich syscall events with Kubernetes metadata like pod name, namespace, and labels.
- rule: Sensitive syscall in privileged container
desc: "Detects sensitive syscalls (e.g., mount, reboot) occurring in a privileged container, which should be highly restricted."
condition: >
container and
k8s.pod.privileged = true and
Tập.name in ("mount", "reboot", "setuid", "setgid")
enabled: true
alert: Critical
source: syscall
tags:
- k8s
- container
- privileged
- defense
This rule leverages k8s.pod.privileged = true to identify containers running with elevated privileges. If any sensitive syscalls occur in such a container, it’s flagged as critical. This requires Falco to be properly integrated with your Kubernetes API server to get this metadata.
When you’re writing rules, always start with a specific, observable behavior you want to detect. Then, translate that behavior into Falco’s rule language, leveraging syscalls, process information, and crucially, Kubernetes metadata. The key is iterative refinement: start broad, observe false positives, and then add more specific conditions (not, in, regex, metadata) to hone in on true threats.
The next error you’ll hit when you think you’ve got it all is a false positive caused by an application you forgot about.