# GitLab DevSecOps Teil 6: Wie man Container Scanning einsetzt

> Teil 6 der GitLab DevSecOps Serie. Patrick Steger und ich ergänzen die GitLab-Pipeline um Container Scanning — wir bauen ein Docker-Image aus dem kompilierten Jar, pushen es in die Registry und scannen es mit Trivy und Grype auf Schwachstellen in OS-Paketen und Application-Libraries.

- Date: 2022-09-06
- Source: https://romanoroth.com/de/blogs/gitlab-devsecops-container-scanning/
- Tags: DevSecOps, GitLab, CI/CD, DevOps, Container Scanning, Docker, Containers, Trivy, Shift Left


Wir haben SAST, Secret Detection und Software Composition Analysis bereits in die GitLab-Pipeline integriert. Diese Checks decken Quellcode und Dependencies ab — aber das Artefakt, das wir tatsächlich ausliefern, ist ein Container-Image. Betriebssystem-Pakete, das Base-Image und alles, was im Lauf des Builds hineinkopiert wird, können eigene Schwachstellen mitbringen. In Teil 6 unserer Serie ergänzen Patrick Steger und ich die Pipeline um Container Scanning, bauen ein Docker-Image aus dem zuvor kompilierten Jar und schicken es durch Trivy und Grype.

<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
			<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube-nocookie.com/embed/1AUKQ32K6D4?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
		</div>


## Was Container Scanning macht

Container Scanning sucht nach bekannten Schwachstellen innerhalb deines Container-Images. Es scannt jede Datei im Image — OS-Pakete, Libraries, die Application-Binaries — gegen Vulnerability-Datenbanken. GitLab nutzt unter der Haube zwei Open-Source-Tools: Trivy und Grype. Das Feature ist Teil von GitLab Ultimate und funktioniert nur für Linux-Images; Windows Container werden bisher nicht unterstützt.

Ein Hinweis, den Patrick früh anbringt: Container Scanning überschneidet sich mit den SCA-Ergebnissen, die wir bereits erzeugt haben. Mit Ultimate kann GitLab diese Duplikate für dich ausfiltern. Ohne Ultimate wirst du dieselbe Library in beiden Reports auftauchen sehen.

## Den Job aktivieren

Wie in den vorherigen Stages ist der Scanning-Job selbst eine einzige Include-Zeile. Wir holen `Container-Scanning.gitlab-ci.yml` aus den GitLab-Templates und ergänzen es in der bestehenden Liste der Imports. Der Haken diesmal: Der Job braucht ein Image, das er scannen kann — und das haben wir noch nicht.

## Erst das Image bauen

Bevor Container Scanning laufen kann, müssen wir einen Container produzieren. Wir starten mit dem Dockerfile aus dem Projekt-Template, ändern es aber. Die Originalversion führte `mvn package` innerhalb des Images aus und baute die ganze Anwendung von Grund auf neu. Das ist Verschwendung — wir haben das Jar bereits im `build`-Job der Pipeline kompiliert. Also entfernen wir den Maven-Schritt und verwenden `COPY target`, um das vorgebaute Jar ins Image zu ziehen. Der letzte Eintrag im Dockerfile startet dieses Jar. Als Base-Image dient JDK 13.

Dann erweitern wir die Pipeline um einen `build_image`-Job. Wir definieren eine Variable `CONTAINER_TEST_IMAGE` für den Image-Namen in der GitLab-Registry, loggen uns bei Docker ein, bauen das Image und pushen es. Weil wir das kompilierte Jar brauchen, ergänzen wir am Ende ein `needs:`-Statement, das `build_image` vom vorherigen `build`-Job abhängig macht. Ohne diese Abhängigkeit hätte die `COPY target`-Zeile nichts zum Kopieren.

Um Docker-Befehle innerhalb eines GitLab-Jobs auszuführen, brauchen wir Docker selbst. Wir hängen `docker:dind` (Docker-in-Docker) als Service in den `services:`-Block des `build_image`-Jobs. Damit steht das Docker-Tooling im Runner zur Verfügung.

