Docker

0 60

Die wichtigsten Sicherheitsrisiken

  1. Root-User in Containern
  2. Veraltete Base Images
  3. Secrets in Images gespeichert
  4. Unnötige Berechtigungen
  5. Ungesicherte Docker-Socket

Best Practice 1: Nie als Root ausführen

Problem

dockerfile

FROM node:18
WORKDIR /app
COPY . .
CMD ["node", "server.js"]
<em># Container läuft als root! ❌</em>

Lösung

dockerfile

FROM node:18-alpine

<em># User erstellen</em>
RUN addgroup -g 1001 -S nodejs && \
    adduser -S nodejs -u 1001

WORKDIR /app

<em># Files mit richtigem Owner kopieren</em>
COPY --chown=nodejs:nodejs package*.json ./
RUN npm ci --only=production

COPY --chown=nodejs:nodejs . .

<em># Als non-root user ausführen</em>
USER nodejs

EXPOSE 3000
CMD ["node", "server.js"]

Warum wichtig?

  • Root-User kann Host-System beeinflussen
  • Bei Exploit hat Angreifer volle Rechte
  • Prinzip der minimalen Berechtigungen

Best Practice 2: Minimale Base Images

Problem

dockerfile

FROM ubuntu:22.04  # 77 MB, viele Pakete

Lösung

dockerfile

FROM alpine:3.18   # 7 MB, minimal
<em># Oder noch besser:</em>
FROM scratch      # 0 MB, nur Binary

Alpine vs. Ubuntu:

Ubuntu:  77 MB + zahlreiche Pakete = größere Angriffsfläche
Alpine:  7 MB + minimale Pakete = kleinere Angriffsfläche

Vergleich

dockerfile

<em># ❌ Groß</em>
FROM php:8.2-apache  # 500+ MB

<em># ✅ Klein</em>
FROM php:8.2-fpm-alpine  # ~100 MB

Best Practice 3: Image-Versionen fixieren

Problem

dockerfile

FROM node:latest  # ❌ Nicht reproduzierbar!

Risiken:

  • Builds nicht reproduzierbar
  • Breaking Changes ohne Warnung
  • Sicherheitslücken in neuen Versionen

Lösung

dockerfile

FROM node:18.17.1-alpine3.18  # ✅ Exakte Version

Versionierungs-Schema:

node:18.17.1-alpine3.18
     |   |  |    |    |
     |   |  |    |    └─ Alpine-Version
     |   |  |    └────── OS-Variante
     |   |  └─────────── Patch-Version
     |   └────────────── Minor-Version
     └────────────────── Major-Version

Best Practice 4: Keine Secrets im Image

Problem

dockerfile

FROM node:18
COPY .env /app/.env  # ❌ Secrets im Image!
RUN npm install
CMD ["node", "server.js"]

Gefahr: Jeder mit Zugriff auf das Image kann Secrets auslesen!

Lösung 1: Environment Variables

yaml

<em># docker-compose.yml</em>
services:
  app:
    image: myapp
    environment:
      DATABASE_PASSWORD: ${DB_PASSWORD}  <em># Aus .env laden</em>
      API_KEY: ${API_KEY}

Lösung 2: Docker Secrets (Swarm)

yaml

services:
  app:
    secrets:
      - db_password
      - api_key

secrets:
  db_password:
    external: true
  api_key:
    external: true

bash

<em># Secrets erstellen</em>
echo "my_secret_password" | docker secret create db_password -

Lösung 3: Secrets Management (Production)

  • HashiCorp Vault
  • AWS Secrets Manager
  • Azure Key Vault
  • Google Secret Manager

Best Practice 5: Multi-Stage Builds

Entfernen Sie Build-Tools aus dem finalen Image:

dockerfile

<em># ❌ Build-Tools bleiben im Image</em>
FROM node:18
WORKDIR /app
COPY . .
RUN npm install  # Auch devDependencies!
CMD ["node", "dist/server.js"]

<em># ✅ Multi-Stage Build</em>
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

