Skip to content
Go back

Falco: Catch Container Attacks at Runtime

By SumGuy 5 min read
Falco: Catch Container Attacks at Runtime

Your container scanner said clean. Your firewall said fine. Your image passed every security gate. And then someone exec’d into your postgres container and ran bash. You’d never know—not until it was too late.

This is where most container security strategies fall apart. You can scan images for vulnerabilities all day long, but the real danger happens at runtime. A zero-day exploit. A compromised CI/CD pipeline. An insider running commands they shouldn’t. Your scanner won’t catch any of that. Falco does.

What Falco Is (and Why You Need It)

Falco is an open-source runtime security tool that watches every syscall your containers make and alerts you when something sketchy happens. It’s built on eBPF (extended Berkeley Packet Filter)—the same technology that powers observability tools like Cilium and Datadog. Instead of just looking at what’s inside a container, Falco watches what a container is doing.

Here’s the thing: once a container is running, image scanning is basically meaningless. You need visibility into runtime behavior. Is your nginx container suddenly trying to read /etc/shadow? Did your application spawn a shell it shouldn’t? Did someone escalate privileges? Falco catches all of it and throws an alert your way.

The default ruleset covers the common attack patterns:

And honestly, you can trust it. Falco isn’t some noisy security theater tool. The rules are tight, the community maintains them, and you can tune them to your environment.

Installing Falco (Docker Approach)

For local testing or small setups, the easiest path is Docker. If you’re running Kubernetes, use Helm (I’ll mention that, but Docker Compose is simpler to demo).

Here’s a docker-compose.yml that runs Falco and monitors your other containers:

docker-compose.yml
version: '3.8'
services:
falco:
image: falcosecurity/falco:latest
container_name: falco
privileged: true
volumes:
- /var/lib/docker/containers:/var/lib/docker/containers:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
- /sys:/sys:ro
- /dev:/dev:ro
- /proc:/proc:ro
- ./falco-rules.yaml:/etc/falco/rules.d/custom.yaml:ro
environment:
- FALCO_LOG_LEVEL=info
command: /usr/bin/falco -o json_output=true
nginx:
image: nginx:latest
container_name: web
ports:
- "8080:80"

A few things to note here:

Fire it up:

Terminal window
docker-compose up -d

Falco will start streaming logs. You should see it loading the default ruleset and listening for container activity.

Testing Falco (Making It Scream)

Here’s where it gets fun. Falco is useless if you don’t trust it, so let’s trigger an alert.

In another terminal, exec into the nginx container and try to read a sensitive file:

Terminal window
docker exec -it web cat /etc/passwd

Now check the Falco logs:

Terminal window
docker logs falco

You should see a wall of JSON alerts. Falco caught:

  1. The docker exec command itself (interactive shell in container)
  2. The cat /etc/passwd attempt (sensitive file read)
  3. Every syscall in between

Each alert includes:

That’s Falco doing its job. At 3 AM, when someone’s actually trying to get into your postgres container, this is the alert that wakes you up.

Writing a Custom Rule

The default rules are great, but sometimes you need to catch something specific. Let’s create a rule that detects any container reading /etc/shadow:

falco-rules.yaml
- rule: Unauthorized Shadow File Read
desc: Detect attempts to read /etc/shadow inside a container
condition: >
open_read and
fd.name = /etc/shadow and
container
output: >
CRITICAL: Container attempted to read /etc/shadow
(user=%user.name command=%proc.name container=%container.name)
priority: CRITICAL
tags: [privilege_escalation, sensitive_file_access]

Drop this into the volume mount path (we mapped ./falco-rules.yaml:/etc/falco/rules.d/custom.yaml), restart Falco, and now any container reading /etc/shadow triggers a CRITICAL alert.

The rule language is readable. You’re matching:

Falco comes with ~200 default rules, and the syntax is documented. You can get as granular as you want.

Routing Alerts Somewhere Useful

Falco can output to:

For real setups, use Falco Sidekick—a little sidecar that takes Falco alerts and routes them to Slack, PagerDuty, email, etc.

Add this to your Compose stack:

docker-compose.yml (sidekick addition)
falco-sidekick:
image: falcosecurity/falcosidekick:latest
container_name: falco-sidekick
ports:
- "2801:2801"
environment:
SLACK_WEBHOOK_URL: https://hooks.slack.com/services/YOUR/WEBHOOK/URL
command: /usr/bin/falcosidekick

Then configure Falco to send alerts to Sidekick via HTTP, and Sidekick forwards to Slack. Your team gets Slack notifications when something sketchy happens. You wake up to a red alert at 3 AM instead of discovering a breach in the morning.

The Reality Check

Falco isn’t magic. It can’t prevent attacks—it only detects them. But detection at runtime, when an attack is actually happening, is infinitely more valuable than scanning images after they’re built. By the time an attacker is exec’ing into your containers, they’ve already bypassed your other defenses. Falco is your last line of sight.

Set it up. Tune the rules. Route the alerts somewhere you’ll actually see them. Because your 2 AM self will appreciate it when Slack wakes you up before the data is gone.


Share this post on:

Send a Webmention

Written about this post on your own site? Send a webmention and it'll show up above once verified.


Previous Post
Gitea vs Forgejo vs GitLab CE: Self-Hosted Git Without the Existential Crisis
Next Post
WireGuard Is Fast, But You're Leaving Performance on the Table

Discussion

Powered by Garrul . Sign in with GitHub or Google, or post anonymously.

Related Posts