Zum Inhalt springen

Services, Wiki-Artikel und Blog-Beiträge durchsuchen

↑↓NavigierenEnterÖffnenESCSchließen
Kubernetes Runtime Security: Falco, Seccomp und OPA Gatekeeper im Einsatz - Container-Sicherheit und Orchestrierung
Cloud Security

Kubernetes Runtime Security: Falco, Seccomp und OPA Gatekeeper

Kubernetes Runtime Security: Falco, Seccomp, AppArmor, OPA Gatekeeper und PSA im Einsatz - mit Incident-Response-Playbooks für kompromittierte Pods.

Vincent Heinen Abteilungsleiter Offensive Services
11 Min. Lesezeit
OSCP+ OSCP OSWP OSWA

TL;DR

Runtime Security ist entscheidend, um Kubernetes-Angriffe zu erkennen, die statische Konfigurationskontrollen umgehen. Sie überwacht dynamisch Systemaufrufe, Dateizugriffe und Netzwerkaktivitäten innerhalb laufender Container. Falco, ein CNCF-Projekt, nutzt eBPF oder Kernel-Module, um Kernel-Systemaufrufe in Echtzeit zu analysieren und bei Regelverstößen wie dem Start einer Shell in einem Web-Container Alarm zu schlagen. Ergänzend dazu filtert Seccomp (Secure Computing Mode) auf Linux-Kernel-Ebene unerwünschte Systemaufrufe, indem es Pods mit spezifischen Profilen versieht, die nur erlaubte Syscalls zulassen. Ein Beispielprofil für eine Anwendung kann über 50 Systemaufrufe wie "read", "write" und "execve" explizit erlauben, während alle anderen blockiert werden.

Diese Zusammenfassung wurde KI-gestützt erstellt (EU AI Act Art. 50).

Inhaltsverzeichnis (6 Abschnitte)

Kubernetes-Sicherheit endet nicht bei der Konfiguration von RBAC und Network Policies. Runtime Security überwacht was während der Ausführung passiert: welche Syscalls ein Container aufruft, ob Dateisysteme unerwartet modifiziert werden, ob Prozesse neue Netzwerkverbindungen aufbauen. Diese zweite Verteidigungslinie erkennt Angriffe die statische Konfigurationskontrollen umgehen.

Runtime Security: Warum Konfiguration alleine nicht reicht

Das Lückenmodell:

Prävention (statisch):

  • RBAC: wer darf was deployen?
  • Network Policies: wer darf mit wem kommunizieren?
  • Pod Security: privileged=false, read-only rootfs
  • Image Scanning: bekannte CVEs in Images prüfen

Problem: Image-Scan findet keine ZERO-DAY-Schwachstellen! Kompromittierter Container sieht von außen "normal" aus.

Runtime Security (dynamisch):

  • Was passiert INNERHALB des Containers während er läuft?
  • Kernel-Systemcalls: welche Syscalls werden aufgerufen?
  • Dateizugriffe: unerwartete Schreibzugriffe auf /etc?
  • Netzwerk: neues unbekanntes Verbindungsziel?
  • Prozesse: unerwarteter Prozessstart im Container?

Angriffsvektor, den Runtime Security abdeckt: CVE in Anwendung → Angreifer führt Shell aus. Ohne Runtime: kein Alert (Shell-Start ist normaler Syscall). Mit Runtime (Falco): "Neuer Prozess in Web-Container: /bin/bash" → Alert!

Falco: Kernel-Level Runtime Security

Falco - CNCF-Projekt für Runtime Security:

Wie Falco arbeitet:

  • eBPF-basiertes oder Kernel-Modul-basiertes Monitoring
  • Liest Kernel-System-Calls in Echtzeit
  • Vergleicht mit Regeln → Alert wenn Regel zutrifft

Installation via Helm:

helm repo add falcosecurity https://falcosecurity.github.io/charts
helm install falco falcosecurity/falco \
  --namespace falco \
  --create-namespace \
  --set driver.kind=ebpf \
  --set falco.grpc.enabled=true \
  --set falco.grpc_output.enabled=true

Falco-Regeln Syntax:

- rule: Spawn Shell in Container
  desc: Erkennt wenn eine Shell in einem Container gestartet wird
  condition: >
    spawned_process and container and
    shell_procs and
    not container.image.repository in (allowed_shell_containers)
  output: >
    Shell gestartet in Container (user=%user.name cmd=%proc.cmdline
    container=%container.name image=%container.image.repository)
  priority: WARNING
  tags: [container, shell, attack]

Wichtige Falco-Standardregeln:

- rule: Write below etc in container
  condition: open_write and container and fd.directory in (/etc)
  priority: ERROR

- rule: Contact K8s API Server From Container
  condition: >
    k8s_api_server and container and
    not ka.user.name in (allowed_k8s_users)
  priority: WARNING

- rule: Netcat Remote Code Execution in Container
  condition: >
    spawned_process and container and
    proc.name = "nc" and
    (proc.args contains "-e" or proc.args contains "-c")
  priority: CRITICAL

