Zum Hauptinhalt springen
GitHub DevSecOps Teil 6: Wie man Container Scanning einsetzt
  1. Blogs/

GitHub DevSecOps Teil 6: Wie man Container Scanning einsetzt

Autor
Romano Roth
Ich bin überzeugt: Der nächste Wettbewerbsvorteil ist nicht AI selbst, sondern die Organisation drumherum. Als Chief AI Officer bei Zühlke arbeite ich mit C-Level-Führungskräften daran, Unternehmen zu bauen, die wahrnehmen, entscheiden und sich kontinuierlich anpassen. Seit über 20 Jahren mache ich diese Überzeugung zur Praxis.
Frag die KI über diesen Artikel

Wir haben die GitHub-Actions-Pipeline in fünf Sessions aufgebaut: Projekt-Grundlagen, Software Composition Analysis, License Compliance und Static Application Security Testing. Die nächste Schicht ist Container Scanning — die Suche nach Schwachstellen im Docker-Image, das wir ausliefern, nicht nur im Source, den wir geschrieben haben. In Teil 6 unserer Serie teilen Patrick Steger und ich die Arbeit in zwei GitHub-Actions-Sub-Workflows auf: einer baut das Image und pusht es in die Registry, der andere zieht es zurück und lässt Trivy darauf laufen.

Was Container Image Scanning abdeckt
#

Container Image Scanning sucht nach Schwachstellen in allem, was im Docker-Image landet — sowohl in den Operating-System-Komponenten als auch in den Application-Libraries, die du mitbringst. Diese Überlappung mit SCA ist Absicht, und Patrick spricht sie früh an: Die meisten Container-Scanning-Findings hat Software Composition Analysis bei den Source-Dependencies bereits gemeldet. Du kannst diese Überschneidung wegkonfigurieren, wenn sie dich stört — wir lassen es im Default und nehmen die Duplikate zugunsten der Vollständigkeit in Kauf.

Zwei Sub-Workflows: Build und Scan
#

Das Muster, das wir in dieser Serie verwenden, hält die Main-Pipeline schlank und verlagert die eigentliche Arbeit in separate Workflow-Files. Für Container Scanning heisst das zwei neue Sub-Jobs:

  1. Ein Build-Docker-Image-Job, der das Image erzeugt und in der GitHub Container Registry ablegt. Er gibt den Image-Tag als Output aus.
  2. Ein Container-Image-Scan-Job, der diesen Tag als Input nimmt, das Image pullt, mit Trivy scannt und die Findings in den GitHub Security Tab schiebt.

In der Main-Pipeline hängt der Docker-Job vom build-Job ab (wir brauchen das kompilierte Application-Jar im Image). Der Scan-Job deklariert den Docker-Job als Voraussetzung und liest den Image-Tag aus dessen Outputs. Beide Jobs beginnen mit “D”, damit sie in der GitHub-Actions-UI sauber unten sortiert werden.

Der Build-Docker-Image-Job
#

Der Build-Job lebt in docker.yml. Er kann manuell ausgelöst oder vom Main-Workflow aufgerufen werden. Wir deklarieren auf Workflow- und Job-Ebene einen outputs:-Block, der den Image-Tag für nachgelagerte Konsumenten verfügbar macht. Auch die Container-Registry und der Image-Name werden als Inputs deklariert.

Die Steps sind unspektakulär: Source auschecken, damit wir das Dockerfile haben, die vorgebaute Application-Binary aus dem vorherigen Build-Step herunterladen, mit den von GitHub bereitgestellten Credentials in der Container-Registry einloggen, Docker-Metadaten (Tags, Labels) extrahieren und dann mit der docker/build-push-action das Image bauen und publishen. Der Tag, unter dem wir publishen, ist derselbe, den wir als Output des Jobs nach aussen geben.

Der Container-Scan-Job
#