<em># Production Stage</em>
FROM node:18-alpine
RUN addgroup -g 1001 nodejs && adduser -S nodejs -u 1001
WORKDIR /app
COPY --from=builder --chown=nodejs:nodejs /app/dist ./dist
COPY --from=builder --chown=nodejs:nodejs /app/node_modules ./node_modules
USER nodejs
CMD ["node", "dist/server.js"]

Vorteile:

  • Kleineres finales Image
  • Keine Build-Tools im Production-Image
  • Weniger Angriffsfläche

Best Practice 6: Vulnerability Scanning

Docker Scout (offiziell)

bash

<em># Image scannen</em>
docker scout cves myapp:latest

<em># Detaillierter Report</em>
docker scout quickview myapp:latest

<em># Empfehlungen</em>
docker scout recommendations myapp:latest

Trivy (Open Source)

bash

<em># Installation</em>
docker pull aquasec/trivy

<em># Image scannen</em>
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
  aquasec/trivy image myapp:latest

<em># Nur HIGH und CRITICAL</em>
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
  aquasec/trivy image --severity HIGH,CRITICAL myapp:latest

Snyk

bash

<em># Installation</em>
npm install -g snyk

<em># Login</em>
snyk auth

<em># Docker Image scannen</em>
snyk container test myapp:latest

Best Practice 7: Read-Only Root Filesystem

yaml

services:
  app:
    image: myapp
    read_only: true
    tmpfs:
      - /tmp
      - /run

Warum?

  • Verhindert Änderungen am Dateisystem
  • Malware kann sich nicht installieren
  • Angreifer kann keine Backdoors einrichten

Best Practice 8: Ressourcen-Limits

yaml

services:
  app:
    image: myapp
    deploy:
      resources:
        limits:
          cpus: '1.0'
          memory: 512M
        reservations:
          cpus: '0.5'
          memory: 256M

Schutz vor:

  • DoS-Attacken
  • Memory Leaks
  • CPU-intensiven Angriffen

Best Practice 9: Network Isolation

yaml

services:
  frontend:
    networks:
      - frontend-net

  backend:
    networks:
      - frontend-net
      - backend-net

  database:
    networks:
      - backend-net  <em># Nur Backend hat Zugriff!</em>

networks:
  frontend-net:
  backend-net:

Best Practice 10: Capabilities einschränken

yaml

services:
  app:
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE  <em># Nur Port 80/443 binden</em>

Standard Capabilities entfernen:

yaml

cap_drop:
  - SETUID
  - SETGID
  - NET_RAW
  - SYS_CHROOT
  - MKNOD
  - AUDIT_WRITE
  - SETFCAP

Best Practice 11: Healthchecks

dockerfile

HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
  CMD curl -f http://localhost:3000/health || exit 1

Compose:

yaml

services:
  app:
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

Best Practice 12: Logging Security Events

yaml

services:
  app:
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"
        labels: "security,audit"

Wichtige Security Events loggen:

  • Login-Versuche
  • API-Zugriffe
  • Datenbankzugriffe
  • Fehler und Exceptions

Best Practice 13: Regelmäßige Updates

bash

#!/bin/bash
<em># update-images.sh</em>

<em># Base Images aktualisieren</em>
docker compose pull

<em># Neu bauen</em>
docker compose build --no-cache

<em># Mit neuen Images starten</em>
docker compose up -d

<em># Alte Images aufräumen</em>
docker image prune -a -f

Automation:

yaml

<em># .gitlab-ci.yml</em>
scheduled-update:
  schedule:
    - cron: "0 2 * * 0"  <em># Jeden Sonntag um 2 Uhr</em>
  script:
    - ./update-images.sh

Best Practice 14: Docker Socket absichern

Problem

yaml

<em># ❌ GEFÄHRLICH!</em>
volumes:
  - /var/run/docker.sock:/var/run/docker.sock

Risiko: Container kann Host-System übernehmen!

Lösung 1: Vermeiden

Nutzen Sie Alternativen wie Docker-in-Docker oder Buildah.

Lösung 2: Socket Proxy

yaml

