Falco, the cloud-native runtime security tool, is designed to detect anomalous activity in your Kubernetes cluster by analyzing syscalls and container activity. Deploying it as a DaemonSet ensures that Falco agents are running on every node, providing comprehensive visibility.

Here’s how you can deploy Falco as a DaemonSet on your Kubernetes cluster:

1. Create a Namespace for Falco

It’s good practice to isolate Falco’s resources in its own namespace.

kubectl create namespace falco

2. Create a ConfigMap for Falco Configuration

Falco’s behavior is controlled by a configuration file. We’ll create a ConfigMap to hold this file.

apiVersion: v1
kind: ConfigMap
metadata:
  name: falco-config
  namespace: falco
data:
  falco.yaml: |
    # Global settings
    log_level: info
    keep_alive:
      enabled: true
      interval: 10s

    # Load rules from the rules directory
    rules_files:
      - /etc/falco/rules.d/*.yaml

    # Load additional rules from a file
    # falco_rules_file: /etc/falco/falco_rules.yaml

    # Output configuration
    outputs:
      - output: json_output
        file: /tmp/falco_output.json
      - output: alert_output
        file: /tmp/falco_alerts.log

    # JSON output format
    json_output_layout:
      - key: timestamp
        value: "%(ts)"
      - key: priority
        value: "%(p)"
      - key: rule
        value: "%(r)"
      - key: output
        value: "%(m)"
      - key: pid
        value: "%(pid)"
      - key: comm
        value: "%(comm)"
      - key: tty
        value: "%(tty)"
      - key: proc_name
        value: "%(proc.name)"
      - key: proc_exe
        value: "%(proc.exe)"
      - key: proc_cmdline
        value: "%(proc.cmdline)"
      - key: proc_ppid
        value: "%(proc.ppid)"
      - key: proc_loginuid
        value: "%(proc.loginuid)"
      - key: proc_cwd
        value: "%(proc.cwd)"
      - key: container_id
        value: "%(container.id)"
      - key: container_name
        value: "%(container.name)"
      - key: container_image
        value: "%(container.image)"
      - key: k8s_evt_user
        value: "%(k8s.evt.user)"
      - key: k8s_evt_source
        value: "%(k8s.evt.source)"
      - key: k8s_evt_object
        value: "%(k8s.evt.object)"
      - key: k8s_evt_namespace
        value: "%(k8s.evt.namespace)"
      - key: k8s_evt_name
        value: "%(k8s.evt.name)"
      - key: k8s_evt_uid
        value: "%(k8s.evt.uid)"
      - key: k8s_evt_request
        value: "%(k8s.evt.request)"

    # Alert output format
    alert_output_layout:
      - key: timestamp
        value: "%(ts)"
      - key: priority
        value: "%(p)"
      - key: rule
        value: "%(r)"
      - key: output
        value: "%(m)"
      - key: proc_name
        value: "%(proc.name)"
      - key: proc_exe
        value: "%(proc.exe)"
      - key: container_name
        value: "%(container.name)"
      - key: k8s_evt_user
        value: "%(k8s.evt.user)"
      - key: k8s_evt_namespace
        value: "%(k8s.evt.namespace)"
      - key: k8s_evt_name
        value: "%(k8s.evt.name)"

    # Rule configuration
    # For a full list of rule options, see https://falco.org/docs/rules/
    rules:
      - rule: Unexpected outbound network connection
        desc: An unexpected outbound network connection was detected.
        condition: outbound and net
        output: Unexpected outbound connection from %container.name (%container.image) to %fd.sip:%fd.sport
        priority: warning
        tags: [network, outbound]
      - rule: Write to sensitive directories
        desc: A process tried to write to a sensitive directory.
        condition: syscall="open" and fd.flags contains "O_WRONLY" and fd.name startswith "/etc/kubernetes/" or fd.name startswith "/run/containerd/" or fd.name startswith "/var/lib/kubelet/"
        output: Write to sensitive directory %fd.name by %container.name (%container.image)
        priority: error
        tags: [filesystem, security]
      - rule: Suspicious shell in container
        desc: A shell process was detected running in a container.
        condition: container and proc.name = "sh" or proc.name = "bash" or proc.name = "zsh"
        output: Suspicious shell '%proc.name' executed in container '%container.name' (%container.image)
        priority: notice
        tags: [container, security]

Apply this ConfigMap:

kubectl apply -f falco-configmap.yaml

3. Create a ServiceAccount for Falco

Falco needs a ServiceAccount to interact with the Kubernetes API.

apiVersion: v1
kind: ServiceAccount
metadata:
  name: falco
  namespace: falco

Apply this ServiceAccount:

kubectl apply -f falco-sa.yaml

4. Create a ClusterRole and ClusterRoleBinding for Falco

Falco needs read-only access to Kubernetes audit events.

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: falco-clusterrole
rules:
- apiGroups: ["audit.k8s.io"]
  resources: ["audits"]
  verbs: ["get", "list", "watch"]
- apiGroups: [""]
  resources: ["namespaces"]
  verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: falco-clusterrolebinding
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: falco-clusterrole
subjects:
- kind: ServiceAccount
  name: falco
  namespace: falco

Apply these RBAC resources:

kubectl apply -f falco-rbac.yaml

5. Create the Falco DaemonSet

This is the core of the deployment. The DaemonSet ensures Falco runs on every node.

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: falco
  namespace: falco
  labels:
    app: falco
spec:
  selector:
    matchLabels:
      app: falco
  template:
    metadata:
      labels:
        app: falco
    spec:
      serviceAccountName: falco
      hostNetwork: true # Falco needs host network access to capture syscalls
      containers:
      - name: falco
        image: falcosecurity/falco:latest # Use the latest Falco image
        ports:
        - containerPort: 8765 # Default port for Falco's gRPC API
          name: grpc
        volumeMounts:
        - name: falco-config-volume
          mountPath: /etc/falco
        - name: sys-kernel-debug
          mountPath: /sys/kernel/debug
        - name: var-run-containerd
          mountPath: /run/containerd
        - name: var-lib-docker
          mountPath: /var/lib/docker # Only if using Docker
        - name: var-log-pods
          mountPath: /var/log/pods
        - name: var-lib-kubelet
          mountPath: /var/lib/kubelet
        - name: dev
          mountPath: /dev
        securityContext:
          privileged: true # Falco needs elevated privileges to capture syscalls
      volumes:
      - name: falco-config-volume
        configMap:
          name: falco-config
      - name: sys-kernel-debug
        hostPath:
          path: /sys/kernel/debug
      - name: var-run-containerd
        hostPath:
          path: /run/containerd
      - name: var-lib-docker
        hostPath:
          path: /var/lib/docker # Only if using Docker
      - name: var-log-pods
        hostPath:
          path: /var/log/pods
      - name: var-lib-kubelet
        hostPath:
          path: /var/lib/kubelet
      - name: dev
        hostPath:
          path: /dev

Apply the DaemonSet:

kubectl apply -f falco-daemonset.yaml

Verifying the Deployment

Check if the Falco pods are running on all your nodes:

kubectl get pods -n falco -o wide

You should see one Falco pod for each node in your cluster.

Accessing Falco Alerts

Falco logs alerts to /tmp/falco_alerts.log inside the container. To view them, you can use kubectl logs:

# Find the name of one of your falco pods
POD_NAME=$(kubectl get pods -n falco -l app=falco -o jsonpath='{.items[0].metadata.name}')

# View the logs
kubectl logs $POD_NAME -n falco

For more advanced integration, you can configure Falco to send alerts to other systems like Elasticsearch, Splunk, or a SIEM via its output plugins. You can also expose Falco’s gRPC API to query rules and events programmatically.

The next step is to tune Falco’s rules to reduce false positives and detect the specific threats relevant to your environment.

Want structured learning?

Take the full Falco course →