Schliesslich richten wir den Container-Scanning-Job auf das gerade gebaute Image. Das Template stellt eine vordefinierte Variable namens `DOCKER_IMAGE` bereit — wir setzen sie im globalen Variables-Block auf `CONTAINER_TEST_IMAGE`. Damit weiss der Scanner genau, welches Image er aus der Registry ziehen und analysieren soll.

## Ganz schön viele Images

An dieser Stelle lohnt es sich, kurz die Docker-Images zu zählen. Jeder Build-Job läuft in seinem eigenen Image. Jetzt haben wir zusätzlich ein neues Image gebaut, das unsere Anwendung enthält. Und genau dieses neue Application-Image ist es, das Container Scanning analysiert. Drei verschiedene Rollen für "Docker-Image" — und die Pipeline hat sie alle.

## Findings — und eine grüne Pipeline

Die Pipeline läuft. Der Container-Scanning-Job wird grün. Wir öffnen ihn und finden Schwachstellen. Warum ist die Pipeline nicht rot?

GitLab betrachtet einen Job als erfolgreich, wenn er seine Aufgabe abgeschlossen hat — in diesem Fall: "Ich habe das Image gescannt und einen Report erzeugt." Findings allein lassen die Pipeline nicht scheitern. Aus reiner Security-Sicht würde Patrick einen harten Fail bei jedem Finding bevorzugen, in der Praxis hast du aber fast immer mindestens eine CVE irgendwo, und eine permanent rote Pipeline ist eine Pipeline, der niemand mehr traut. Ein vernünftiger Mittelweg wäre, nur bei High oder Critical zu failen, aber GitLab bietet diesen Schalter aktuell nicht out of the box. Die Remediation, auf die Patrick verweist, sind Merge-Request-Approval-Regeln — wir können verlangen, dass bestimmte Reviewer freigeben, wenn eine Änderung neue Schwachstellen einführt. Das schauen wir uns in einer späteren Session genauer an.

Die Findings selbst kannst du an zwei Orten ansehen. Erstens im **Security Tab des Pipeline-Runs**, gefiltert nach Tool — wähle "Container Scanning". Zweitens unter **Security & Compliance → Vulnerability Report** im Governance-Bereich des Projekts, ebenfalls filterbar nach Tool.

## Key Takeaways

1. **Container Scanning prüft das Artefakt, das du wirklich ausspielst.** SAST und SCA decken Source und Dependencies ab. Container Scanning deckt das Image ab: OS-Pakete, Base-Image, kopierte Binaries. Wer das weglässt, liefert blind aus.

2. **Den Build wiederverwenden, nicht im Dockerfile neu bauen.** `mvn package` aus dem Dockerfile entfernen und das von der Pipeline bereits erzeugte Jar mit `COPY target` reinziehen. Alles andere dupliziert Arbeit und zerfasert das Artefakt, das du eigentlich getestet hast.

3. **Abhängigkeiten explizit über `needs:` verdrahten.** Container Scanning braucht ein Image. Der Image-Build-Job braucht das kompilierte Jar. Schreib diese Abhängigkeiten ins YAML, sonst überrascht dich die Reihenfolge.

4. **Docker-in-Docker ist der Preis fürs Image-Bauen in CI.** `docker:dind` als Service am `build_image`-Job ergänzen. Ohne diesen Service hat die Docker-CLI im Runner nichts, mit dem sie reden kann.

5. **Eine grüne Pipeline mit Vulnerabilities ist Absicht.** GitLab markiert den Job als erfolgreich, wenn der Scan durchgelaufen ist. Severity-basierter Pipeline-Fail ist nicht eingebaut. Nutze Merge-Request-Approval-Regeln, damit keine neuen Findings unbemerkt durchrutschen.

6. **Mit Überschneidungen zur SCA rechnen — und einen Plan dafür haben.** Container Scanning entdeckt Application-Library-CVEs erneut, die SCA bereits gemeldet hat. Ultimate filtert die Duplikate; ohne Ultimate vorab klären, welcher Report welches Finding besitzt — sonst diskutiert das Team an zwei Stellen gleichzeitig.

