The core difference between Falco’s eBPF driver and its kernel module boils down to how they hook into the Linux kernel to observe system calls.
Let’s see this in action. Imagine we have a simple Go program that spins up a new process:
package main
import (
"fmt"
"os/exec"
)
func main() {
fmt.Println("Starting a new process...")
cmd := exec.Command("sleep", "5")
err := cmd.Start()
if err != nil {
fmt.Printf("Error starting process: %v\n", err)
return
}
fmt.Printf("Process started with PID: %d\n", cmd.Process.Pid)
cmd.Wait()
fmt.Println("Process finished.")
}
When we run this, Falco, if configured to use the eBPF driver, will generate an event for the execve syscall. The eBPF program, loaded into the kernel, will intercept this syscall before it’s fully processed by the kernel and send a structured event to the Falco userspace daemon. The kernel module, on the other hand, achieves a similar outcome but relies on a loadable kernel module that directly inserts itself into kernel code paths.
The problem Falco solves is providing a security observability layer that can detect anomalous behavior by analyzing system calls. It acts as a "security camera" for your Linux system, alerting you to suspicious activities like unexpected process creation, network connections, or file modifications. The "probe" is the mechanism Falco uses to see these system calls.
The eBPF driver leverages Extended Berkeley Packet Filter (eBPF), a technology that allows sandboxed programs to run in the kernel without changing kernel source code or loading kernel modules. These eBPF programs are verified by the kernel for safety before execution. For Falco, this means an eBPF program is attached to specific kernel tracepoints or kprobes. When a syscall occurs, the eBPF program is triggered, extracts relevant information (like the syscall number, arguments, and process context), and then passes this data to a perf buffer, which the Falco userspace daemon reads. The kernel module, in contrast, is a traditional Linux kernel module (.ko file) that is loaded into the kernel using insmod. It uses kernel internal APIs, often involving kprobes or directly patching kernel functions, to intercept syscalls and send data back to userspace.
The most surprising true thing about eBPF is that it allows you to run complex, user-defined logic within the kernel, with performance characteristics close to native kernel code, but without the inherent risks of loading arbitrary kernel modules. It’s like having a secure, programmable extension point deep inside the operating system.
Here’s how the mental model breaks down:
- System Calls (Syscalls): The fundamental interface between user-space programs and the Linux kernel. Everything a program does, from opening a file to creating a process, eventually translates to a syscall.
- Probes (eBPF vs. Kernel Module): These are the "listeners" Falco deploys.
- eBPF Driver: A small, safe eBPF program loaded into the kernel. It’s attached to syscall entry/exit points. It’s verified by the kernel’s verifier before running, ensuring it won’t crash the kernel. It’s generally preferred for its safety and flexibility.
- Kernel Module Driver: A traditional
.kofile loaded into the kernel. It has more direct access to kernel internals but comes with higher risks if buggy or malicious.
- Falco Daemon: The userspace component that receives events from the probe, applies rules (written in YAML), and triggers alerts.
- Rules: Define what constitutes suspicious behavior. For example, a rule might alert if
execveis called with a binary path like/bin/shfrom a web server process.
The primary levers you control are:
- Which driver to use:
eBPF(default and recommended) orkernel_module. This is set infalco.yaml. - Rule configuration: You define what events Falco should monitor and alert on.
- Event filtering: Falco can filter events at the source (eBPF/kernel module) to reduce noise, though most filtering is done in rules.
The one thing most people don’t fully grasp is that eBPF programs can be dynamically updated without rebooting or even unloading/reloading modules. When Falco updates its eBPF probe, the kernel verifies and loads the new program, and the old one is discarded. This is a stark contrast to kernel modules, which require explicit loading/unloading and can sometimes cause system instability if not handled carefully.
The next concept you’ll likely encounter is understanding Falco’s rich rule language and how to craft effective detection strategies.