Zum Inhalt springen

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

↑↓NavigierenEnterÖffnenESCSchließen
Container-Sicherheit: Docker und Kubernetes richtig absichern - Container-Sicherheit und Orchestrierung
Cloud Security

Container-Sicherheit: Docker und Kubernetes richtig absichern

Praxisguide zur Container-Sicherheit: Sichere Dockerfiles, nicht-root User, Capabilities reduzieren, Image-Scanning mit Trivy, Kubernetes RBAC und Pod Security Standards, Network Policies und Runtime Security mit Falco. Für DevOps-Teams die Security ernst nehmen.

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

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.

Cloud Security Assessment anfragen | Penetrationstest Cloud

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

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 9001AZAVBSI

Cookielose Analyse via Matomo (selbst gehostet, kein Tracking-Cookie). Datenschutzerklärung