Steven's Knowledge

Getting Started

Install Falco on kind, trigger a sample alert, explore the rule language, try Tetragon's K8s-aware detection

Getting Started

This page installs Falco on a local cluster, triggers detections, writes a custom rule, then compares with Tetragon for the modern eBPF-first approach.

Prerequisites

kind create cluster --name runtime-sec
kubectl cluster-info --context kind-runtime-sec

kind runs containers, which means we can install Falco that watches the host kernel.

Install Falco

helm repo add falcosecurity https://falcosecurity.github.io/charts
helm repo update

helm install falco falcosecurity/falco \
  --namespace falco --create-namespace \
  --set tty=true \
  --set driver.kind=ebpf

Falco runs as a DaemonSet — one pod per node. It uses eBPF probes by default (less invasive than the kernel module).

kubectl get pods -n falco
# Wait for Running

# Stream Falco's alerts (these appear in stdout)
kubectl logs -n falco -l app.kubernetes.io/name=falco -f

Trigger an Alert

Falco ships with a baseline ruleset. Trigger one of its rules:

# Run a pod
kubectl run nginx --image=nginx
kubectl wait --for=condition=ready pod/nginx --timeout=30s

# Exec into it — Falco rule "Terminal shell in container" fires
kubectl exec -it nginx -- bash -c "ls /etc/shadow"

In the Falco log:

22:34:12.123: Notice A shell was spawned in a container with an attached
              terminal (user=root user_loginuid=-1 container_id=abc123
              container_name=nginx image=nginx:latest shell=bash
              parent=runc cmdline=bash -c ls /etc/shadow)

22:34:12.140: Warning Sensitive file opened for reading by non-trusted
              program (file=/etc/shadow command=cat container_name=nginx
              image=nginx)

Two rules fired: shell-in-container and sensitive-file-read. The forensic signal is detailed: container, image, command, user, parent process.

Explore the Rule Language

Falco rules are YAML. Show the default rules:

kubectl exec -it -n falco $(kubectl get pod -n falco -o name | head -1) -- \
  cat /etc/falco/falco_rules.yaml | head -100

Each rule has:

- rule: Terminal shell in container
  desc: A shell was spawned in a container with an attached terminal
  condition: >
    spawned_process and container
    and shell_procs
    and proc.tty != 0
    and container_entrypoint
    and not user_expected_terminal_shell_in_container_conditions
  output: >
    A shell was spawned in a container with an attached terminal
    (user=%user.name user_loginuid=%user.loginuid %container.info
    shell=%proc.name parent=%proc.pname cmdline=%proc.cmdline
    terminal=%proc.tty container_id=%container.id image=%container.image.repository)
  priority: NOTICE
  tags: [container, shell, mitre_execution, T1059]

Fields:

  • condition: a filter expression on events. Predefined macros like shell_procs, container_entrypoint.
  • output: format string for the alert.
  • priority: severity (DEBUG, INFO, NOTICE, WARNING, ERROR, CRITICAL).
  • tags: searchable categories, including MITRE ATT&CK mappings.

Write a Custom Rule

# custom-rules.yaml
- macro: trusted_internal_ranges
  condition: (fd.cip in (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16))

- rule: Egress to External Network from Production
  desc: A production pod made a connection to a non-internal IP
  condition: >
    outbound and container
    and not trusted_internal_ranges
    and k8s.ns.name = "production"
    and not fd.sip in (allowed_external_destinations)
  output: >
    Production pod connected to external IP (pod=%k8s.pod.name
    namespace=%k8s.ns.name image=%container.image.repository
    destination_ip=%fd.sip destination_port=%fd.sport
    command=%proc.cmdline)
  priority: WARNING
  tags: [network, exfiltration, production]

Apply it via Helm or by mounting into the pod:

kubectl create configmap falco-custom-rules \
  --from-file=custom-rules.yaml \
  -n falco

helm upgrade falco falcosecurity/falco \
  -n falco \
  --set 'customRules.custom-rules\.yaml'="$(cat custom-rules.yaml)"

Now any production pod connecting outside your internal ranges (and not in allowed_external_destinations) triggers an alert.

Route Alerts Somewhere Useful

Falco logs to stdout by default. For production:

# Falco's falcosidekick — fans out to many backends
helm upgrade falco falcosecurity/falco -n falco \
  --set falcosidekick.enabled=true \
  --set falcosidekick.config.slack.webhookurl="https://hooks.slack.com/..." \
  --set falcosidekick.config.elasticsearch.hostport="http://es:9200" \
  --set falcosidekick.config.kafka.hostport="kafka:9092"

falcosidekick supports dozens of backends: Slack, PagerDuty, OpenSearch, Splunk, Kafka, AWS Security Hub, GCP Pub/Sub, Loki, etc. Route by severity: NOTICE to Slack, WARNING to PagerDuty, CRITICAL to incident response.

Try Tetragon (Different Approach)

Tetragon is Cilium-family, more K8s-aware, supports prevention (not just detection):

helm repo add cilium https://helm.cilium.io
helm install tetragon cilium/tetragon -n kube-system

Watch events:

kubectl exec -it -n kube-system ds/tetragon -c tetragon -- \
  tetra getevents -o compact

Now apply a Tetragon policy:

# tetragon-block-curl.yaml
apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
  name: "block-curl"
spec:
  kprobes:
    - call: "sys_execve"
      syscall: true
      args:
        - index: 0
          type: "string"
      selectors:
        - matchArgs:
            - index: 0
              operator: "Equal"
              values: ["/usr/bin/curl"]
          matchActions:
            - action: Sigkill
kubectl apply -f tetragon-block-curl.yaml

# Try to curl
kubectl exec -it nginx -- curl http://example.com
# Process killed

Tetragon kills the process synchronously — detection and prevention in one. Powerful but careful: a wrong rule kills legitimate processes too.

For most teams: Falco for detection + Tetragon for selected high-risk events you want to actively block.

Compare

FalcoTetragon
ApproachUserspace rule engine reading eBPF eventsKernel-side filtering with selective userspace
Default modeDetectDetect; can prevent
Rule languageYAML with macrosTracingPolicy CRDs (Kubernetes-native)
K8s integrationGood (labels, namespaces)Excellent (pod identity from Cilium)
MaturityHighest, large communityNewer but stable
Best forAudit, SIEM feed, broad detectionTargeted prevention; Cilium users

Both fine; Falco is the safer first install. Tetragon shines if you want kernel-side blocking.

Cleanup

kind delete cluster --name runtime-sec

What's Next

  • Patterns — rule tuning, SIEM integration, MITRE mapping, blocking, multi-cluster
  • Best Practices — alert fatigue, response runbooks, compliance, performance

On this page