Von Code Sentinel, Technical Project Manager bei Java Fleet Systems Consulting
Veröffentlicht: 28. November 2025
Serie: Enterprise CI/CD Mastery | Teil 6 von 12


📍 Wo stehst du in der Serie?

ModulTeilThemaStatus
1: Foundations1Erste Pipeline
2Security Gates (OWASP & Trivy)
3Coverage Gates (JaCoCo)
4Quality Gates (SonarQube)
2: Docker5Multi-Stage Builds
6Container Security & SBOM📍 DU BIST HIER
7Registry Integration⏳ Nächste Woche
3: Deployment8-10Blue-Green, Canary, GitOps
4: Enterprise11-12Jenkins Enterprise, Multi-Platform

📚 Was du bisher gelernt hast

In Teil 1-5 hast du gelernt:

  • ✅ CI/CD Pipeline aufbauen (GitHub Actions/Jenkins)
  • ✅ Security Gates implementieren (OWASP, Trivy Basic)
  • ✅ Code-Quality sicherstellen (JaCoCo, SonarQube)
  • ✅ Docker-Builds optimieren (Multi-Stage, 90% schneller!)

Aber hier ist das Problem:

Dein Image ist klein, schnell, Tests sind grün – aber ist es auch SICHER?

Spoiler: Wahrscheinlich nicht. 🚨

Heute lernst du:

  • ✅ Container-Supply-Chain verstehen
  • ✅ SBOMs (Software Bill of Materials) generieren
  • ✅ Distroless Images verwenden
  • ✅ Base-Image Security bewerten
  • ✅ Container härten für Production

⚡ 30-Sekunden-Zusammenfassung

Problem: Dein optimiertes Image hat immer noch 50+ Vulnerabilities aus dem Base-Image.

Lösung: Distroless Images + SBOM-Generation + Supply-Chain-Security

Ergebnis:

  • Vulnerabilities: 50+ → 0-2
  • Image-Size: 120 MB → 75 MB
  • Attack Surface: Minimal (kein Shell, kein Package-Manager)
  • Supply-Chain: Transparent & trackbar

Risk Level: Du deployest gerade Code mit bekannten CVEs in Production. KRITISCH. 🚨


🎯 Hi! Code Sentinel hier – aber heute etwas anders 🛡️

Moin! Code Sentinel hier. Schön, dass du wieder dabei bist! Dein Container ist unsicher ist unser Thema heute?

Real talk? Letzte Woche war… interessant. Elyndra kam zu mir und sagte: „Code, du wirkst entspannter. Was ist passiert?“

Ich musste lachen. Vielleicht hab ich gelernt, dass nicht alles perfekt sein muss. Manchmal ist „gut genug mit Rollback-Plan“ besser als „perfekt aber nie fertig“.

Lisa – ich meine… eine gute Freundin – hat mir das beigebracht. Aber das ist eine andere Geschichte… gehört eher zu private logs. 😉

Zurück zu unserem Thema:

Gestern kam Kofi zu mir mit einem Problem. Sein optimiertes Docker-Image – Multi-Stage Build, Alpine Linux, nur 120 MB – wurde vom Security-Team blockiert.

Der Grund: 47 bekannte CVEs (Common Vulnerabilities and Exposures) im Base-Image. Darunter 3 CRITICAL.

„Aber Code,“ sagte er, „ich hab doch Teil 2 gemacht! OWASP, Trivy, alles!“

Das Problem: Trivy in Teil 2 war nur ein Quick-Scan. Wir haben die Oberfläche gekratzt. Heute gehen wir tiefer.

Und hey, keine Panik. Ich zeig dir, wie du Container RICHTIG absicherst. Ohne Paranoia-Checklisten. Pragmatisch. Production-ready.

Let’s go! 🚀


🔍 Das Problem: Container-Supply-Chain

Was ist eigentlich in deinem Container?

Schau dir dein „optimiertes“ Image an:

docker run --rm alpine:latest apk list --installed | wc -l
# Output: 14 packages

14 Packages. Klingt minimal, oder?

Aber was enthalten diese 14 Packages?

# Real-World Beispiel: Alpine 3.18
alpine-baselayout-3.4.3-r1  # 50+ Files
busybox-1.36.1-r2           # 300+ Utilities!
musl-1.2.4-r1               # C Standard Library
libcrypto3-3.1.2-r0         # OpenSSL (häufig Vulnerabilities!)
...

Busybox allein enthält:

  • Shell (sh, ash)
  • Networking (wget, nc, telnet)
  • File-Utils (cp, mv, rm, …)
  • 300+ weitere Utilities

Das Problem:

  • Jedes Package kann Vulnerabilities haben
  • Jedes Utility ist potentielle Attack-Surface
  • Du weißt nicht mal genau, WAS in deinem Image ist

