Je scande je images met Trivy. Je dwingt policies af met Kyverno. Je workloads hebben cryptografische identiteit via SPIFFE.
Maar wat gebeurt er na deployment? Wat als een container wordt gecompromitteerd tijdens runtime? Wat als een aanvaller een zero-day exploiteert?
Preventie is niet genoeg. Je hebt detectie nodig.
Falco is een runtime security tool die system calls monitort in je cluster. Het ziet alles wat containers doen — bestandstoegang, netwerkverbindingen, procesuitvoering — en alert wanneer iets er verkeerd uitziet.
Waarom Runtime Security?
Security heeft lagen:
- Build time — Scan images voor bekende kwetsbaarheden (Trivy)
- Deploy time — Dwing policies af (Kyverno, admission controllers)
- Runtime — Detecteer afwijkend gedrag (Falco)
De meeste teams focussen op lagen 1 en 2. Maar aanvallers geven niet om je pipeline — ze exploiteren wat draait.
Runtime voorbeelden die andere tools missen:
- Container voert
/bin/bashuit (zou niet moeten in productie) - Proces leest
/etc/shadow - Onverwachte uitgaande verbinding naar crypto mining pool
- Container schrijft naar
/etc/directories - Kubernetes secrets benaderd vanuit ongewone pods
Falco vangt deze omdat het daadwerkelijk gedrag monitort, niet alleen configuratie.
Hoe Falco Werkt
Falco gebruikt eBPF (of een kernel module) om system calls te intercepten:
flowchart TD
subgraph userspace["User Space"]
A["Container A"]
B["Container B"]
C["Container C"]
end
subgraph kernel["Kernel Space"]
EBPF["eBPF / Kernel Module<br/>(intercepts syscalls)"]
end
A --> EBPF
B --> EBPF
C --> EBPF
EBPF --> FALCO["Falco<br/>(rules engine)"]
FALCO --> ALERTS["Alerts<br/>(stdout, webhook, Kafka...)"]
Elke syscall wordt geëvalueerd tegen rules. Matchende syscalls genereren alerts.
Falco Installeren
Met Helm en eBPF driver (aanbevolen):
helm repo add falcosecurity https://falcosecurity.github.io/charts
helm repo update
helm install falco falcosecurity/falco \
--namespace falco \
--create-namespace \
--set driver.kind=ebpf \
--set falcosidekick.enabled=true
Voor GitOps met ArgoCD:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: falco
namespace: argocd
spec:
project: default
source:
repoURL: https://falcosecurity.github.io/charts
chart: falco
targetRevision: 4.0.0
helm:
values: |
driver:
kind: ebpf
falcosidekick:
enabled: true
config:
slack:
webhookurl: "https://hooks.slack.com/services/xxx"
tty: true
destination:
server: https://kubernetes.default.svc
namespace: falco
syncPolicy:
automated:
prune: true
selfHeal: true
Falco Rules Begrijpen
Rules definiëren welk gedrag te detecteren. De syntax:
- rule: Terminal shell in container
desc: Detect shell being spawned in a container
condition: >
spawned_process and
container and
shell_procs and
proc.tty != 0
output: >
Shell spawned in container
(user=%user.name container=%container.name shell=%proc.name
parent=%proc.pname cmdline=%proc.cmdline)
priority: WARNING
tags: [container, shell, mitre_execution]
Belangrijke delen:
- condition — Wanneer te triggeren (met Falco’s filter taal)
- output — Wat op te nemen in de alert
- priority — Severity level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
- tags — Voor categorisatie en filtering
Ingebouwde Rules
Falco komt met uitgebreide default rules:
# Bekijk geladen rules
kubectl exec -n falco -it falco-xxx -- falco --list
Categorieën omvatten:
- Container escape pogingen
- Privilege escalation
- Gevoelige bestandstoegang
- Verdachte netwerkactiviteit
- Kubernetes API misbruik
Voorbeeld ingebouwde rules:
# Detecteer container escape via mount
- rule: Launch Sensitive Mount Container
condition: >
spawned_process and container and
sensitive_mount
# Detecteer lezen van gevoelige bestanden
- rule: Read sensitive file untrusted
condition: >
open_read and sensitive_files and
not trusted_containers
# Detecteer crypto mining
- rule: Detect outbound connections to crypto mining pools
condition: >
outbound and
fd.sip.name in (cryptomining_pool_domains)
Custom Rules
Voeg eigen rules toe voor applicatie-specifieke detectie:
# custom-rules.yaml
customRules:
rules-custom.yaml: |-
# Alert wanneer onze API server onverwachte processen spawnt
- rule: Unexpected process in api-server
desc: Detect processes other than the main app in api-server containers
condition: >
spawned_process and
container.image.repository contains "api-server" and
not proc.name in (api-server, node, npm)
output: >
Unexpected process in api-server
(command=%proc.cmdline container=%container.name image=%container.image.repository)
priority: WARNING
# Alert op database verbinding van onverwachte pods
- rule: Database connection from non-backend pod
desc: Detect connections to PostgreSQL from pods that shouldn't connect
condition: >
outbound and
fd.sport = 5432 and
not k8s.pod.label.app in (api-server, worker, migration-job)
output: >
Unexpected database connection
(pod=%k8s.pod.name namespace=%k8s.ns.name dest=%fd.sip)
priority: ERROR
Deploy custom rules via Helm values:
# values.yaml
customRules:
rules-custom.yaml: |-
- rule: My custom rule
...
Falco’s Filter Taal
De filter taal begrijpen is essentieel voor effectieve rules.
Classes en Fields
# Process fields
proc.name # Proces naam
proc.cmdline # Volledige command line
proc.pname # Parent proces naam
proc.exepath # Executable pad
# Container fields
container.name # Container naam
container.id # Container ID
container.image.repository # Image naam
# File fields
fd.name # File descriptor naam (bestandspad)
fd.directory # Directory van het bestand
# Network fields
fd.sip # Server IP
fd.sport # Server poort
fd.cip # Client IP
# Kubernetes fields
k8s.pod.name # Pod naam
k8s.ns.name # Namespace
k8s.pod.label.app # Pod label
Macros
Herbruikbare conditie fragmenten:
- macro: container
condition: container.id != host
- macro: shell_procs
condition: proc.name in (bash, sh, zsh, dash, ksh)
- macro: spawned_process
condition: evt.type = execve and evt.dir = <
- macro: open_read
condition: evt.type in (open, openat) and evt.is_open_read = true
Lijsten
Benoemde sets van waarden:
- list: sensitive_files
items: [/etc/shadow, /etc/sudoers, /etc/pam.d, /root/.ssh]
- list: package_managers
items: [apt, apt-get, yum, dnf, apk, pip, npm]
- list: shell_binaries
items: [bash, sh, zsh, csh, tcsh, ksh, dash]
Praktijkvoorbeelden van Detectie
Detecteer Reverse Shells
- rule: Reverse shell detected
desc: Detect reverse shells via common patterns
condition: >
spawned_process and
container and
((proc.name = bash and proc.args contains ">&" and proc.args contains "/dev/tcp") or
(proc.name = nc and proc.args contains "-e") or
(proc.name = python and proc.args contains "socket" and proc.args contains "subprocess"))
output: >
Reverse shell detected (user=%user.name command=%proc.cmdline container=%container.name)
priority: CRITICAL
tags: [mitre_execution, reverse_shell]
Detecteer Kubernetes Secret Toegang
- rule: K8s secret accessed from unexpected namespace
desc: Detect when secrets are read from non-standard locations
condition: >
open_read and
fd.name startswith "/var/run/secrets/kubernetes.io" and
not k8s.ns.name in (kube-system, monitoring, vault)
output: >
K8s secret accessed (file=%fd.name pod=%k8s.pod.name namespace=%k8s.ns.name)
priority: WARNING
Detecteer Package Installatie op Runtime
- rule: Package manager in container
desc: Detect package installations in running containers
condition: >
spawned_process and
container and
proc.name in (apt, apt-get, yum, dnf, apk, pip, npm) and
not container.image.repository in (allowed_builder_images)
output: >
Package manager run in container
(command=%proc.cmdline container=%container.name image=%container.image.repository)
priority: ERROR
tags: [mitre_persistence, package_install]
Falco Sidekick: Alert Routing
Falco output alerts naar stdout standaard. Falcosidekick routeert alerts naar diverse bestemmingen:
falcosidekick:
enabled: true
config:
slack:
webhookurl: "https://hooks.slack.com/services/xxx"
minimumpriority: warning
prometheus:
enabled: true
elasticsearch:
hostport: "elasticsearch.logging:9200"
index: "falco"
alertmanager:
hostport: "http://alertmanager.monitoring:9093"
webhook:
address: "http://security-responder.security:8080/falco"
Dit stuurt:
- Warnings en hoger naar Slack
- Alle alerts als Prometheus metrics
- Alle alerts naar Elasticsearch voor doorzoeken
- Critical alerts naar Alertmanager voor paging
- Alles naar een custom webhook voor geautomatiseerde response
Integratie met Prometheus
Falcosidekick stelt Prometheus metrics beschikbaar:
# ServiceMonitor
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: falco-sidekick
namespace: monitoring
spec:
selector:
matchLabels:
app.kubernetes.io/name: falcosidekick
endpoints:
- port: http
Maak alerts gebaseerd op Falco detecties:
# PrometheusRule
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: falco-alerts
spec:
groups:
- name: falco
rules:
- alert: FalcoCriticalAlert
expr: increase(falco_events{priority="Critical"}[5m]) > 0
for: 0m
labels:
severity: critical
annotations:
summary: "Falco critical security event detected"
description: "{{ $labels.rule }} triggered in {{ $labels.k8s_ns_name }}/{{ $labels.k8s_pod_name }}"
Ruis Verminderen
Default rules kunnen luidruchtig zijn. Stem ze af:
Rules Uitschakelen
# values.yaml
falco:
rules_file:
- /etc/falco/falco_rules.yaml
- /etc/falco/falco_rules.local.yaml # Overrides
- /etc/falco/rules.d # Custom rules
customRules:
falco_rules.local.yaml: |-
# Schakel luidruchtige rule uit
- rule: Terminal shell in container
enabled: false
# Wijzig bestaande rule om bepaalde containers uit te sluiten
- rule: Read sensitive file untrusted
condition: >
open_read and sensitive_files and
not trusted_containers and
not container.image.repository in (my-trusted-app)
Macros Afstemmen
customRules:
falco_rules.local.yaml: |-
# Voeg toe aan trusted containers
- macro: trusted_containers
condition: >
(container.image.repository in (
my-company/trusted-app,
my-company/another-app
))
# Sluit namespaces uit van monitoring
- macro: user_namespace
condition: >
k8s.ns.name not in (kube-system, falco, monitoring)
Mijn Productie Configuratie
driver:
kind: ebpf
ebpf:
hostNetwork: true
falco:
grpc:
enabled: true
grpc_output:
enabled: true
json_output: true
log_level: info
rules_file:
- /etc/falco/falco_rules.yaml
- /etc/falco/falco_rules.local.yaml
- /etc/falco/rules.d
falcosidekick:
enabled: true
replicaCount: 2
config:
slack:
webhookurl: "${SLACK_WEBHOOK}"
minimumpriority: error
prometheus:
enabled: true
alertmanager:
hostport: "http://alertmanager.monitoring:9093"
minimumpriority: critical
customRules:
rules-custom.yaml: |-
# Onze applicatie-specifieke rules
- rule: Unexpected outbound connection from backend
desc: Backend pods should only connect to known services
condition: >
outbound and
k8s.pod.label.app = "backend" and
not fd.sip.name in (postgres.db, redis.cache, api.internal)
output: >
Backend made unexpected outbound connection
(pod=%k8s.pod.name dest=%fd.sip:%fd.sport)
priority: WARNING
falco_rules.local.yaml: |-
# Stem default rules af voor onze omgeving
- macro: user_known_write_etc_conditions
condition: >
(container.image.repository = "my-company/config-manager")
Belangrijke beslissingen:
- eBPF driver — Betere performance dan kernel module
- JSON output — Makkelijker parsen voor downstream tools
- Getrapte alerting — Errors naar Slack, Critical naar PagerDuty
- Custom rules — Applicatie-bewuste detectie
Response Automatisering
Falco detecteert; jij bepaalt wat te doen. Voorbeeld geautomatiseerde responses:
# security-responder deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: security-responder
spec:
template:
spec:
containers:
- name: responder
image: my-company/security-responder
env:
- name: SLACK_WEBHOOK
valueFrom:
secretKeyRef:
name: security-responder
key: slack-webhook
Response script voorbeeld:
@app.route('/falco', methods=['POST'])
def handle_falco_alert():
alert = request.json
if alert['priority'] == 'Critical':
# Kill de pod direct
pod = alert['output_fields']['k8s.pod.name']
namespace = alert['output_fields']['k8s.ns.name']
# Delete pod (Deployment zal hem herstellen)
v1.delete_namespaced_pod(pod, namespace)
# Notificeer
send_slack(f"Pod {namespace}/{pod} gekilld vanwege: {alert['rule']}")
return 'ok'
Waarom Dit Ertoe Doet
Je kunt niet beveiligen wat je niet kunt zien.
Statische analyse vangt bekende kwetsbaarheden. Admission control dwingt policies af. Maar runtime security ziet daadwerkelijk gedrag — wat er echt gebeurt in je cluster, nu.
Wanneer iets onverwachts gebeurt:
- Zonder Falco: Je weet het niet totdat de schade is aangericht
- Met Falco: Je ziet het terwijl het gebeurt
Dit is begrip toegepast op security. Niet hopen dat je preventie werkt, maar weten wat er daadwerkelijk gebeurt en direct reageren.
Preventie is belangrijk, maar detectie is essentieel. Falco geeft je ogen in je containers — zien wat ze doen, niet alleen wat ze zouden moeten doen.