Der Scan-Job liegt ebenfalls in einem eigenen File. Er kann manuell gestartet werden, wenn du den Image-Tag mitlieferst — oder von einer anderen Pipeline aufgerufen werden, was in unserem Fall die Main-Pipeline tut, indem sie den Tag des Build-Jobs übergibt. Die Steps loggen sich in die Container-Registry ein, holen das zuvor erstellte Image per docker pull und lassen Trivy darauf laufen. Die wichtigste Option ist das Output-Format: Wir setzen es auf SARIF, damit GitHub das Format nativ verarbeiten kann. Im letzten Step laden wir das SARIF-File mit der Standard-Action github/codeql-action/upload-sarif hoch — und damit erscheinen die Findings im Security Tab.

Findings in GitHub ansehen
#

Nach dem Run gehen wir in den Security Tab → Code scanning und filtern nach Tool — Trivy. Viele der Findings stecken in app.jar. Das sind die Dependencies unserer Anwendung, also genau jene, die SCA in einer früheren Session bereits gemeldet hat. Es gibt aber auch wirklich neue Findings aus dem OS-Layer — Patrick zeigt als Beispiel einen Fehler in einer SSL-Library. Container Scanning hat rund hundert neue Einträge zusätzlich zu dem aufgemacht, was wir vorher schon hatten.

Was tun mit so vielen Findings?
#

Patricks Empfehlung ist eindeutig: Default-Aktion ist Fixen, und “Fixen” heisst fast immer Library-Update. Eine aktualisierte verwundbare Dependency entfernt das Finding aus Trivy und aus dem SCA-Scanner in einem Rutsch. Wenn dich die Duplikate zwischen SCA und Container-Scan stören, kannst du app.jar aus Trivy ausschliessen — aber das ist Kosmetik, keine Security.

Wenn ein Update nicht möglich ist, beginnt die eigentliche Arbeit. Du musst beurteilen, ob die Schwachstelle tatsächlich Auswirkung auf deine Anwendung hat, das Restrisiko bewerten und dann über einen Workaround, eine bewusste Risiko-Akzeptanz oder — bei hohem Risiko — sogar das Abschalten der Anwendung entscheiden. Patrick wiederholt es zur Betonung: Default-Aktion ist Remediieren. Wie man Vulnerabilities in GitHub managt und triagiert, zeigen wir in einer späteren Session.

Key Takeaways
#

  1. Das Image scannen, nicht nur den Source. SAST und SCA prüfen, was du geschrieben hast und worauf du dich verlässt. Container Scanning prüft den OS-Layer und alles, was im Image gelandet ist. Ohne diesen Schritt lieferst du Schwachstellen aus, die du nie getestet hast.

  2. Build und Scan in zwei Workflows aufteilen. Ein Job baut und publisht das Image und gibt den Tag aus. Der andere konsumiert den Tag und scannt. Saubere Inputs und Outputs machen die Abhängigkeit explizit und die Files wiederverwendbar.

  3. SARIF ist die entscheidende Integration. Trivy auf SARIF-Output konfigurieren und mit upload-sarif hochladen. Diese eine Entscheidung bringt die Findings in den Security Tab — also dorthin, wo Entwickler sie tatsächlich sehen.

  4. Mit Überschneidungen zur SCA rechnen. Die meisten Container-Findings stehen schon in deinem SCA-Report. Das ist normal. Wähle einen als Quelle der Wahrheit oder schliesse app.jar aus Trivy aus — aber gerate wegen der Duplikate nicht in Panik.

  5. Default-Aktion: Library updaten. Ein Versions-Bump erledigt Findings in Trivy und SCA gleichzeitig. Workarounds und Risiko-Akzeptanz sind Notlösungen, nicht der erste Schritt.

  6. Triage ist Prozess, nicht Tooling. Wenn du nicht updaten kannst, brauchst du Impact-Analyse, Risikobewertung und eine explizite Entscheidung. Tooling macht das Problem sichtbar; Menschen entscheiden, was zu tun ist. Diesen Workflow bewusst gestalten — nicht über Ad-hoc-Slack-Threads.