Falco Runtime Security
Overview
Falco is a cloud-native runtime security tool originally created by Sysdig and now a CNCF graduated project. It detects unexpected application behavior and alerts on threats at runtime by monitoring Linux system calls made by containers and hosts. Falco is the primary runtime security tool tested on the CKS exam.
CKS Exam Relevance
Falco is heavily tested on the CKS exam. You should be able to:
- Understand Falco's architecture and how it detects threats
- Read and interpret Falco rules
- Write custom Falco rules
- Investigate Falco alerts to identify the source of threats
- Modify Falco configuration and rules files
What Falco Is and How It Works
Falco monitors the behavior of your applications by tapping into the Linux kernel's system call interface. Every time a process in a container (or on the host) makes a system call -- opening a file, establishing a network connection, executing a binary -- Falco sees it and evaluates it against a set of security rules.
If a system call matches a rule condition, Falco generates an alert with details about what happened, who did it, and where.
Key Capabilities
- Detect shell execution inside containers
- Detect reads/writes to sensitive files (
/etc/shadow,/etc/passwd) - Detect privilege escalation attempts
- Detect unexpected network connections
- Detect container drift (new binaries executed)
- Detect crypto mining processes
- Detect reverse shells
Falco Architecture
Architecture Components
| Component | Role |
|---|---|
| Kernel Module / eBPF Probe | Captures system calls at the kernel level with minimal overhead |
| libscap | Captures raw system call events from the kernel module/eBPF |
| libsinsp | Enriches raw events with metadata (container name, pod name, namespace) |
| Rule Engine | Evaluates enriched events against Falco rules |
| Rules Files | Define conditions and output for security violations |
| Output Channels | Send alerts via stdout, files, syslog, HTTP webhooks, or gRPC |
Kernel Module vs. eBPF Probe
| Feature | Kernel Module | eBPF Probe |
|---|---|---|
| Performance | Excellent | Excellent |
| Security | Requires kernel module loading | Safer -- no kernel module needed |
| Compatibility | Broad kernel support | Requires kernel 4.14+ |
| Installation | Needs kernel headers | No kernel headers needed |
| Preferred for | Legacy systems | Modern systems (recommended) |
Exam Tip
On the CKS exam, Falco will typically be pre-installed. You do not need to install it from scratch, but you should know where its configuration and rules files are located.
Installing Falco
While Falco is pre-installed in the exam, understanding the installation process helps you know the file locations.
Installation on a Node
# Add the Falco repository
curl -fsSL https://falco.org/repo/falcosecurity-packages.asc | \
sudo gpg --dearmor -o /usr/share/keyrings/falco-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/falco-archive-keyring.gpg] https://download.falco.org/packages/deb stable main" | \
sudo tee /etc/apt/sources.list.d/falcosecurity.list
# Install Falco
sudo apt-get update
sudo apt-get install -y falco
# Start Falco
sudo systemctl enable falco
sudo systemctl start falco
# Verify Falco is running
sudo systemctl status falcoKey File Locations
| Path | Description |
|---|---|
/etc/falco/falco.yaml | Main Falco configuration file |
/etc/falco/falco_rules.yaml | Default rules (do not modify) |
/etc/falco/falco_rules.local.yaml | Custom rules (add your rules here) |
/etc/falco/rules.d/ | Directory for additional rule files |
/var/log/syslog or journalctl -u falco | Falco log output |
Falco Rules Syntax and Structure
Falco rules use a YAML-based domain-specific language. Each rule file can contain three types of elements: rules, macros, and lists.
Rule Processing Flow
Lists
Lists are named collections of values that can be referenced in macros and rules.
- list: sensitive_file_names
items:
- /etc/shadow
- /etc/passwd
- /etc/pam.conf
- /etc/pam.d
- list: allowed_shells
items:
- bash
- sh
- zsh
- dash
- list: package_managers
items:
- apt
- apt-get
- dpkg
- yum
- rpm
- pip
- pip3
- npmMacros
Macros are reusable condition fragments that simplify complex rules.
- macro: container
condition: (container.id != host)
- macro: spawned_process
condition: (evt.type in (execve, execveat) and evt.dir=<)
- macro: sensitive_files
condition: (fd.name startswith /etc and fd.name in (sensitive_file_names))
- macro: shell_procs
condition: (proc.name in (allowed_shells))
- macro: open_write
condition: >
evt.type in (open, openat, openat2) and
evt.is_open_write=true and
fd.typechar='f'
- macro: never_true
condition: (evt.num=0)
- macro: always_true
condition: (evt.num>=0)Rules
Rules combine lists and macros into complete detection conditions with formatted output.
- rule: <name>
desc: <description>
condition: <filter expression>
output: <output format string with %fields>
priority: <severity level>
tags: [<tag1>, <tag2>]
enabled: true|false
source: syscall|k8s_auditPriority Levels
| Priority | Level | Use Case |
|---|---|---|
EMERGENCY | 0 | System is unusable |
ALERT | 1 | Action must be taken immediately |
CRITICAL | 2 | Critical conditions |
ERROR | 3 | Error conditions |
WARNING | 4 | Warning conditions |
NOTICE | 5 | Normal but significant condition |
INFORMATIONAL | 6 | Informational messages |
DEBUG | 7 | Debug-level messages |
Output Fields
Common output fields you can use in rule output strings:
| Field | Description |
|---|---|
%evt.time | Event timestamp |
%proc.name | Process name |
%proc.pname | Parent process name |
%proc.cmdline | Full command line |
%proc.pcmdline | Parent command line |
%user.name | User name |
%user.uid | User ID |
%container.id | Container ID |
%container.name | Container name |
%container.image.repository | Container image name |
%fd.name | File descriptor name (file path or network address) |
%evt.type | System call type |
%k8s.pod.name | Kubernetes pod name |
%k8s.ns.name | Kubernetes namespace |
Writing Custom Falco Rules
Rule 1: Detect Shell Spawned in Container
- rule: Terminal shell in container
desc: >
Detect a shell (bash, sh, etc.) being spawned inside a container.
This is a common indicator of compromise -- attackers often spawn
interactive shells after exploiting a vulnerability.
condition: >
spawned_process and
container and
shell_procs
output: >
Shell spawned in container
(user=%user.name user_uid=%user.uid shell=%proc.name
parent=%proc.pname cmdline=%proc.cmdline
container_id=%container.id container_name=%container.name
image=%container.image.repository
pod=%k8s.pod.name ns=%k8s.ns.name)
priority: WARNING
tags: [container, shell, mitre_execution]Rule 2: Detect Write to Sensitive Files
- rule: Write below etc
desc: >
Detect any write to files under /etc inside a container.
Configuration files should not be modified at runtime in
properly designed immutable containers.
condition: >
open_write and
container and
fd.name startswith /etc
output: >
File below /etc modified in container
(user=%user.name user_uid=%user.uid file=%fd.name
process=%proc.name command=%proc.cmdline
container_id=%container.id container_name=%container.name
image=%container.image.repository
pod=%k8s.pod.name ns=%k8s.ns.name)
priority: ERROR
tags: [container, filesystem, mitre_persistence]Rule 3: Detect Privilege Escalation
- rule: Container privilege escalation via setuid
desc: >
Detect a process calling setuid or setgid to escalate privileges
inside a container. This is a common technique used by attackers
after gaining initial access.
condition: >
evt.type in (setuid, setgid) and
container and
evt.dir=< and
not (user.name=root)
output: >
Privilege escalation detected in container
(user=%user.name uid=%user.uid target_uid=%evt.arg.uid
process=%proc.name command=%proc.cmdline
container_id=%container.id container_name=%container.name
image=%container.image.repository
pod=%k8s.pod.name ns=%k8s.ns.name)
priority: CRITICAL
tags: [container, privilege_escalation, mitre_privilege_escalation]Rule 4: Detect Package Manager Execution
- rule: Package manager launched in container
desc: >
Detect the execution of a package manager (apt, yum, pip, etc.)
inside a container. Containers should be immutable and should
not install packages at runtime.
condition: >
spawned_process and
container and
proc.name in (package_managers)
output: >
Package manager launched in container
(user=%user.name package_manager=%proc.name
command=%proc.cmdline
container_id=%container.id container_name=%container.name
image=%container.image.repository
pod=%k8s.pod.name ns=%k8s.ns.name)
priority: ERROR
tags: [container, software_mgmt, mitre_persistence]Rule 5: Detect Outbound Connection to Unusual Port
- list: allowed_outbound_ports
items: [53, 80, 443, 8080, 8443]
- rule: Unexpected outbound connection
desc: >
Detect an outbound network connection from a container to a port
not in the allowed list. This can indicate data exfiltration,
command-and-control communication, or crypto mining.
condition: >
evt.type=connect and
evt.dir=< and
container and
fd.typechar=4 and
fd.ip != "0.0.0.0" and
not (fd.sport in (allowed_outbound_ports))
output: >
Unexpected outbound connection from container
(process=%proc.name command=%proc.cmdline
connection=%fd.name
container_id=%container.id container_name=%container.name
image=%container.image.repository
pod=%k8s.pod.name ns=%k8s.ns.name)
priority: WARNING
tags: [container, network, mitre_command_and_control]Rule 6: Detect Sensitive File Read
- rule: Read sensitive file
desc: >
Detect a process reading a sensitive file such as /etc/shadow
or /etc/passwd inside a container.
condition: >
evt.type in (open, openat, openat2) and
evt.is_open_read=true and
container and
fd.name in (/etc/shadow, /etc/passwd, /etc/sudoers)
output: >
Sensitive file read in container
(user=%user.name file=%fd.name process=%proc.name
command=%proc.cmdline
container_id=%container.id container_name=%container.name
image=%container.image.repository
pod=%k8s.pod.name ns=%k8s.ns.name)
priority: WARNING
tags: [container, filesystem, mitre_credential_access]Rule 7: Detect Process Running as Root
- rule: Non-root container running as root
desc: >
Detect a new process running as root (UID 0) inside a container
that should not require root access.
condition: >
spawned_process and
container and
user.uid=0 and
not (container.image.repository in (allowed_root_images))
output: >
Process running as root in container
(user=%user.name uid=%user.uid process=%proc.name
command=%proc.cmdline
container_id=%container.id container_name=%container.name
image=%container.image.repository
pod=%k8s.pod.name ns=%k8s.ns.name)
priority: NOTICE
tags: [container, process, mitre_execution]
- list: allowed_root_images
items: []Default Rules That Matter for CKS
Falco ships with many default rules. These are the most relevant for the CKS exam:
| Rule Name | What It Detects |
|---|---|
Terminal shell in container | Interactive shell in a container |
Write below etc | Write to /etc directory in a container |
Read sensitive file untouched | Read of /etc/shadow, etc. |
Write below binary dir | Write to /bin, /usr/bin, etc. |
Change thread namespace | Namespace escape attempts |
Launch Privileged Container | Privileged container started |
Contact K8S API Server From Container | Container accessing the API server |
Unexpected outbound connection destination | Connection to unusual endpoints |
Launch Package Management Process in Container | Package manager in a container |
Mkdir binary dirs | Creating directories in /bin, /usr/bin |
Launch Sensitive Mount Container | Container with sensitive host mounts |
Launch Remote File Copy Tools in Container | wget, curl, scp in containers |
Do Not Modify Default Rules
Never edit /etc/falco/falco_rules.yaml directly. Instead, override or add rules in /etc/falco/falco_rules.local.yaml or files in /etc/falco/rules.d/. If you need to disable a default rule, override it in the local file with enabled: false.
Disabling a Default Rule
# In /etc/falco/falco_rules.local.yaml
- rule: Terminal shell in container
desc: Override to disable this rule
condition: never_true
output: Shell spawned in container
priority: WARNING
enabled: falseOverriding a Default Rule's Output
# In /etc/falco/falco_rules.local.yaml
- rule: Terminal shell in container
desc: Override with custom output
condition: >
spawned_process and container and shell_procs
output: >
CUSTOM ALERT - Shell in container
(user=%user.name container=%container.name
pod=%k8s.pod.name ns=%k8s.ns.name
command=%proc.cmdline image=%container.image.repository)
priority: CRITICAL
tags: [custom, container, shell]Falco Outputs and Alerting
Falco supports multiple output channels configured in /etc/falco/falco.yaml.
stdout Output (Default)
# In /etc/falco/falco.yaml
stdout_output:
enabled: trueFile Output
file_output:
enabled: true
keep_alive: false
filename: /var/log/falco/falco_alerts.logSyslog Output
syslog_output:
enabled: trueHTTP Output (Webhook)
http_output:
enabled: true
url: "http://some-webhook-endpoint:8080/alerts"
user_agent: "falcosecurity/falco"gRPC Output
grpc:
enabled: true
bind_address: "unix:///run/falco/falco.sock"
threadiness: 8
grpc_output:
enabled: trueFalco Configuration File
The main configuration file is /etc/falco/falco.yaml. Key settings:
# Rule files to load (in order)
rules_file:
- /etc/falco/falco_rules.yaml
- /etc/falco/falco_rules.local.yaml
- /etc/falco/rules.d
# Watch config and rule files for changes
watch_config_files: true
# Time format for output
time_format_iso_8601: false
# JSON output format (useful for parsing)
json_output: true
json_include_output_property: true
json_include_tags_property: true
# Log level for Falco itself
log_stderr: true
log_syslog: true
log_level: info
# Priority threshold -- only output rules at this level or higher
priority: debug
# Buffer configuration
syscall_buf_size_preset: 4
# Output channels
stdout_output:
enabled: true
syslog_output:
enabled: true
file_output:
enabled: false
keep_alive: false
filename: /opt/falco/events.txt
http_output:
enabled: false
url: "http://localhost:8080"
# Metadata collection
metadata_download:
max_mb: 100
chunk_wait_us: 1000
watch_freq_sec: 1Exam Tip
In the CKS exam, you may need to:
- Change the output file path in
falco.yaml - Enable JSON output for better log parsing
- Add a custom rules file to the
rules_filelist - Restart Falco after any configuration change:
systemctl restart falco
Working with Falco in the Exam
Common Commands
# Check Falco service status
systemctl status falco
# Restart Falco after config/rule changes
systemctl restart falco
# View Falco alerts in real time
journalctl -u falco -f
# View alerts from output file
tail -f /var/log/falco/falco_alerts.log
# Validate a rules file
falco -V /etc/falco/falco_rules.local.yaml
# Run Falco with a specific rules file (foreground)
falco -r /etc/falco/custom_rules.yaml
# Run Falco in dry-run mode (validate config only)
falco --dry-run
# List loaded rules
falco --listTypical Exam Workflow
- Read the Falco alert to understand what was detected
- Identify the rule that triggered (check default or custom rules)
- Find the offending pod/container from the alert output
- Investigate the pod specification or runtime behavior
- Apply the fix (modify pod spec, add security context, etc.)
- Verify the fix by checking Falco alerts
Example: Investigating a Falco Alert
# 1. Check recent Falco alerts
journalctl -u falco --since "5 minutes ago" --no-pager
# Example output:
# WARNING Shell spawned in container
# (user=root shell=bash parent=nginx cmdline=bash
# container_id=abc123 container_name=web
# image=nginx pod=web-pod ns=default)
# 2. Identify the pod
kubectl get pod web-pod -n default -o yaml
# 3. Check what's running in the container
kubectl exec web-pod -n default -- ps aux
# 4. Apply fix (e.g., add readOnlyRootFilesystem)
kubectl edit pod web-pod # or recreate with fixSummary
| Concept | Key Point |
|---|---|
| What Falco does | Monitors system calls to detect runtime threats |
| How it works | Kernel module/eBPF captures syscalls, rule engine evaluates |
| Rules location | Default: /etc/falco/falco_rules.yaml, Custom: /etc/falco/falco_rules.local.yaml |
| Config location | /etc/falco/falco.yaml |
| Rule components | Lists (values), Macros (reusable conditions), Rules (full detection) |
| Priority levels | EMERGENCY through DEBUG (8 levels) |
| Output channels | stdout, file, syslog, HTTP webhook, gRPC |
| After changes | Always restart: systemctl restart falco |