Falco, the open-source runtime security tool, can detect unexpected application behavior and potential threats within your containers.
Let’s see Falco in action, monitoring a Kubernetes cluster running on Google Kubernetes Engine (GKE).
Imagine you have a simple Nginx deployment in GKE. You’ve deployed it, and it’s running fine.
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
Now, we’ll deploy Falco to this same GKE cluster. The most straightforward way is using its Helm chart.
helm repo add falcosecurity https://falcosecurity.github.io/charts
helm repo update
helm install falco falcosecurity/falco --namespace falco --create-namespace
This installs Falco as a DaemonSet, meaning it runs on every node in your GKE cluster. It uses falcosecurity/falco as its container image, which bundles the Falco engine and its default rules.
Once deployed, Falco starts observing system calls across your nodes. It compares these calls against its predefined ruleset.
Consider a common security scenario: a container attempting to execute a shell. By default, many applications don’t need to spawn a shell. Falco flags this as suspicious.
Let’s simulate this. We’ll exec into our Nginx pod and try to run sh.
kubectl exec -it <nginx-pod-name> -- sh
Inside the pod, if you then try to execute sh (or any other shell like bash), Falco, running on the GKE node hosting this pod, will detect it.
Here’s a snippet of what you might see in the Falco logs (often found on the node itself or forwarded to a logging system):
{
"output": "10:05:00.123456789: Notice: User tries to execute shell inside container (user=root user_uid=0 user_loginuid=123456789 exe=/bin/sh exe_uid=0 comm=sh cwd=/ cmd=sh /bin/sh container_id=abcdef1234567890 container_name=nginx-deployment-xyz image=nginx:latest rule=Execute shell inside container)",
"priority": "Notice",
"rule": "Execute shell inside container",
"time": 1678886700123456789,
"output_fields": {
"container.id": "abcdef1234567890",
"container.image": "nginx:latest",
"container.name": "nginx-deployment-xyz",
"user.name": "root",
"user.uid": 0,
"proc.name": "sh",
"proc.exe": "/bin/sh",
"proc.cwd": "/",
"proc.cmdline": "sh /bin/sh"
},
"source": "syscall",
"tags": [
"execution",
"shell"
]
}
This event tells us that a process named sh was executed within the nginx-deployment-xyz container, which is running the nginx:latest image. The rule that triggered this alert is Execute shell inside container.
Falco works by leveraging eBPF (extended Berkeley Packet Filter) on Linux. It hooks into the kernel’s system call layer. This allows it to see exactly what processes are doing at the most fundamental level, without requiring agents inside each container.
The power of Falco lies in its rule engine. Rules are written in a YAML format and define conditions based on system call arguments, process attributes, network activity, and more.
- rule: Execute shell inside container
desc: An interactive shell was spawned in a container. This is often a sign of compromise.
condition: evt.type = execve and container.id != host and proc.name in (sh, bash, zsh, ksh, csh, tcsh) and proc.cwd != /proc/*
output: User tries to execute shell inside container (user=%user.name user_uid=%user.uid exe=%proc.exe comm=%proc.name cwd=%proc.cwd cmd=%proc.cmdline container_id=%container.id container_name=%container.name image=%container.image)
priority: Notice
tags: [execution, shell]
In this rule, evt.type = execve means we’re looking for the execution of a new program. container.id != host filters out events happening on the host itself, focusing on containers. proc.name in (sh, bash, zsh, ksh, csh, tcsh) specifically targets common shell executables. The and proc.cwd != /proc/* part is a subtle but important exclusion to avoid noisy alerts from processes interacting with the /proc filesystem in legitimate ways.
To configure Falco on GKE, you can modify its configuration via ConfigMaps or, more commonly, by customizing the Helm chart values during installation. For example, to change the output format or the list of monitored syscalls, you’d edit values.yaml for the Helm chart.
# values.yaml example for Helm
image:
repository: falcosecurity/falco
tag: 0.18.1 # Example tag, use the latest stable
# You can also specify custom rules or override existing ones
# customRules: |
# - rule: My custom detection
# desc: Detect specific unusual activity
# condition: ...
# output: ...
# priority: ...
# tags: [...]
When you run helm install falco falcosecurity/falco -f values.yaml, these settings are applied.
The most surprising thing about Falco’s detection mechanism is how it can infer complex behaviors from seemingly simple system calls. For instance, a rule might detect a web server process (nginx) attempting to write to a sensitive system directory (/etc/shadow). This isn’t a single "bad" syscall, but a sequence of open, write, and close calls on a file that nginx should never touch. Falco correlates these events based on process lineage and file access patterns to identify the anomaly.
The next step after setting up Falco is to integrate its alerts with your incident response workflow.