Falco’s default output is a human-readable text format, which is great for quick debugging but doesn’t play well with automated analysis or forwarding to SIEMs. The real power comes when you configure Falco to emit structured JSON events, which can then be sent to various destinations like syslog or a gRPC endpoint for real-time processing.
Let’s see Falco in action, spitting out JSON. Imagine we have a rule that fires when a new process is spawned with a specific name.
Here’s a simple Falco rule (rules/test-rules.yml):
- rule: Spawned Test Process
desc: Detects when a process named 'my-test-app' is spawned
condition: proc.name = "my-test-app"
output: "A test process was spawned: %proc.name %proc.cmdline"
priority: INFO
Now, let’s configure Falco to output this rule’s findings in JSON format to a local syslog server.
First, edit your falco.yaml configuration file. You’ll want to uncomment and configure the json_output and log_output directives.
# falco.yaml
# ... other configurations ...
json_output: true
log_output:
- syslog
# - file # You can combine outputs, but let's focus on syslog for now
# If you are using syslog, you might need to specify the address.
# The default is usually localhost on the standard syslog port.
# syslog:
# address: localhost:514
# # If you need to use TCP instead of UDP:
# # protocol: tcp
# ... other configurations ...
With this configuration, when my-test-app is executed (e.g., touch /tmp/trigger_falco_rule && my-test-app), Falco will generate output like this, sent to your syslog daemon:
{
"output": "A test process was spawned: my-test-app",
"priority": "info",
"rule": "Spawned Test Process",
"time": "2023-10-27T10:30:00.123456789Z",
"output_fields": {
"proc.cmdline": "my-test-app",
"proc.name": "my-test-app"
},
"fd.name": "",
"fd.type": "",
"k8sinfo": {
"container_id": "...",
"container_name": "...",
"namespace": "...",
"pod_name": "..."
},
"user.name": "root",
"container.name": "...",
"container.runtime": "docker",
"container.id": "...",
"container.image": "...",
"container.mount_id": "...",
"evt.args": "...",
"evt.category": "process",
"evt.cpu_core": 0,
"evt.id": 1234,
"evt.name": "execve",
"evt.time": 1698393000123456789,
"evt.type": "open",
"pprof.mutex": "",
"pprof.wait_ns": 0,
"thread.id": 12345,
"thread.name": "my-test-app",
"user.group": "root",
"user.group.id": 0,
"user.id": 0,
"veth.name": ""
}
Notice the output_fields section. This is where Falco puts the values that were captured by your rule’s output string (%proc.name, %proc.cmdline). All the other fields are context Falco automatically gathers.
The gRPC Channel: For Real-Time Streaming
Syslog is great for batching and standard logging, but for true real-time, low-latency event processing, Falco can stream events directly over gRPC. This is commonly used to feed events into a dedicated security analytics engine or a custom application that needs immediate notification.
To configure gRPC output, you’ll again modify falco.yaml:
# falco.yaml
# ... other configurations ...
json_output: true # gRPC output also requires JSON
log_output:
- grpc
grpc:
# The address of your gRPC server. This is typically where your
# event collector or SIEM endpoint is listening.
# Example: "your-grpc-server.example.com:50051"
address: "localhost:50051" # Replace with your actual gRPC server address
# You can also specify a timeout for the connection
# timeout: "5s"
# ... other configurations ...
When configured this way, Falco will attempt to establish a gRPC connection to localhost:50051 and stream the JSON-formatted events as they are detected. The client on the other end of the gRPC stream would be responsible for receiving, parsing, and acting upon these events.
The Mental Model: Event Pipeline
Think of Falco’s output configuration as defining stages in an event pipeline.
- Detection: Falco’s kernel module or userspace agent detects a system event matching a rule.
- Rule Matching: The event is compared against your defined rules.
- Output Formatting: If a rule matches, Falco formats the event data.
json_output: trueis crucial here, as it tells Falco to structure this data into a JSON object. - Output Routing: The
log_outputdirective determines where this formatted JSON data goes.syslog: The JSON object is sent as a single string message to a configured syslog daemon.grpc: The JSON object is sent as a payload within a gRPC call to a specified endpoint.file: The JSON object is written as a line to a specified file.
Each log_output target can be configured with its own specific parameters (like address for syslog or gRPC). You can even have multiple outputs enabled simultaneously (e.g., log_output: [syslog, file]).
The Counterintuitive Lever: output_fields vs. output
It’s common to see the output field in Falco rules and assume it’s the only way to get specific data into the event. However, the output string in the rule definition is primarily for human-readable messages and also defines which fields are explicitly pulled into the output_fields map within the JSON output. All other fields that appear in the JSON are automatically populated by Falco based on the event context, regardless of what’s in your output string. This means you can have rich contextual data available in the JSON event even if you only specify a very simple message in your rule’s output field. The real power is in understanding that output_fields mirrors your output string’s placeholders, but the entire JSON object is packed with system context.
The next step after getting your events out is to parse and act on them efficiently, which often involves understanding how to filter and enrich these events on the receiving end.