CI-Builds bei kritischen CVEs wirklich blockieren, nicht nur warnen
Ein Scan-Report, den niemand liest, schuetzt kein System. Ein echtes Vulnerability Policy Gate stoppt den Build-Prozess aktiv, sobald ein Container-Image kritische Schwachstellen enthaelt, mit klaren Schwellenwerten und einem definierten Umgang mit False Positives, statt Sicherheitswarnungen als optionale Fussnote im CI-Log zu behandeln.
Inhaltsverzeichnis
- 1. Der Unterschied zwischen Melden und Blockieren
- 2. Exit-Code-basiertes Blockieren mit Trivy
- 3. Integration als eigener Pipeline-Schritt
- 4. Schwellenwert-Strategien: nicht jede Severity gleich behandeln
- 5. Umgang mit False Positives ohne das Gate auszuhoehlen
- 6. Praevention durch Basisimage-Wahl statt reines Nachfiltern
- 7. Dependency-Scan versus Image-Scan: unterschiedliche Ebenen
- 8. SBOM als Grundlage fuer schnellere Reaktion auf neue CVEs
- 9. Reporting und Nachverfolgung ueber die Pipeline hinaus
- 10. Zusammenfassung
- 11. FAQ
1. Der Unterschied zwischen Melden und Blockieren
Viele Teams richten einen Container-Scan in ihrer Pipeline ein und halten das faelschlicherweise bereits fuer ausreichende Sicherheit. Ein Scan, der lediglich einen Report generiert und der Pipeline unabhaengig vom Ergebnis mit Exit-Code 0 signalisiert, dass alles in Ordnung ist, hat aber keine tatsaechliche Schutzwirkung, weil niemand gezwungen ist, den Report vor dem Deploy zu lesen. In der Praxis verschwinden solche Reports in Build-Logs, die routinemaessig ignoriert werden, sobald der grosse gruene Haken der Pipeline erscheint.
Ein echtes Policy Gate unterscheidet sich fundamental: Der Scan-Schritt selbst entscheidet aktiv ueber Erfolg oder Misserfolg des Builds, indem er bei Ueberschreitung eines definierten Schwellenwerts einen Exit-Code ungleich null zurueckgibt, wodurch die gesamte Pipeline fehlschlaegt und der nachfolgende Deploy-Schritt gar nicht erst ausgefuehrt wird. Dieser Unterschied zwischen passiver Sichtbarkeit und aktiver Durchsetzung ist der Kern jeder wirksamen Vulnerability-Policy in CI/CD.
2. Exit-Code-basiertes Blockieren mit Trivy
Trivy, einer der verbreitetsten Open-Source-Scanner fuer Container-Images, bietet mit den Parametern --exit-code und --severity genau diesen Mechanismus direkt eingebaut. Wird --exit-code 1 zusammen mit --severity CRITICAL gesetzt, gibt Trivy Exit-Code 1 zurueck, sobald mindestens eine Schwachstelle mit dem Schweregrad CRITICAL gefunden wird, was in fast jedem CI-System automatisch zum Fehlschlagen des Jobs fuehrt. Ohne explizit gesetzten --exit-code gibt Trivy standardmaessig immer Exit-Code 0 zurueck, unabhaengig davon, wie viele oder wie schwerwiegende Schwachstellen gefunden wurden, ein Detail, das in vielen Pipeline-Konfigurationen uebersehen wird.
Ein haeufiger Konfigurationsfehler ist, den Scan zwar mit korrektem Exit-Code laufen zu lassen, aber gleichzeitig continue-on-error: true oder ein aehnliches Flag im CI-System zu setzen, meist aus der berechtigten Sorge heraus, dass ein einzelner falsch-positiver Fund die gesamte Pipeline lahmlegt. Genau dieses Flag hebt aber die gesamte Schutzwirkung des Gates wieder auf und sollte nur in einer separaten, klar gekennzeichneten Report-Only-Stufe verwendet werden, nie in der eigentlichen Blocking-Stufe.
# Blockierender Scan: schlaegt fehl bei kritischen Schwachstellen
trivy image --exit-code 1 --severity CRITICAL myapp:latest
# Kombinierter Schwellenwert: HIGH und CRITICAL blockieren
trivy image --exit-code 1 --severity HIGH,CRITICAL myapp:latest
# Nur fixierbare Schwachstellen beruecksichtigen (ignoriert CVEs ohne verfuegbaren Patch)
trivy image --exit-code 1 --severity CRITICAL --ignore-unfixed myapp:latest
3. Integration als eigener Pipeline-Schritt
Ein Policy Gate gehoert als eigenstaendiger Schritt zwischen den Image-Build und den Push in die produktive Registry, niemals danach. Wird der Scan erst nach dem Push ausgefuehrt, ist das verwundbare Image bereits verfuegbar und koennte theoretisch schon deployt worden sein, bevor das Scan-Ergebnis vorliegt. Die richtige Reihenfolge ist: Image lokal in der Pipeline bauen, Image scannen und bei Ueberschreitung des Schwellenwerts abbrechen, erst danach in die Registry pushen und weiterverarbeiten.
In GitHub Actions laesst sich das direkt als sequenzieller Schritt nach dem Build abbilden, wobei ein separater Build-Schritt ohne Push notwendig ist, damit das Image lokal fuer den Scan verfuegbar ist, bevor es tatsaechlich in die Registry gelangt. Alternativ kann man mit load: true in docker/build-push-action arbeiten, um das gebaute Image in den lokalen Docker-Daemon des Runners zu laden, ohne es bereits zu pushen.
jobs:
build-and-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build image locally (no push yet)
uses: docker/build-push-action@v6
with:
context: .
load: true
tags: myapp:scan-target
- name: Scan for critical vulnerabilities
uses: aquasecurity/trivy-action@0.28.0
with:
image-ref: myapp:scan-target
severity: CRITICAL
exit-code: '1'
ignore-unfixed: true
- name: Push only if scan passed
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: ghcr.io/myorg/myapp:latest
4. Schwellenwert-Strategien: nicht jede Severity gleich behandeln
Eine Policy, die bei jeder Schwachstelle jeder Severity-Stufe blockiert, einschliesslich LOW und MEDIUM, fuehrt in der Praxis fast immer dazu, dass Teams das Gate umgehen oder deaktivieren, weil kaum ein realistisches Basis-Image, insbesondere mit vielen Betriebssystem-Paketen, vollstaendig frei von LOW- oder MEDIUM-Findings ist. Die uebliche und praktikablere Strategie ist eine gestufte Policy: CRITICAL blockiert den Build sofort und ausnahmslos, HIGH blockiert ebenfalls, aber mit einem definierten Ausnahmeprozess fuer dokumentierte, akzeptierte Risiken, waehrend MEDIUM und LOW lediglich reportet werden, ohne den Build zu stoppen.
Diese Abstufung sollte nicht willkuerlich gewaehlt werden, sondern sich am tatsaechlichen Risikoprofil der Anwendung orientieren. Ein Image, das direkt mit dem Internet exponiert ist, etwa ein oeffentlich erreichbarer Webserver, rechtfertigt eine strengere Policy als ein internes Batch-Verarbeitungssystem ohne Netzwerkzugriff von aussen. Manche Teams fuehren deshalb zwei verschiedene Policy-Profile, ein strenges fuer extern erreichbare Services und ein moderateres fuer rein interne Systeme.
# Gestufte Policy als zwei separate Scan-Schritte
# Schritt 1: CRITICAL und HIGH blockieren den Build hart
trivy image --exit-code 1 --severity CRITICAL,HIGH --ignore-unfixed myapp:latest
# Schritt 2: MEDIUM und LOW nur reportieren, Build laeuft trotzdem weiter
trivy image --exit-code 0 --severity MEDIUM,LOW --format json --output report.json myapp:latest
5. Umgang mit False Positives ohne das Gate auszuhoehlen
Jedes Policy Gate wird frueher oder spaeter mit einem False Positive konfrontiert: einer CVE, die formal auf eine im Image enthaltene Paketversion zutrifft, aber aus konkreten Gruenden nicht ausnutzbar ist, etwa weil die betroffene Funktion im tatsaechlichen Anwendungsfall nie aufgerufen wird oder ein Upstream-Patch bereits inoffiziell zurueckportiert wurde. Der entscheidende Unterschied zu einem schlecht gefuehrten Gate ist, wie mit diesem Fall umgegangen wird: Statt das gesamte Gate global abzuschalten, sollte jede Ausnahme einzeln, nachvollziehbar und mit Ablaufdatum dokumentiert werden.
Trivy und vergleichbare Scanner unterstuetzen dafuer eine .trivyignore-Datei, in der einzelne CVE-IDs mit einer Begruendung als Kommentar aufgefuehrt werden koennen. Wichtig ist, diese Datei regelmaessig zu ueberpruefen, etwa per Kalendererinnerung oder automatisiertem Reminder, denn eine als 'nicht ausnutzbar' markierte CVE kann durch eine spaetere Codeaenderung ploetzlich doch relevant werden, ohne dass die Ignore-Liste automatisch aktualisiert wird. Eine .trivyignore-Datei ohne Ablaufdatum wird in der Praxis fast immer zu einer stetig wachsenden Liste, die niemand mehr hinterfragt.
# .trivyignore
# CVE-2024-12345: Betrifft nur die XML-Parser-Funktion, die in unserem
# Code-Pfad nicht aufgerufen wird. Review-Datum: 2026-11-01, Owner: Team Platform
CVE-2024-12345
# CVE-2023-98765: Upstream-Fix bereits im naechsten Base-Image-Update enthalten,
# geplantes Update: Q4 2026, Owner: Team Platform
CVE-2023-98765
6. Praevention durch Basisimage-Wahl statt reines Nachfiltern
Ein Policy Gate allein loest kein Problem, es verhindert nur, dass ein bereits existierendes Problem produktiv wird. Deutlich effektiver ist eine Praeventionsstrategie, die die Zahl der Findings von vornherein reduziert, allen voran die Wahl eines schlanken Basisimages. Distroless-Images oder Alpine-basierte Varianten enthalten drastisch weniger Betriebssystem-Pakete als ein volles Debian- oder Ubuntu-Image, wodurch es strukturell weniger Angriffsflaeche und weniger potenzielle CVE-Traeger gibt, unabhaengig davon, wie gut das eigentliche Anwendungs-Image gepflegt wird.
Ebenso wichtig ist ein regelmaessiges, automatisiertes Neubauen von Images allein aufgrund eines aktualisierten Basisimages, selbst wenn sich am eigenen Anwendungscode nichts geaendert hat. Viele kritische CVEs entstehen nicht durch eigenen Code, sondern durch Betriebssystem-Pakete im Basisimage, die zum urspruenglichen Build-Zeitpunkt noch unauffaellig waren und erst spaeter als verwundbar bekannt wurden. Ein woechentlicher, geplanter Rebuild-Job, der unabhaengig von Code-Aenderungen laeuft, faengt genau diese Kategorie von Schwachstellen zuverlaessig ab, bevor sie in einem regulaeren Deploy-Zyklus unbemerkt bleiben.
name: Scheduled base image rebuild
on:
schedule:
- cron: '0 3 * * 1' # jeden Montag 03:00 UTC
workflow_dispatch:
jobs:
rebuild-and-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Rebuild with latest base image
run: docker build --pull --no-cache -t myapp:latest .
- name: Scan rebuilt image
uses: aquasecurity/trivy-action@0.28.0
with:
image-ref: myapp:latest
severity: CRITICAL,HIGH
exit-code: '1'
7. Dependency-Scan versus Image-Scan: unterschiedliche Ebenen
Ein haeufiges Missverstaendnis ist, Dependency-Scanning der Anwendungsbibliotheken, etwa per npm audit oder pip-audit, und Image-Scanning des fertigen Containers als redundant zu betrachten und nur eines von beiden einzusetzen. Tatsaechlich decken beide unterschiedliche Ebenen ab: Dependency-Scanning findet Schwachstellen in den direkten und transitiven Abhaengigkeiten der Anwendung, waehrend Image-Scanning zusaetzlich Betriebssystem-Pakete, Systembibliotheken und alles, was im Basisimage mitgebracht wird, erfasst, Bereiche, die reines Dependency-Scanning grundsaetzlich nicht sieht.
Fuer eine vollstaendige Abdeckung gehoeren deshalb beide Scan-Typen in die Pipeline, idealerweise als getrennte Schritte mit jeweils eigenem Schwellenwert, da die typische Menge und Kritikalitaet der Findings zwischen Anwendungsabhaengigkeiten und Betriebssystem-Paketen oft unterschiedlich ausfaellt. Manche Teams setzen fuer Dependency-Scans eine strengere Policy an, weil sie direkten Einfluss auf die gewaehlten Bibliotheken haben, waehrend bei Betriebssystem-Paketen im Basisimage oft nur das Warten auf einen Upstream-Patch bleibt.
8. SBOM als Grundlage fuer schnellere Reaktion auf neue CVEs
Ein Policy Gate prueft immer nur den Stand zum Zeitpunkt des Builds, es kann keine Aussage darueber treffen, welche bereits produktiv laufenden Images von einer neu veroeffentlichten CVE betroffen sind. Genau dafuer ist eine Software Bill of Materials, kurz SBOM, entscheidend: Sie listet praezise jede im Image enthaltene Paketversion auf und ermoeglicht es, bei einer neuen kritischen CVE innerhalb von Minuten statt Tagen zu beantworten, welche produktiven Images tatsaechlich betroffen sind, ohne jedes Image erneut scannen zu muessen.
Trivy kann eine SBOM direkt beim Image-Build im CycloneDX- oder SPDX-Format erzeugen und als Artefakt neben dem eigentlichen Image ablegen, etwa als signiertes Attestation-Objekt in der Registry. Kombiniert mit einem zentralen SBOM-Archiv laesst sich bei jeder neuen CVE-Veroeffentlichung eine einfache Abfrage durchfuehren, ob die betroffene Paketversion in irgendeinem produktiven Image vorkommt, was die Reaktionszeit auf sogenannte Zero-Day-Situationen erheblich verkuerzt gegenueber dem reaktiven Warten auf den naechsten geplanten Scan.
# SBOM im CycloneDX-Format direkt beim Build erzeugen
trivy image --format cyclonedx --output sbom.json myapp:latest
# Spaeter: gezielt pruefen, ob eine bestimmte Paketversion enthalten ist
trivy sbom sbom.json --severity CRITICAL,HIGH
9. Reporting und Nachverfolgung ueber die Pipeline hinaus
Ein Blocking-Gate allein beantwortet nicht die Frage, wie viele blockierte Builds es insgesamt gab, welche CVEs am haeufigsten auftreten und ob sich der allgemeine Sicherheitszustand ueber die Zeit verbessert oder verschlechtert. Deshalb lohnt es sich, Scan-Ergebnisse zusaetzlich zum reinen Pass/Fail-Signal in ein zentrales Dashboard oder System zu exportieren, etwa im SARIF-Format fuer die GitHub Security-Uebersicht oder in ein dediziertes Vulnerability-Management-Tool, unabhaengig davon, ob der Build besteht oder scheitert.
Diese Historie ist besonders wertvoll fuer die bereits erwaehnten dokumentierten Ausnahmen: Ohne zentrale Nachverfolgung verliert ein Team schnell den Ueberblick, welche Ausnahmen aktiv sind, wann sie ablaufen und ob eine als temporaer akzeptierte Schwachstelle laengst haette behoben werden muessen. Eine regelmaessige, etwa monatliche Durchsicht aller aktiven Ausnahmen zusammen mit dem Security-Verantwortlichen verhindert, dass die .trivyignore-Datei zu einem dauerhaften blinden Fleck wird.
| Severity | Empfohlene Aktion | Ausnahmeprozess | Typisches Beispiel |
|---|---|---|---|
| CRITICAL | Build sofort blockieren, keine Ausnahme | Nur mit dokumentiertem, zeitlich begrenztem Ausnahmefall | Remote Code Execution in Kernbibliothek |
| HIGH | Build blockieren | Dokumentierte Ausnahme mit Ablaufdatum moeglich | Privilege Escalation ohne bekannten Exploit |
| MEDIUM | Nur reportieren, Build laeuft weiter | Keine noetig, wird im Dashboard verfolgt | Denial-of-Service unter speziellen Bedingungen |
| LOW | Nur reportieren, Build laeuft weiter | Keine noetig | Informationsleck ohne direkten Schaden |
Mironsoft
Container-Infrastruktur, CI-Pipelines und Deployment-Automatisierung
Docker-Setups, die im Team und in Produktion tragfähig bleiben?
Wir prüfen bestehende Dockerfiles und Compose-Stacks auf Sicherheitslücken, aufgeblähte Images und fragile Build-Pipelines und bauen daraus eine Container-Infrastruktur, die schnell baut, sicher läuft und im Team nachvollziehbar bleibt.
Dockerfile-Review
Multi-Stage-Builds, Layer-Caching und Image-Größe systematisch optimieren.
Security-Audit
Container-Isolation, Secrets-Handling und Image-Scanning gegen echte Angriffsflächen absichern.
CI/CD-Integration
Build-Pipelines, Registries und Deployment-Strategien für reproduzierbare Releases aufbauen.
10. Zusammenfassung
Vulnerability Policy Gates: Das Wichtigste auf einen Blick
Report vs. Gate
Nur ein Exit-Code ungleich null bei Schwellenwertueberschreitung stoppt die Pipeline wirklich.
Gestufte Policy
CRITICAL und HIGH blockieren, MEDIUM und LOW werden nur dokumentiert.
False Positives
Einzelne, begruendete Ausnahmen mit Ablaufdatum statt das gesamte Gate abzuschalten.
Praevention
Schlanke Basisimages und geplante Rebuilds reduzieren Findings, bevor das Gate greifen muss.