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?
| Modul | Teil | Thema | Status |
|---|---|---|---|
| 1: Foundations | 1 | Erste Pipeline | ✅ |
| 2 | Security Gates (OWASP & Trivy) | ✅ | |
| 3 | Coverage Gates (JaCoCo) | ✅ | |
| 4 | Quality Gates (SonarQube) | ✅ | |
| 2: Docker | 5 | Multi-Stage Builds | ✅ |
| 6 | Container Security & SBOM | 📍 DU BIST HIER | |
| 7 | Registry Integration | ⏳ Nächste Woche | |
| 3: Deployment | 8-10 | Blue-Green, Canary, GitOps | ⏳ |
| 4: Enterprise | 11-12 | Jenkins 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?
- SBOM (Software Bill of Materials):
- Liste ALLER Components im Image
- Alle Dependencies mit exakter Version
- Transitive Dependencies sichtbar
- Machine-readable (JSON, CycloneDX)
- Secret-Scanning:
- AWS Keys im Image?
- Private Keys committed?
- API-Tokens hardcoded?
- 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
curlPackage: 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?
- Transparency: Du weißt, was in Production läuft
- Compliance: Viele Regulierungen verlangen SBOMs
- Supply-Chain-Security: Trackbar bei Incidents
- 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 execfunktioniert 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?
- 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
- Kein Shell:
docker exec -it container sh→ FEHLER- Gut! Shell ist häufigster RCE-Vector
- 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 historyzeigt 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?
- runAsNonRoot: true
- Container MUSS als non-root laufen
- Falls root-User: Pod wird nicht gestartet
- allowPrivilegeEscalation: false
- Verhindert Privilege-Escalation
- User kann nicht zu root werden
- capabilities drop ALL
- Entfernt alle Linux Capabilities
- App hat nur minimal Required Permissions
- 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:
- Log4Shell Dezember 2021: Unternehmen ohne SBOM brauchten WOCHEN um zu prüfen: „Haben wir Log4j? Welche Version?“
- Compliance: EU Cyber Resilience Act, Executive Order 14028, …
- 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:
- CRITICAL + HIGH: Sofort fixen
- MEDIUM: Priorisieren, Fix planen
- 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:
- Nimm dein Image aus Teil 5
- Erstelle Distroless-Variant
- Generiere SBOMs für beide
- Vergleiche:
- Image-Size
- Vulnerability-Count
- Build-Time
- Complexity
- 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) ✅
- Erste Pipeline ✅
- Security Gates (OWASP & Trivy Basic) ✅
- Coverage Gates (JaCoCo) ✅
- Quality Gates (SonarQube) ✅
Modul 2: Docker & Container (Teil 5-7) 🔄 5. Multi-Stage Builds ✅ 6. Container Security & SBOM ✅ DU 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:
- 📧 code.sentinel@javafleet.de
- 🐦 @CodeSentinel247
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