Falco-Ausgabe konfigurieren:

# falco.yaml:
outputs:
  rate: 1
  max_burst: 1000

file_output:
  enabled: true
  filename: /var/log/falco/falco.log

http_output:
  enabled: true
  url: https://siem.intern/api/falco  # SIEM-Integration!

program_output:
  enabled: true
  program: "jq '{severity:.priority,message:.output}' | curl -X POST ..."

Falco Sidekick (Alert-Routing):

# Weiterleitung zu Slack, PagerDuty, Datadog, etc.:
helm install falco-sidekick falcosecurity/falcosidekick \
  --set config.slack.webhookurl=https://hooks.slack.com/... \
  --set config.pagerduty.routingKey=...

Seccomp Profile

Seccomp (Secure Computing Mode) - Syscall-Filterung:

Funktionsprinzip:

  • Linux-Kernel-Feature: welche Systemcalls darf ein Prozess ausführen?
  • Profile: Allowlist oder Blocklist von Syscalls
  • Nicht erlaubter Syscall → SIGKILL oder SCMP_ACT_ERRNO

Kubernetes Seccomp-Integration:

# Pod mit RuntimeDefault-Profil:
securityContext:
  seccompProfile:
    type: RuntimeDefault

# Pod mit eigenem Profil:
securityContext:
  seccompProfile:
    type: Localhost
    localhostProfile: profiles/myprofile.json

Eigenes Seccomp-Profil erstellen:

Schritt 1: Syscalls aufzeichnen (Learning Mode):

# Mit strace (außerhalb des Clusters zum Entwickeln):
strace -c ./meine-anwendung
# Ausgabe zeigt alle genutzten Syscalls

Schritt 2: Minimales Profil erstellen (/var/lib/kubelet/seccomp/profiles/myapp.json):

{
  "defaultAction": "SCMP_ACT_ERRNO",
  "syscalls": [
    {
      "names": [
        "read", "write", "open", "close", "stat", "fstat",
        "mmap", "mprotect", "munmap", "brk", "rt_sigaction",
        "rt_sigprocmask", "ioctl", "access", "pipe", "select",
        "sched_yield", "dup2", "pause", "nanosleep", "getpid",
        "socket", "connect", "sendto", "recvfrom", "bind",
        "listen", "accept", "getsockname", "getpeername",
        "sendmsg", "recvmsg", "shutdown", "setsockopt",
        "getsockopt", "exit_group", "futex", "set_tid_address",
        "set_robust_list", "prctl", "arch_prctl", "clone",
        "wait4", "execve", "getuid", "getgid", "getppid"
      ],
      "action": "SCMP_ACT_ALLOW"
    }
  ]
}

Schritt 3: Testen in Audit-Mode (Log statt Block):

# Erstmal: SCMP_ACT_LOG statt SCMP_ACT_ERRNO
# Logs in audit.log: welche Syscalls wurden blockiert?
ausearch -m SECCOMP | tail -20

OCI Hook für automatische Profil-Generierung (Security-Profile-Operator):

kubectl apply -f https://github.com/kubernetes-sigs/security-profiles-operator/.../deploy.yaml
# SPO zeichnet Syscalls auf und generiert minimale Profile automatisch!

OPA Gatekeeper und Kyverno

Policy-as-Code: Präventive Kontrollen im Admission Webhook:

OPA Gatekeeper:

# Installation:
kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper/main/deploy/gatekeeper.yaml

ConstraintTemplate definieren:

apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
  name: k8srequiredlabels
spec:
  crd:
    spec:
      names:
        kind: K8sRequiredLabels
      validation:
        openAPIV3Schema:
          type: object
          properties:
            labels:
              type: array
              items: {type: string}
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8srequiredlabels
        violation[{"msg": msg, "details": {"missing_labels": missing}}] {
          provided := {label | input.review.object.metadata.labels[label]}
          required := {label | label := input.parameters.labels[_]}
          missing := required - provided
          count(missing) > 0
          msg := sprintf("Missing labels: %v", [missing])
        }

Constraint auf Cluster anwenden:

apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata:
  name: require-app-label
spec:
  match:
    kinds:
      - apiGroups: ["apps"]
        kinds: ["Deployment"]
  parameters:
    labels: ["app", "version", "owner"]
# Alle Deployments müssen app, version, owner Labels haben!

Kyverno (Kubernetes-native, YAML-basiert):

# Installation:
helm install kyverno kyverno/kyverno -n kyverno --create-namespace
# Policy: Keine privilegierten Container:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: disallow-privileged-containers
spec:
  validationFailureAction: enforce  # oder: audit
  rules:
    - name: privileged-containers
      match:
        resources:
          kinds: [Pod]
      validate:
        message: "Privilegierte Container sind nicht erlaubt!"
        pattern:
          spec:
            containers:
              - (name): "?*"
                securityContext:
                  privileged: false  # Muss false sein!