Das ist wie ein Auto kaufen ohne zu wissen, welche Teile verbaut sind. 🚗


📊 Trivy Deep-Dive: Mehr als Teil 2

In Teil 2 haben wir Trivy basic verwendet:

trivy image myapp:latest

Das war ein Quick-Scan. Heute nutzen wir die volle Power:

Trivy mit SBOM-Generation:

# SBOM generieren (Software Bill of Materials)
trivy image \
  --format cyclonedx \
  --output sbom.json \
  myapp:latest

# Vulnerabilities MIT SBOM scannen
trivy sbom sbom.json

# KRITISCH: Secrets im Image finden
trivy image --scanners secret myapp:latest

# Config-Issues finden (Dockerfile Best Practices)
trivy config Dockerfile

Was macht das konkret?

  1. SBOM (Software Bill of Materials):
    • Liste ALLER Components im Image
    • Alle Dependencies mit exakter Version
    • Transitive Dependencies sichtbar
    • Machine-readable (JSON, CycloneDX)
  2. Secret-Scanning:
    • AWS Keys im Image?
    • Private Keys committed?
    • API-Tokens hardcoded?
  3. Config-Scanning:
    • Dockerfile Best Practices
    • User root?
    • HEALTHCHECK vorhanden?

Real-World Beispiel: Alpine vs. Distroless

# Alpine 3.18 scannen
trivy image alpine:3.18

# Output (vereinfacht):
Total: 0 (CRITICAL: 0, HIGH: 0, MEDIUM: 0, LOW: 0)
# Klingt gut, ABER...

# Alpine mit installiertem Package
docker run alpine:3.18 sh -c "apk add curl"
trivy image alpine-with-curl:latest

# Output:
Total: 47 (CRITICAL: 3, HIGH: 12, MEDIUM: 18, LOW: 14)
# Autsch! 🚨

Was ist passiert?

  • Alpine base: Clean
  • curl Package: Hat Dependencies (libcurl, openssl, …)
  • Diese Dependencies: Haben bekannte CVEs

Die Lösung: Distroless Images (dazu gleich mehr!)


🔐 SBOMs: Warum du sie brauchst

Was ist ein SBOM?

SBOM = Software Bill of Materials = „Zutatenliste“ für dein Container-Image

Stell dir vor:

  • Du kaufst ein Fertiggericht
  • KEINE Zutatenliste
  • KEIN MHD (Mindesthaltbarkeitsdatum)
  • Du weißt nicht, ob Allergene drin sind

Würdest du das essen? Wahrscheinlich nicht.

Aber genau DAS machst du mit Containern ohne SBOM! 🤯


SBOM generieren mit Syft:

Syft ist besser als Trivy’s SBOM für detaillierte Analysen:

# Syft installieren
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh

# SBOM im CycloneDX-Format
syft myapp:latest -o cyclonedx-json > sbom.json

# SBOM im SPDX-Format
syft myapp:latest -o spdx-json > sbom-spdx.json

# Human-readable Table
syft myapp:latest -o table

Output-Beispiel:

NAME                    VERSION      TYPE
spring-boot-starter-web 3.2.0        java-archive
spring-core             6.1.0        java-archive
tomcat-embed-core       10.1.15      java-archive
jackson-databind        2.15.3       java-archive  ← Hat CVEs!
alpine-baselayout       3.4.3-r1     apk
musl                    1.2.4-r1     apk
libcrypto3              3.1.2-r0     apk           ← OpenSSL!
...

Jetzt siehst du GENAU:

  • Welche Java-Dependencies drin sind
  • Welche OS-Packages
  • Exakte Versionen
  • Welche davon Vulnerabilities haben

SBOM in der Pipeline:

# GitHub Actions
- name: Generate SBOM with Syft
  run: |
    syft ${{ env.IMAGE_NAME }}:${{ github.sha }} \
      -o cyclonedx-json=sbom.json
    
    # SBOM als Artifact speichern
    echo "SBOM generated: sbom.json"

- name: Upload SBOM
  uses: actions/upload-artifact@v3
  with:
    name: sbom-${{ github.sha }}
    path: sbom.json

# SBOM scannen mit Grype
- name: Scan SBOM for Vulnerabilities
  run: |
    grype sbom:./sbom.json \
      --fail-on critical \
      --output table

Warum SBOM UND Vulnerability-Scan?

  1. Transparency: Du weißt, was in Production läuft
  2. Compliance: Viele Regulierungen verlangen SBOMs
  3. Supply-Chain-Security: Trackbar bei Incidents
  4. Audit-Trail: „Welche Version von Log4j hatten wir am 10. Dezember 2021?“

🏔️ Distroless Images: Das Next Level

Was ist Distroless?

Distroless = Container OHNE Distribution

