Falco, the cloud-native runtime security tool, can alert you when containers try to access sensitive files, but its real power isn’t just detecting bad behavior; it’s about understanding the intent behind that behavior.

Let’s see Falco in action. Imagine a container running a web application. We’ve configured Falco to watch for specific file access patterns.

Here’s a simplified example of a Falco rule that triggers on unexpected /etc directory access:

- rule: Suspicious /etc Access
  desc: "Detect unexpected access to files in /etc from a container."
  condition: container and proc.name != "my-webserver" and fd.name contains "/etc/" and fd.type in (file, dir)
  output: "Unexpected /etc access by %container.name (PID: %proc.pid) to %fd.name"
  priority: WARNING

Now, let’s simulate a malicious actor trying to read /etc/passwd from within that container.

# Inside the container
cat /etc/passwd

Falco, running on the host, would immediately generate an alert:

10:35:15.123456789: Warning: Unexpected /etc access by my-web-app-container (PID: 12345) to /etc/passwd

This alert tells us that a process within the my-web-app-container (with PID 12345) attempted to read /etc/passwd. The rule specifically excludes my-webserver from triggering this alert, meaning if the web server legitimately needs to read a file in /etc (which is rare but possible), it won’t noisy up our alerts.

The core problem Falco solves here is blind trust. By default, containers have broad access to the host filesystem, or at least a significant portion of it. Without runtime security, a compromised container can freely explore and exfiltrate sensitive data like credentials, configuration files, or secrets stored on the host. Falco bridges this gap by providing visibility into container activity and allowing us to define what "normal" looks like, alerting on deviations.

Internally, Falco uses kernel-level instrumentation (e.g., auditd or eBPF) to capture system calls. It then filters these calls through a set of rules defined in its configuration. When a system call matches a rule’s condition, the output is generated. For file access, Falco watches open, read, write, and stat syscalls, along with filesystem event information like fd.name (the file path) and fd.type (file or directory).

The exact levers you control are the condition and output fields in the rules. The condition is a powerful expression language that lets you combine various fields:

  • container: Filters for events originating from within a container.
  • proc.name: The name of the process making the system call.
  • fd.name: The name of the file or directory being accessed.
  • fd.type: Whether the access is to a file, dir, socket, etc.
  • user.name: The user ID or name.
  • k8s.ns.name, k8s.pod.name, k8s.container.name: Kubernetes-specific metadata.

You can create highly granular rules. For instance, you might want to alert if any process other than nginx tries to write to /etc/nginx/nginx.conf.

- rule: Unexpected Nginx Config Write
  desc: "Detect unauthorized modifications to the main Nginx configuration file."
  condition: >
    container and
    fd.name = "/etc/nginx/nginx.conf" and
    fd.type = file and
    write and
    proc.name != "nginx"
  output: "Unauthorized write to %fd.name by container %container.name (PID: %proc.pid, Process: %proc.name)"
  priority: CRITICAL

This rule uses write and proc.name != "nginx" to specifically catch unauthorized writes to the Nginx configuration file, ensuring the web server process itself is the only one allowed to modify it.

Most people know Falco alerts on suspicious file access, but they often miss that the fd.name field in Falco rules can be a regular expression. This allows for incredibly flexible pattern matching beyond simple exact matches. For example, to catch access to any file within /var/lib/mysql/ that isn’t part of a legitimate MySQL process, you could use something like:

- rule: Suspicious MySQL Data Dir Access
  desc: "Detect non-MySQL processes accessing sensitive MySQL data files."
  condition: >
    container and
    fd.name matches "/var/lib/mysql/.*" and
    fd.type = file and
    proc.name != "mysqld" and
    proc.name != "mysqladmin" and
    proc.name != "mysqldump"
  output: "Non-MySQL process %proc.name (PID: %proc.pid) accessed MySQL data file %fd.name in container %container.name."
  priority: WARNING

Here, fd.name matches "/var/lib/mysql/.*" will trigger for any file or directory directly under /var/lib/mysql/, and the proc.name != clauses ensure we only alert when it’s not a known MySQL process.

The next logical step after configuring file access alerts is to explore network-related security events.

Want structured learning?

Take the full Falco course →