TL;DR
Privilegierte Container gewähren über bekannte Schwachstellen wie CVE-2019-5736 direkten Host-Kernel-Zugriff - das größte Risiko in Container-Umgebungen. Absicherung beginnt im Dockerfile: Alpine- oder Distroless-Images reduzieren die Angriffsfläche von 800 MB auf unter 80 MB, Multi-Stage-Builds eliminieren Build-Tools aus dem Produktions-Image, Non-Root-User und `--cap-drop ALL` begrenzen Laufzeit-Rechte. Trivy scannt Images auf CVEs vor dem Deploy. In Kubernetes schränken RBAC, Pod Security Standards und Network Policies laterale Bewegung ein; Falco erkennt Laufzeit-Anomalien wie unerwartete Shell-Ausführung in laufenden Containern.
Diese Zusammenfassung wurde KI-gestützt erstellt (EU AI Act Art. 52).
Inhaltsverzeichnis (7 Abschnitte)
Container haben die Art wie Software entwickelt und betrieben wird revolutioniert. Mit dieser Verbreitung kamen neue Sicherheitsrisiken: Privilegierte Container die Host-Zugriff ermöglichen, unverschlüsselte Secrets in Umgebungsvariablen, Images mit Hunderten bekannter Schwachstellen. Dieser Guide zeigt wie Container-Sicherheit systematisch angegangen wird.
Warum Container-Sicherheit anders ist
Container-spezifische Sicherheitsrisiken:
Container-Escape:
→ Privilegierter Container = direkter Host-Zugriff
→ docker run --privileged = "Wähle dein Abenteuer im Host-Kernel"
→ Escape via: CVE-2019-5736 (runc), CVE-2022-0847 (Dirty Pipe)
Supply Chain:
→ Docker Hub: öffentliche Images können Malware enthalten
→ Typosquatting: "nginx" vs "n1ginx" (Millionen Pulls!)
→ Abhängigkeiten in npm/pip im Image mit bekannten CVEs
Secrets-Leakage:
→ ENV PASSWORT=geheim → in Image-Layer gespeichert!
→ docker history myimage → alle Layer sichtbar!
→ auch nach rm $PASSWORT im Dockerfile → Layer bleibt!
Fehlende Isolation:
→ Container teilen Host-Kernel (kein echtes VM-Level-Isolation!)
→ Kernel-Exploit → Container-Escape möglich
→ Lösung: gVisor, Kata Containers (stärkere Isolation)
Netzwerk-Flachheit:
→ Alle Container im selben Docker-Netzwerk: vollständige Erreichbarkeit
→ Kubernetes: ohne Network Policies kann jeder Pod jeden erreichen
Sichere Dockerfiles
Anti-Pattern vs. Best Practice:
UNSICHERES Dockerfile:
FROM ubuntu:latest # ← latest = unbekannte Version!
# ← Ubuntu = riesiges Image!
RUN apt-get install -y curl wget git
ENV DB_PASSWORD=geheim123 # ← Secret im Layer!
COPY . /app
RUN npm install
CMD ["node", "server.js"]
USER root # ← Root-User!
---
SICHERES Dockerfile:
# 1. Spezifische Version + kleine Base:
FROM node:20.11-alpine3.19
# 2. Non-Root User erstellen:
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
# 3. Arbeitsverzeichnis setzen:
WORKDIR /app
# 4. Nur nötige Dependencies kopieren (Layer-Caching):
COPY package*.json ./
RUN npm ci --only=production # Kein devDependencies!
# 5. Anwendungscode kopieren:
COPY --chown=appuser:appgroup src/ ./src/
# 6. Non-Root User aktivieren:
USER appuser
# 7. Health Check:
HEALTHCHECK --interval=30s --timeout=3s \
CMD node healthcheck.js || exit 1
# 8. Nur nötiger Port:
EXPOSE 8080
CMD ["node", "src/server.js"]
---
Multi-Stage Build (nur nötiges im finalen Image):
FROM node:20-alpine AS builder
WORKDIR /build
COPY package*.json ./
RUN npm ci # Inkl. devDependencies für Build
COPY . .
RUN npm run build && npm prune --production
FROM node:20-alpine AS production
# Nur Production-Build + Runtime kopieren, kein Build-Tool!
WORKDIR /app
RUN addgroup -S app && adduser -S app -G app
COPY --from=builder --chown=app:app /build/dist ./dist
COPY --from=builder --chown=app:app /build/node_modules ./node_modules
USER app
EXPOSE 8080
CMD ["node", "dist/server.js"]
# Finales Image: ~80MB statt 800MB!
# Keine Build-Tools, kein npm, kein git im Produktions-Image!
---
Distroless Images (maximale Reduktion):
FROM gcr.io/distroless/nodejs20-debian12:nonroot
WORKDIR /app
COPY --chown=65532:65532 dist/ ./dist/
COPY --chown=65532:65532 node_modules/ ./node_modules/
USER 65532
EXPOSE 8080
CMD ["dist/server.js"]
# Kein Shell! Kein Package Manager! Kein curl/wget!
# Angreifer kann nach Container-Zugriff kaum etwas tun!
Docker Sicherheitskonfiguration
docker run - Sichere Flags:
# Minimal:
docker run \
--read-only \ # Dateisystem schreibgeschützt
--tmpfs /tmp \ # Nur /tmp beschreibbar
--no-new-privileges \ # Keine Privilege-Escalation
--cap-drop ALL \ # ALLE Linux Capabilities entfernen
--cap-add NET_BIND_SERVICE \ # Nur explizit nötige addieren
--security-opt no-new-privileges:true \
--memory 256m \ # RAM begrenzen (DoS-Schutz)
--cpus 0.5 \ # CPU begrenzen
--user 1000:1000 \ # Non-Root User
myapp:v1.2.3
# Seccomp-Profil (System-Call-Filterung):
docker run \
--security-opt seccomp=/path/to/seccomp.json \
myapp:latest
# AppArmor (MAC-basierter Schutz):
docker run \
--security-opt apparmor=docker-default \
myapp:latest
---
docker-compose.yml sicher:
services:
app:
image: myapp:v1.2.3@sha256:abc123... # Digest-Pinning!
user: "1000:1000"
read_only: true
tmpfs:
- /tmp
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
environment:
- NODE_ENV=production
# KEIN DB_PASSWORD hier! → Secrets aus Docker Secrets/Vault!
secrets:
- db_password
networks:
- backend
deploy:
resources:
limits:
cpus: '0.5'
memory: 256M
secrets:
db_password:
external: true # Docker Swarm Secret oder Docker Secret
---
Docker Secrets (für Swarm):
echo "geheim123" | docker secret create db_password -
# Im Container: /run/secrets/db_password (Datei, nicht ENV!)
Image-Scanning in CI/CD
Trivy - Umfassendstes Open-Source Image-Scanner:
# Lokaler Scan:
trivy image myapp:latest
# Output:
# myapp:latest (alpine 3.19.1)
# ========================
# Total: 12 (HIGH: 3, CRITICAL: 0)
#
# Library Version Severity CVE
# openssl 3.1.3-r2 HIGH CVE-2024-0727
# ...
# Nur Critical und High anzeigen:
trivy image --severity HIGH,CRITICAL myapp:latest
# JSON-Output für CI/CD:
trivy image --format json --output trivy-results.json myapp:latest
# Im GitLab CI (.gitlab-ci.yml):
container_scanning:
stage: security
image: aquasec/trivy:latest
script:
- trivy image
--exit-code 1 # Pipeline schlägt bei Findings fehl!
--severity HIGH,CRITICAL
--no-progress
$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
allow_failure: false
artifacts:
reports:
container_scanning: trivy-results.json
---
Regelmäßiges Scanning laufender Images:
# Trivy als Cronjob für alle laufenden Container:
for image in $(docker ps --format "{{.Image}}"); do
trivy image --severity CRITICAL $image
done
# Kubernetes: Trivy Operator (automatisches Scanning aller Pods):
kubectl apply -f https://raw.githubusercontent.com/aquasecurity/trivy-operator/main/deploy/static/trivy-operator.yaml
# → Automatischer Scan aller Container im Cluster!
# → Ergebnisse als CRDs (VulnerabilityReport) verfügbar
Kubernetes Pod Security und RBAC
Pod Security Standards (PSS):
# Alle Namespaces auf "restricted" setzen:
kubectl label namespace --all \
pod-security.kubernetes.io/enforce=baseline \
pod-security.kubernetes.io/warn=restricted
# Bestimmter Namespace auf "restricted":
kubectl label namespace production \
pod-security.kubernetes.io/enforce=restricted
# Was "restricted" verhindert:
# ✗ privileged: true
# ✗ runAsUser: 0 (Root)
# ✗ allowPrivilegeEscalation: true
# ✗ hostPID: true, hostNetwork: true
# ✗ hostPath Volumes (Host-Filesystem!)
---
Sicheres Pod-Manifest:
apiVersion: v1
kind: Pod
metadata:
name: secure-app
spec:
automountServiceAccountToken: false # Kein automatisches SA-Token!
securityContext:
runAsNonRoot: true
runAsUser: 10001
runAsGroup: 10001
fsGroup: 10001
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: registry.intern.de/myapp:v1.2.3@sha256:abc...
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
readOnlyRootFilesystem: true
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "256Mi"
volumeMounts:
- name: tmp-volume
mountPath: /tmp
volumes:
- name: tmp-volume
emptyDir: {}
---
RBAC Least Privilege:
# Service Account ohne Cluster-Rechte:
apiVersion: v1
kind: ServiceAccount
metadata:
name: myapp-sa
namespace: production
automountServiceAccountToken: false # Explizit kein Token!
# Nur nötige Rechte:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: myapp-role
namespace: production
rules:
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get"]
resourceNames: ["myapp-config"] # Nur diese ConfigMap!
Runtime Security mit Falco
Falco - Kubernetes Runtime Security:
→ Erkennt verdächtige Aktivitäten in Echtzeit
→ Basiert auf Linux Kernel System Calls
→ CNCF-Projekt, Open Source
Beispiel Falco-Regeln (rules.yaml):
# Shell in Container → Verdächtig!
- rule: Terminal shell in container
desc: A shell was used as the entrypoint/exec point into a container
condition: >
spawned_process and container
and shell_procs
and proc.tty != 0
and container.image.repository != allowed_images
output: >
A shell was spawned in a container with an attached terminal
(user=%user.name container=%container.name image=%container.image)
priority: WARNING
# Sensitive Datei gelesen:
- rule: Read sensitive file in container
desc: >
Detect an attempt to read /etc/shadow, /etc/passwd from a container
condition: >
open_read and container
and fd.name in (sensitive_files)
and not proc.name in (allowed_processes)
output: >
Sensitive file opened for reading by non-privileged program
(user=%user.name file=%fd.name container=%container.name)
priority: ERROR
Alerts:
→ Falco sendet Alerts an: syslog, Slack, SIEM, PagerDuty
→ Integration: Falco → Fluentd → Elasticsearch → Kibana
→ Falcosidekick für Alert-Routing
---
Bekannte Container-Angriffsmuster die Falco erkennt:
□ Reverse Shell: nc -e /bin/sh ... im Container
□ Credential-Diebstahl: cat /etc/shadow, env | grep PASSWORD
□ Crypto-Mining: gpg, xmrig, cryptonight im Container
□ Lateral Movement: kubectl exec in anderen Pod
□ Data Exfiltration: curl/wget große Datenmengen nach außen
□ Container Escape: mount /proc/sysrq-trigger
Security-Checkliste Container
Docker:
□ Kein :latest-Tag in Produktion (Digest-Pinning!)
□ Non-Root User in Dockerfile (USER 1000)
□ Read-only Filesystem (--read-only)
□ Capabilities dropen (--cap-drop ALL)
□ Keine Secrets in ENV (Docker Secrets oder Vault!)
□ Multi-Stage Build → minimales Produktions-Image
□ Trivy-Scan in CI/CD Pipeline
□ Distroless oder Alpine (minimale Base-Images)
Kubernetes:
□ Pod Security Standards: restrict oder baseline
□ RBAC: Least Privilege für Service Accounts
□ automountServiceAccountToken: false
□ Network Policies: Default-Deny in allen Namespaces
□ Secrets: etcd-Verschlüsselung oder External Secrets
□ Image-Registry: nur interne Registry erlaubt (OPA)
□ Resource Limits für alle Container (CPU + RAM)
□ Falco oder ähnliches Runtime-Security-Tool
CI/CD:
□ Image-Scanning: Trivy/Grype in Pipeline
□ SBOM-Generierung: Syft
□ Image-Signierung: Cosign (Supply Chain)
□ Private Registry: Docker Hub ist nicht erlaubt
□ Critical CVEs → Pipeline-Fehler, kein Deploy
Container-Sicherheit ist komplex aber systematisch lösbar. AWARE7 führt Container- und Kubernetes-Security-Assessments durch - von der Dockerfile-Analyse bis zum vollständigen Cluster-Audit.
Nächster Schritt
Unsere zertifizierten Sicherheitsexperten beraten Sie zu den Themen aus diesem Artikel — unverbindlich und kostenlos.
Kostenlos · 30 Minuten · Unverbindlich