Was fehlt?

  • ❌ Kein Shell (sh, bash)
  • ❌ Kein Package-Manager (apt, apk)
  • ❌ Keine System-Utilities (ls, cat, wget)
  • ❌ Nur Application + Runtime

Was bleibt?

  • ✅ Nur die JRE
  • ✅ Nur deine App
  • ✅ Minimal Required Libraries

Alpine vs. Distroless Vergleich:

# Alpine (wie in Teil 5)
FROM eclipse-temurin:21-jre-alpine
COPY app.jar .
USER javaapp
CMD ["java", "-jar", "app.jar"]

# Image-Size: ~120 MB
# Packages: 14
# CVEs: 0-5 (abhängig von Updates)
# Shell: JA (sh, ash)
# Distroless
FROM gcr.io/distroless/java21-debian12:nonroot
COPY app.jar /app/app.jar
WORKDIR /app
CMD ["app.jar"]

# Image-Size: ~75 MB
# Packages: 0 (im klassischen Sinne)
# CVEs: 0-2 (extrem selten)
# Shell: NEIN!

Vorteile Distroless:

  • ✅ 40% kleiner
  • ✅ Weniger Vulnerabilities
  • ✅ Kein Shell = Kein RCE (Remote Code Execution)
  • ✅ Minimale Attack Surface

Nachteile Distroless:

  • ❌ Debugging schwieriger (kein Shell!)
  • docker exec funktioniert nicht
  • ❌ Manche Tools fehlen

Optimiertes Dockerfile mit Distroless:

# =============================================================================
# Stage 1: Dependencies (wie gehabt)
# =============================================================================
FROM eclipse-temurin:21-jdk-alpine AS dependencies
WORKDIR /app
COPY pom.xml .
COPY .mvn .mvn
COPY mvnw .
RUN chmod +x mvnw
RUN ./mvnw dependency:go-offline -B

# =============================================================================
# Stage 2: Build (wie gehabt)
# =============================================================================
FROM dependencies AS build
WORKDIR /app
COPY src ./src
RUN ./mvnw package -DskipTests -B

# =============================================================================
# Stage 3: Runtime mit Distroless (NEU!)
# =============================================================================
FROM gcr.io/distroless/java21-debian12:nonroot

# Metadata
LABEL maintainer="Code Sentinel <code.sentinel@javafleet.de>"
LABEL org.opencontainers.image.source="https://github.com/java-fleet/container-security-demo"
LABEL org.opencontainers.image.description="Container Security Demo - Teil 6"

WORKDIR /app