services:
  docker-proxy:
    image: tecnativa/docker-socket-proxy
    environment:
      CONTAINERS: 1
      IMAGES: 1
      VOLUMES: 0
      POST: 0
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro

  app:
    environment:
      DOCKER_HOST: tcp://docker-proxy:2375

Best Practice 15: .dockerignore nutzen

# .dockerignore
node_modules
npm-debug.log
.git
.env
.env.*
*.md
.DS_Store
.vscode
.idea
coverage
__pycache__
*.pyc
.pytest_cache
dist
build
secrets/
*.key
*.pem

Warum?

  • Verhindert Secrets im Image
  • Kleinere Images
  • Schnellere Builds

Security Checklist für Production

Image

  • Minimales Base Image (Alpine/Distroless)
  • Fixierte Versionen (kein latest)
  • Non-Root User
  • Multi-Stage Build
  • Vulnerability Scan durchgeführt
  • Keine Secrets im Image
  • .dockerignore vorhanden
  • Health Check implementiert

Container

  • Read-Only Filesystem
  • Ressourcen-Limits gesetzt
  • Capabilities eingeschränkt
  • Logging konfiguriert
  • Network Isolation
  • Restart Policy gesetzt

Infrastruktur

  • TLS/SSL aktiviert
  • Firewall konfiguriert
  • Regelmäßige Updates
  • Monitoring aktiv
  • Backup-Strategie
  • Secrets Management
  • Audit Logging

Tools und Automation

Hadolint – Dockerfile Linter

bash

<em># Installation</em>
docker pull hadolint/hadolint

<em># Dockerfile prüfen</em>
docker run --rm -i hadolint/hadolint < Dockerfile

<em># In CI/CD</em>
docker run --rm -i hadolint/hadolint < Dockerfile || exit 1

Dockle – Container Linter

bash

<em># Installation</em>
docker pull goodwithtech/dockle

<em># Image prüfen</em>
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
  goodwithtech/dockle myapp:latest

GitHub Actions Security

yaml

name: Docker Security

on: [push]

jobs:
  security:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Build image
        run: docker build -t myapp .
      
      - name: Run Trivy
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: myapp:latest
          severity: CRITICAL,HIGH
      
      - name: Run Hadolint
        uses: hadolint/hadolint-action@v3.1.0
        with:
          dockerfile: Dockerfile

Incident Response

Container kompromittiert?

bash

<em># 1. Container isolieren</em>
docker network disconnect bridge suspicious-container

<em># 2. Logs sichern</em>
docker logs suspicious-container > incident.log
docker inspect suspicious-container > incident-details.json

<em># 3. Forensik</em>
docker export suspicious-container > container-filesystem.tar

<em># 4. Container stoppen</em>
docker stop suspicious-container

<em># 5. Analyse durchführen</em>
<em># 6. Neue, sichere Version deployen</em>

Zusammenfassung

Die 15 wichtigsten Security-Maßnahmen:

  1. ✅ Non-Root User verwenden
  2. ✅ Minimale Base Images
  3. ✅ Fixierte Versionen
  4. ✅ Keine Secrets im Image
  5. ✅ Multi-Stage Builds
  6. ✅ Vulnerability Scanning
  7. ✅ Read-Only Filesystem
  8. ✅ Ressourcen-Limits
  9. ✅ Network Isolation
  10. ✅ Capabilities einschränken
  11. ✅ Health Checks
  12. ✅ Security Logging
  13. ✅ Regelmäßige Updates
  14. ✅ Docker Socket absichern
  15. ✅ .dockerignore nutzen

Security ist kein Zustand, sondern ein Prozess!

Author: Andreas Lang

Sphinx-Flashdesign.de

Andreas Lang konzentriert sich seit zwei Jahrzehnten auf die Webentwicklung und Webdesign mit dem Schwerpunkt PHP, Laravel und Javascript und betreut seine Kunden mit Herz und Seele in allen Bereichen von Entwicklung, Design, Suchmaschinenoptimierung, IT-Recht, IT-Sicherheit etc.