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. 52).
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!
Problem: 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 der Runtime Security braucht:
→ 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 erlaubte 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
# Oder: Falco + falco-driver-loader für Profil-Erstellung
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 (SPO):
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 (Constraint wird auf Cluster angewendet):
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:
privileged: Keinerlei Einschränkungen (nur für System-Namespaces!)
baseline: Minimale Einschränkungen (sinnvoller Standard)
restricted: Strenge 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 unconforme 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
# Trigger NetworkPolicy auf quarantined=true-Pods Nächster Schritt
Unsere zertifizierten Sicherheitsexperten beraten Sie zu den Themen aus diesem Artikel — unverbindlich und kostenlos.
Kostenlos · 30 Minuten · Unverbindlich