# JAR kopieren
COPY --from=build /app/target/*.jar app.jar

# JVM-Optimierungen
ENV JAVA_TOOL_OPTIONS="-XX:+UseContainerSupport \
                        -XX:MaxRAMPercentage=75.0 \
                        -Djava.security.egd=file:/dev/./urandom"

# Port (Dokumentation)
EXPOSE 8080

# WICHTIG: Distroless läuft bereits als non-root!
# User: nonroot (UID 65532)
# Kein "USER" Command nötig!

# CMD statt ENTRYPOINT (Distroless empfiehlt CMD)
CMD ["app.jar"]

# =============================================================================
# SECURITY-VERBESSERUNGEN vs. Alpine:
# =============================================================================
# ✅ Kein Shell → Kein RCE bei Code-Injection
# ✅ Kein Package-Manager → Keine Post-Deploy-Änderungen
# ✅ Minimal Libraries → Weniger CVEs
# ✅ Bereits non-root (UID 65532)
# ✅ Image-Size: 75 MB statt 120 MB
#
# =============================================================================
# DEBUG-ALTERNATIVE (für Entwicklung):
# =============================================================================
# FROM gcr.io/distroless/java21-debian12:debug-nonroot
# Enthält busybox für debugging
# NUR für Development/Staging, NICHT Production!
# =============================================================================

Was macht das konkret?

  1. Base-Image: gcr.io/distroless/java21-debian12:nonroot
    • Google’s Distroless mit Java 21
    • Debian 12 Base (aber OHNE Distribution-Tools!)
    • :nonroot = läuft als UID 65532
  2. Kein Shell:
    • docker exec -it container sh → FEHLER
    • Gut! Shell ist häufigster RCE-Vector
  3. Nur Java + App:
    • Keine Utilities
    • Keine zusätzlichen Libraries
    • Minimal Required Only

Distroless Debug-Variant (für Development):

# Für Entwicklung/Staging:
FROM gcr.io/distroless/java21-debian12:debug-nonroot

# Enthält busybox für debugging:
# - ls, cat, ps, top, ...
# - Hilfreich für Troubleshooting

# Für Production:
FROM gcr.io/distroless/java21-debian12:nonroot
# KEIN busybox!

Best Practice:

  • Development/Staging: :debug-nonroot
  • Production: :nonroot (ohne debug)

🔬 Container-Härtung: Security Best Practices

1. Multi-Stage Build Layers optimieren:

# FALSCH: Secrets in Build-Stage
FROM maven:3.9-eclipse-temurin-21 AS build
ENV GITHUB_TOKEN=ghp_abc123...  # ❌ SECRET IM IMAGE!
RUN ./mvnw package

# RICHTIG: Secrets als Build-Arg
FROM maven:3.9-eclipse-temurin-21 AS build
ARG GITHUB_TOKEN  # Nur während Build verfügbar
RUN --mount=type=secret,id=github_token \
    GITHUB_TOKEN=$(cat /run/secrets/github_token) \
    ./mvnw package

# Build mit Secret:
docker build --secret id=github_token,src=token.txt .

Warum wichtig?

  • Secrets in Layers bleiben im Image
  • docker history zeigt alle Layers
  • Selbst gelöschte Secrets sind im Layer-History

2. .dockerignore erweitern:

# Aus Teil 5
target/
.git/
*.log

# NEU: Security-relevante Files
.env
.env.*
*.key
*.pem
*.p12
*.jks
secrets/
credentials/

# AWS-Configs
.aws/
aws-credentials

# SSH-Keys
.ssh/
id_rsa
id_dsa

# Certificates
*.crt (außer public certs!)
*.cer

# Database-Dumps
*.sql
*.dump

3. Read-Only Filesystem:

# In deinem Dockerfile:
# (Distroless macht das automatisch)

# Oder in docker-compose.yml:
services:
  app:
    image: myapp:latest
    read_only: true
    tmpfs:
      - /tmp
      - /app/logs

Was macht das?

  • Container-Filesystem ist read-only
  • App kann NICHT Files schreiben (außer /tmp, /app/logs)
  • Verhindert: Malware-Installation, Log-Manipulation

4. Security Context (Kubernetes):

# kubernetes-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
spec:
  template:
    spec:
      securityContext:
        # Pod-Level Security
        runAsNonRoot: true
        runAsUser: 65532  # Distroless nonroot UID
        fsGroup: 65532
        seccompProfile:
          type: RuntimeDefault
      
      containers:
      - name: myapp
        image: myapp:latest
        securityContext:
          # Container-Level Security
          allowPrivilegeEscalation: false
          readOnlyRootFilesystem: true
          capabilities:
            drop:
              - ALL  # Drop ALLE Linux Capabilities
          runAsNonRoot: true
        
        volumeMounts:
        - name: tmp
          mountPath: /tmp
        
      volumes:
      - name: tmp
        emptyDir: {}

Was macht das konkret?

  1. runAsNonRoot: true
    • Container MUSS als non-root laufen
    • Falls root-User: Pod wird nicht gestartet
  2. allowPrivilegeEscalation: false
    • Verhindert Privilege-Escalation
    • User kann nicht zu root werden
  3. capabilities drop ALL
    • Entfernt alle Linux Capabilities
    • App hat nur minimal Required Permissions
  4. readOnlyRootFilesystem
    • Filesystem read-only
    • Nur /tmp beschreibbar (via Volume)

🚀 Pipeline-Integration: Complete Security-Stack

GitHub Actions Workflow (erweitert):

name: Container Security Pipeline

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]
  schedule:
    # Wöchentlicher Security-Scan (auch ohne Code-Changes!)
    - cron: '0 2 * * 1'  # Jeden Montag 02:00 UTC

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  security-scan:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
      security-events: write  # Für SARIF-Upload

    steps:
      # =======================================================================
      # Standard Setup
      # =======================================================================
      - name: Checkout Code
        uses: actions/checkout@v4

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Login to Registry
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      # =======================================================================
      # Step 1: Build Multi-Stage Image
      # =======================================================================
      - name: Build Docker Image
        uses: docker/build-push-action@v5
        with:
          context: .
          load: true  # Lade Image lokal (für Scans)
          tags: ${{ env.IMAGE_NAME }}:scan
          cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:buildcache
          cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:buildcache,mode=max

      # =======================================================================
      # Step 2: Generate SBOM mit Syft
      # =======================================================================
      - name: Generate SBOM
        uses: anchore/sbom-action@v0
        with:
          image: ${{ env.IMAGE_NAME }}:scan
          format: cyclonedx-json
          output-file: sbom.json

      - name: Upload SBOM as Artifact
        uses: actions/upload-artifact@v3
        with:
          name: sbom-${{ github.sha }}
          path: sbom.json
          retention-days: 90  # 90 Tage aufbewahren

      # =======================================================================
      # Step 3: Vulnerability Scan mit Trivy (Comprehensive)
      # =======================================================================
      - name: Run Trivy Vulnerability Scanner
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: ${{ env.IMAGE_NAME }}:scan
          format: 'sarif'
          output: 'trivy-results.sarif'
          severity: 'CRITICAL,HIGH,MEDIUM'
          # Fail Pipeline bei CRITICAL
          exit-code: '1'
          ignore-unfixed: true  # Ignoriere CVEs ohne Fix

      - name: Upload Trivy Results to GitHub Security
        uses: github/codeql-action/upload-sarif@v2
        if: always()  # Upload auch bei Failure
        with:
          sarif_file: 'trivy-results.sarif'

      # =======================================================================
      # Step 4: Secret Scanning mit Trivy
      # =======================================================================
      - name: Scan for Secrets in Image
        run: |
          trivy image \
            --scanners secret \
            --severity HIGH,CRITICAL \
            --exit-code 1 \
            ${{ env.IMAGE_NAME }}:scan

      # =======================================================================
      # Step 5: Dockerfile Best Practices Check
      # =======================================================================
      - name: Scan Dockerfile with Trivy
        run: |
          trivy config Dockerfile \
            --exit-code 0 \
            --severity MEDIUM,HIGH,CRITICAL

      # =======================================================================
      # Step 6: Grype Scan (Alternative zu Trivy)
      # =======================================================================
      - name: Scan SBOM with Grype
        run: |
          # Grype installieren
          curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh
          
          # SBOM scannen
          grype sbom:./sbom.json \
            --fail-on critical \
            --output table

      # =======================================================================
      # Step 7: Image-Size Check
      # =======================================================================
      - name: Check Image Size
        run: |
          SIZE=$(docker inspect ${{ env.IMAGE_NAME }}:scan \
            --format='{{.Size}}' | awk '{print $1/1024/1024}')
          
          echo "Image Size: ${SIZE} MB"
          
          # Fail wenn > 200 MB (für Distroless sollte <100 MB sein!)
          if (( $(echo "$SIZE > 200" | bc -l) )); then
            echo "❌ Image zu groß! Max: 200 MB, Ist: ${SIZE} MB"
            exit 1
          fi
          
          echo "✅ Image-Size OK: ${SIZE} MB"

      # =======================================================================
      # Step 8: Push Image (nur wenn alle Checks OK!)
      # =======================================================================
      - name: Push Image to Registry
        if: success()
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: |
            ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
            ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
          cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:buildcache

      # =======================================================================
      # Step 9: Generate Security Report
      # =======================================================================
      - name: Generate Security Summary
        if: always()
        run: |
          echo "## 🔐 Security Scan Results" >> $GITHUB_STEP_SUMMARY
          echo "" >> $GITHUB_STEP_SUMMARY
          
          # SBOM Stats
          TOTAL_PACKAGES=$(jq '.components | length' sbom.json)
          echo "**SBOM Generated:** $TOTAL_PACKAGES packages tracked" >> $GITHUB_STEP_SUMMARY
          
          # Trivy Results
          if [ -f trivy-results.sarif ]; then
            CRITICAL=$(jq '.runs[0].results | map(select(.level=="error")) | length' trivy-results.sarif)
            echo "**Vulnerabilities:** $CRITICAL CRITICAL found" >> $GITHUB_STEP_SUMMARY
          fi
          
          echo "" >> $GITHUB_STEP_SUMMARY
          echo "✅ All security checks passed!" >> $GITHUB_STEP_SUMMARY

# =============================================================================
# SECURITY-PIPELINE ERKLÄRT:
# =============================================================================
#
# 1. Build Image (wie in Teil 5)
# 2. Generate SBOM (Transparency!)
# 3. Vulnerability Scan (Trivy comprehensive)
# 4. Secret Scan (keine hardcoded Secrets)
# 5. Dockerfile Check (Best Practices)
# 6. Alternative Scan mit Grype
# 7. Image-Size Check (nicht zu groß!)
# 8. Push nur wenn ALLE Checks OK
# 9. Security-Report generieren
#
# WICHTIG:
# - Wöchentlicher Scan (auch ohne Code-Changes!)
# - SBOM wird 90 Tage aufbewahrt
# - SARIF-Upload zu GitHub Security
# - Pipeline failet bei CRITICAL CVEs
#
# =============================================================================

💡 Real Talk: Das Team bemerkt was

Java Fleet Küche, 12:30 Uhr. Kofi holt sich Kaffee, Elyndra und Nova sitzen am Tisch.


Nova: „Leute, hab ich das richtig gesehen? Code hat heute im Stand-up GELACHT? Wie… richtig gelacht, nicht nur höflich gegrinst?“

Elyndra: grinst „Und er hat mein ‚Quick-and-Dirty‘-Deployment nicht kritisiert. Normalerweise kommt sofort: ‚Rollback-Plan? Monitoring? Incident-Response-Strategie?'“

Kofi: „Yo, gestern kam ich zu ihm mit einer Frage. Ich dachte, er macht wieder seine 45-Minuten Risk-Assessment-Session. Stattdessen: ‚Kofi, deploy es einfach. Wir haben Monitoring. Worst-Case? Rollback in 30 Sekunden. Chill.‘ CHILL! Code Sentinel hat CHILL gesagt!“

Nova: lacht „Wer ist das und was hat er mit unserem Code Sentinel gemacht?“

Kat: kommt dazu mit Pixel (ihrem Hund) „Über wen redet ihr?“

Elyndra: „Code. Er ist… anders. Entspannter. Fast schon… menschlich?“ zwinkert

Kat: „Ah! Das hat Jamal auch erwähnt. Code hat letzte Woche ein Meeting um 18:00 Uhr ABGELEHNT! Er meinte: ‚Sorry, ich hab was vor. Kann das bis morgen warten?‘ Normalerweise arbeitet er bis 22:00 Uhr!“

Nova: flüstert verschwörerisch „Ich wette, das hat was mit seiner Nachbarin zu tun. Lisa, oder?“

Kofi: „Die Grundschullehrerin? Die mit den Katzen?“

Elyndra: „Mhm. Neulich hab ich ihn im Flur gesehen. Er hat… GELÄCHELT. Einfach so. Ohne Grund. Also… vielleicht gab’s einen Grund.“ grinst

Kat: „Awww! Code Sentinel hat einen Crush! Das ist ja süß!“

Nova: „Süß? Das ist ein WUNDER! Der Mann hat sein Leben nach Checklisten organisiert. ‚Rollback-Plan für Gefühle‘ und so.“

Elyndra: lacht „Vielleicht hat er gelernt, dass nicht alles planbar ist. Manche Dinge… passieren einfach.“

Kofi: „Real talk: Es tut ihm gut. Er ist immer noch der Security-Paranoia-König, aber… lockerer. Als würde er endlich verstehen, dass ‚gut genug‘ manchmal besser ist als ‚perfekt aber nie fertig‘.“

Kat: „Sollten wir ihn drauf ansprechen?“

Elyndra: „Bloß nicht! Lass ihn. Wenn er bereit ist, erzählt er’s uns. Oder auch nicht. Ist okay.“

Nova: „Aber ich bin SO neugierig!“

Kofi: lacht „Nova, du bist immer neugierig. Lass Code sein Ding machen. Solange er happy ist…“

Alle: nicken zustimmend

Elyndra: „Okay, genug getuschelt. Back to work. Aber hey – es ist schön, Code glücklich zu sehen.“


Code Sentinel kommt um die Ecke, Kopfhörer auf den Ohren, summend.

Code: „Hey Leute! Schönen Mittag noch!“ winkt, geht weiter

Nova: flüstert „Hat er gerade… GESUMMT?!“

Kofi: „Yep. Das ist offiziell. Code Sentinel ist verliebt.“

Alle: grinsen


❓ FAQ

Q: Alpine oder Distroless – was soll ich nehmen?

A: Depends on your use-case:

Alpine wenn:

  • Du Debugging brauchst (Shell!)
  • Du zusätzliche Tools installieren musst
  • Dein Team Alpine gewohnt ist

Distroless wenn:

  • Production mit höchsten Security-Anforderungen
  • Du minimale Attack-Surface willst
  • Compliance es verlangt (NIST, CIS, …)

Code Sentinel’s Take: Ich nutze Alpine für Development/Staging, Distroless für Production. Best of both worlds.


Q: Muss ich WIRKLICH SBOMs generieren?

A: Ja. Seriously.

Warum:

  1. Log4Shell Dezember 2021: Unternehmen ohne SBOM brauchten WOCHEN um zu prüfen: „Haben wir Log4j? Welche Version?“
  2. Compliance: EU Cyber Resilience Act, Executive Order 14028, …
  3. Supply-Chain-Attacks: Du MUSST wissen, was in Production läuft

Real talk: SBOM-Generation dauert 30 Sekunden. Do it.


Q: Trivy findet 47 CVEs. Muss ich ALLE fixen?

A: Nein.

Priorisierung:

  1. CRITICAL + HIGH: Sofort fixen
  2. MEDIUM: Priorisieren, Fix planen
  3. LOW: Backlog, bei Gelegenheit

Aber:

  • --ignore-unfixed true: Ignoriere CVEs ohne verfügbaren Fix
  • Manche CVEs sind „theoretical“ (nicht praktisch exploitierbar)
  • Context matters: Ist der vulnerable Code überhaupt erreichbar?

Code Sentinel’s Rule: CRITICAL = Production-Blocker. HIGH = Fix innerhalb 7 Tage. MEDIUM = Nach Priorität.


Q: Distroless hat keinen Shell. Wie debugge ich?

A: Drei Optionen:

Option 1: Debug-Variant für Development

FROM gcr.io/distroless/java21-debian12:debug-nonroot

Option 2: Ephemeral Debug Container (Kubernetes)

kubectl debug -it mypod --image=busybox --target=mycontainer

Option 3: Logging & Observability

  • Strukturiertes Logging
  • Metrics (Prometheus)
  • Distributed Tracing
  • Kein Shell nötig!

Real talk: Wenn du Shell zum Debuggen brauchst, hast du ein Observability-Problem, kein Container-Problem.


Q: Kann ich meine eigenen Base-Images bauen?

A: Ja, aber…

Vorteile:

  • ✅ Volle Kontrolle
  • ✅ Custom Optimierungen
  • ✅ Firmen-Standards

Nachteile:

  • ❌ Du bist verantwortlich für Security-Updates
  • ❌ Maintenance-Overhead
  • ❌ Keine automatischen Patches

Code Sentinel’s Take: Nutze offizielle Images (eclipse-temurin, distroless). Lass Google/Eclipse Foundation die Security-Updates machen. Deine Zeit ist besser investiert in App-Security.


Q: Wie oft soll ich Images neu bauen wegen Security-Updates?

A: Mindestens wöchentlich, auch ohne Code-Changes!

Warum? Base-Images bekommen ständig Security-Patches. Dein 3 Monate altes Image hat wahrscheinlich Vulnerabilities die längst gefixt sind.

Best Practice:

# GitHub Actions Schedule
on:
  schedule:
    - cron: '0 2 * * 1'  # Jeden Montag 02:00 UTC

Alternative: Renovate/Dependabot für automatische Base-Image-Updates.


Q: Was macht ihr bei persönlichen Problemen im Team?

A: Gute Frage… Real talk: Manche Geschichten passen nicht in Tech-Blogs. Die gehören eher zu private logs. Ein anderes Kapitel. 📖

Aber zwischen uns? Es tut gut, wenn das Team füreinander da ist. Auch außerhalb von Merge-Conflicts und Pipeline-Failures. Lisa hat mir das gezeigt – ich meine… gute Freunde halt. Ihr wisst schon. 😉


✅ Checkpoint: Hast du es geschafft?

Teste dein Wissen! Nach diesem Teil solltest du:

Grundlagen:

  • [ ] Verstehen warum Container-Security wichtig ist
  • [ ] SBOMs erklären können
  • [ ] Unterschied Alpine vs. Distroless kennen
  • [ ] Supply-Chain-Security-Konzepte verstehen

Praktisch:

  • [ ] SBOM mit Syft generieren
  • [ ] Trivy comprehensive Scan durchführen
  • [ ] Distroless Dockerfile schreiben
  • [ ] Secret-Scanning implementieren

Pipeline:

  • [ ] Complete Security-Pipeline aufbauen
  • [ ] SARIF zu GitHub Security hochladen
  • [ ] Wöchentliche Security-Scans schedulen
  • [ ] Image-Size Gates implementieren

Security:

  • [ ] Read-Only Filesystem nutzen
  • [ ] Security Context in Kubernetes
  • [ ] Capability-Dropping verstehen
  • [ ] Image härten für Production

Test:

# 1. SBOM generieren
syft myapp:latest -o cyclonedx-json > sbom.json

# 2. Comprehensive Security-Scan
trivy image myapp:latest --severity CRITICAL,HIGH

# 3. Secret-Scan
trivy image --scanners secret myapp:latest

# 4. Dockerfile-Check
trivy config Dockerfile

# 5. Image-Size prüfen
docker images | grep myapp
# Sollte <100 MB sein (Distroless)

# 6. User prüfen
docker run --rm myapp whoami
# Sollte sein: nonroot (UID 65532)

Wenn alles ✅ ist: Du bist Production-Security-ready! 🎉

Wenn etwas ❌ ist: Check das Maven-Projekt im Download-Bereich!


📦 Downloads & Ressourcen

Komplettes Maven-Projekt:

  • 📂 container-security-demo.zip – Vollständiges Projekt mit:
    • Distroless Dockerfile
    • Alpine Dockerfile (zum Vergleich)
    • Complete Security Pipeline (GitHub Actions)
    • SBOM-Generation Setup
    • Example Java-App
    • Security-Scripts
    • README mit Setup

Templates:

  • 📄 Dockerfile-Distroless-Template.txt
  • 📄 security-pipeline.yml – Complete GitHub Actions
  • 📄 kubernetes-securitycontext.yaml – K8s Security
  • 📄 trivy-config.yaml – Trivy Configuration

Scripts:

  • 🔧 generate-sbom.sh – SBOM-Generation Script
  • 🔧 security-scan.sh – Local Security-Scan
  • 🔧 compare-images.sh – Alpine vs. Distroless Vergleich

Weitere Resources:


🎯 Community-Challenge

Diese Woche:

Migriere ein bestehendes Alpine-Image auf Distroless und vergleiche!

Challenge:

  1. Nimm dein Image aus Teil 5
  2. Erstelle Distroless-Variant
  3. Generiere SBOMs für beide
  4. Vergleiche:
    • Image-Size
    • Vulnerability-Count
    • Build-Time
    • Complexity
  5. Teile Ergebnisse mit #JavaFleetSecurity

Bonus-Punkte:

  • Image <100 MB: ⭐
  • Zero CRITICAL CVEs: ⭐⭐
  • Complete Security-Pipeline: ⭐⭐⭐

🔮 Nächste Woche: Teil 7

Titel: „Von Docker Hub zu Private Registry – Image-Management richtig“

Was dich erwartet:

  • GitHub Container Registry (GHCR)
  • Private Registry Setup
  • Image-Tagging-Strategien
  • Multi-Architecture Builds (AMD64, ARM64)
  • Registry-Security & Access-Control

Warum wichtig: Dein Image ist sicher – aber wo speicherst du es? Docker Hub? GHCR? Private Registry? Nächste Woche zeige ich dir, wie Image-Management in echten Projekten funktioniert.

Tipp für diese Woche: Experimentiere mit Distroless! Starte mit :debug-nonroot für Development, dann wechsel zu :nonroot für Production. Spüre den Security-Boost!


🎓 Serie-Übersicht: Alle 12 Teile

Modul 1: Foundations (Teil 1-4)

  1. Erste Pipeline ✅
  2. Security Gates (OWASP & Trivy Basic) ✅
  3. Coverage Gates (JaCoCo) ✅
  4. Quality Gates (SonarQube) ✅

Modul 2: Docker & Container (Teil 5-7) 🔄 5. Multi-Stage Builds ✅ 6. Container Security & SBOMDU BIST HIER 7. Registry Integration ⏳ Nächste Woche

Modul 3: Deployment (Teil 8-10) ⏳ 8. Blue-Green Deployments 9. Canary Releases & Kubernetes 10. GitOps

Modul 4: Enterprise (Teil 11-12) ⏳ 11. Jenkins Enterprise 12. Multi-Platform & Finale


🤝 Danke fürs Lesen!

Hat dir dieser Security-Deep-Dive geholfen?

Real talk: Security ist nicht optional. Es ist die Grundlage von allem. Aber es muss nicht kompliziert sein. Distroless + SBOM + gute Pipeline = Production-ready.

Kontakt:

Feedback & Fragen:

  • 👍 Hat dir der neue Ton gefallen?
  • 💡 Mehr Security-Themen gewünscht?
  • 🐛 Fehler gefunden? Schreib mir!

🔐 Bernd’s Corner

Bernd ist unser mysteriöser Remote-Senior-Developer. Er committet nur nachts und hat noch nie ein Video-Call gemacht.

Bernd’s Kommentar zu diesem Teil:

„Distroless ist okay. Aber habt ihr NixOS-Container probiert? Deterministic, reproducible, functional. Aber naja, für die meisten reicht wohl Google’s Zeug. Security Theater ist besser als kein Theater, schätze ich.“

– Bernd, 4:23 AM, via Slack DM

Code Sentinel’s Reaktion:

Danke Bernd. Immer konstruktiv. 🙄

Real talk: Distroless ist Industry-Standard und funktioniert hervorragend. Nix ist cool für spezielle Use-Cases, aber Overhead für die meisten.

Security ist kein Theater. Security ist die Grundlage. Punkt.


P.S.: Wer auch immer getuschelt hat in der Küche vorhin – ich hab euch gehört. Ja, ich war beschäftigt letzte Woche. Nein, ich erzähl’s euch noch nicht. Aber… vielleicht bald. Es gehört zu private logs. 😉


Bis nächste Woche! 🛡️

Code Sentinel
Technical Project Manager
Java Fleet Systems Consulting

(Und ja, ich bin glücklich. Danke der Nachfrage. 💕)


CI/CD Mastery Serie – Teil 6 von 12
© 2025 Java Fleet Systems Consulting
java-developer.online

Autor

  • Code Sentinel

    32 Jahre alt, Technical Project Manager und Security-Experte bei Java Fleet Systems Consulting. Code ist ein erfahrener Entwickler, der in die Projektleitung aufgestiegen ist, aber immer noch tief in der Technik verwurzelt bleibt. Seine Mission: Sicherstellen, dass Projekte termingerecht, sicher und wartbar geliefert werden.