# Policy: Root-UID verbieten:
    - name: require-non-root
      validate:
        pattern:
          spec:
            securityContext:
              runAsNonRoot: true

# Policy: Read-Only Filesystem erzwingen:
    - name: readonly-rootfs
      validate:
        pattern:
          spec:
            containers:
              - (name): "?*"
                securityContext:
                  readOnlyRootFilesystem: true

# Policy: Ressourcenlimits erzwingen:
    - name: require-resource-limits
      validate:
        message: "CPU und Memory Limits sind Pflicht!"
        pattern:
          spec:
            containers:
              - (name): "?*"
                resources:
                  limits:
                    cpu: "?*"
                    memory: "?*"

Pod Security Admission (PSA)

Pod Security Admission - Nativer K8s-Schutz:

Drei Profile:

ProfilBeschreibung
privilegedKeinerlei Einschränkungen (nur für System-Namespaces!)
baselineMinimale Einschränkungen (sinnvoller Standard)
restrictedStrenge Einschränkungen (Produktions-Best-Practice)

PSA aktivieren (per Namespace-Label):

# Namespace mit restricted-Profil:
kubectl label namespace production \
  pod-security.kubernetes.io/enforce=restricted \
  pod-security.kubernetes.io/audit=restricted \
  pod-security.kubernetes.io/warn=restricted

# Oder mit baseline:
kubectl label namespace staging \
  pod-security.kubernetes.io/enforce=baseline
  • enforce = blockiert nicht-konforme Pods
  • audit = loggt aber blockt nicht
  • warn = Warnung im kubectl-Output

Was "restricted" einschränkt:

  • privileged: false (Pflicht)
  • allowPrivilegeEscalation: false
  • capabilities: DROP ALL (Pflicht)
  • runAsNonRoot: true
  • seccompProfile: RuntimeDefault oder Localhost
  • volumes: nur erlaubte Volume-Typen

Migrationsweg:

  1. Zuerst: warn/audit Mode → sehen was nicht konform ist
  2. Anwendungen anpassen (runAsNonRoot etc.)
  3. Dann: enforce aktivieren

Container-Forensik und Incident Response

Incident Response für kompromittierte Container:

Sofortmaßnahmen:

Pod isolieren (Network Policy):

kubectl apply -f - <<EOF
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: isolate-compromised-pod
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: compromised-app
  policyTypes: [Ingress, Egress]
  # Keine ingress/egress Rules = Alles blockiert!
EOF

Forensic-Snapshot des Pods:

# Container-Prozesse:
kubectl exec -n production compromised-pod -- ps aux
# Netzwerkverbindungen:
kubectl exec -n production compromised-pod -- ss -tulpn
# Crontab prüfen:
kubectl exec -n production compromised-pod -- crontab -l

# Container-Filesystem-Snapshot:
kubectl cp production/compromised-pod:/tmp/malware.sh ./malware.sh

Falco-basierter Incident Response:

Alert: "Reverse Shell von Pod 'payment-api'" - Automated Response (via Falco Sidekick → Python-Script):

  1. Pod-Label setzen: quarantined=true
  2. Network Policy sofort anwenden (Isolation)
  3. Pod-Logs sichern (kubectl logs → S3)
  4. Container-Snapshot: kubectl exec -- tar czf - / → S3
  5. PagerDuty-Alert: Incident P1 öffnen
  6. SIEM: alle Events dieses Pods der letzten 24h korrelieren

Falco-Rule für Auto-Quarantine:

- rule: Detect Reverse Shell
  condition: >
    spawned_process and container and
    proc.name in (shell_procs) and
    fd.type = ipv4 and fd.rip.name = external
  output: "Reverse Shell (pod=%k8s.pod.name ns=%k8s.ns.name)"
  priority: CRITICAL
  tags: [shell, container, reverse_shell]

Falco Sidekick Action: Bei CRITICAL → kubectl label pod $POD_NAME quarantined=true → NetworkPolicy auf quarantined=true-Pods triggern.

Nächster Schritt

Unsere zertifizierten Sicherheitsexperten beraten Sie zu den Themen aus diesem Artikel, unverbindlich und kostenlos.

Kostenlos · 30 Minuten · Unverbindlich

Artikel teilen

Über den Autor

Vincent Heinen
Vincent Heinen

Abteilungsleiter Offensive Services

E-Mail

M.Sc. IT-Sicherheit mit über 5 Jahren Erfahrung in offensiver Sicherheitsanalyse. Leitet die Durchführung von Penetrationstests mit Spezialisierung auf Web-Applikationen, Netzwerk-Infrastruktur, Reverse Engineering und Hardware-Sicherheit. Verantwortlich für mehrere Responsible Disclosures.

OSCP+ OSCP OSWP OSWA
Zertifiziert ISO 27001ISO 9001AZAV

Rufen Sie uns an

Mo-Fr, 8:00-17:00 Uhr - persönlich und unverbindlich.

0209 8830 6764
Jetzt anrufen