18. Dezember 2025

Image Migration mit Chainguard

Letzten Oktober haben wir bereites einen Einblick in unsere Chainguard Partnerschaft gegeben. Heute folgt ein weiterer Teil dieser Blogserie, in der wir euch den Einsatz von Chainguard Images bei Puzzle näherbringen möchten. Dazu haben wir zwei interne Applikationen ausgewählt, welche wir als Proof-Of-Concept auf Chainguard Container Images migrieren wollen. Wir wollen herausfinden, wie gross im Schnitt der Aufwand für eine Migration nach Chainguard ist und was allfällige Hürden sein könnten.

Platform Engineering
Security & Compliance
IT-Security
Visual Partnerschaft Chainguard

Folgende zwei Applikationen wurden für die Migration auf Chainguard Container Images ausgewählt:

  • Dependency-Track
    Dependency-Track ist eine Continuous SBOM Analyse Plattform. Sie wird innerhalb von Puzzle als zentrale Stelle zur Sammlung und Auswertung von SBOMs benutzt. Aus Architektursicht besteht Dependency-Track aus drei Komponenten: dem Frontend, Backend und der Postgres Datenbank. Wobei die Postgres Datenbank Out-Of-Scope ist und weiter so betrieben wird. Das Frontend basiert auf einem Vue.js Stack, welcher über einen Nginx ausgeliefert wird. Als Base Image wird aktuell das nginxinc/nginx-unprivileged:1.29.2-alpine verwendet. Der Code für das Backend wurde in Java geschrieben. Die Basis des offiziellen Backend Containers ist ein Debian 13 Linux und als JRE wird das OpenJDK Temurin 21 verwendet.
  • Jenkins
    Jenkins ist ein Build Server, der in vielen IT-Umgebungen nicht mehr wegzudenken ist. Jenkins wurde ausgewählt, weil dieser allgemein als unser Problemkind gilt. Schwer zu maintainen und sicherheitstechnisch löchriger als Emmentaler Käse. Darüber hinaus gelten CI/Build Server als sogenannte High Privileged Environments. Diese zeichnen sich dadurch aus, dass sie Secrets besitzen, die weitreichenden Zugriff auf andere Systeme erlauben (Source Code Repositories, Deployment Targets etc.). Zudem benutzen wir für unser Jenkins Deployment ein Custom Image, in welchem wir noch diverse eigene Tools und Plugins installieren.

Die Migration

Dependency-Track

Bei der Dependency-Track-Migration gingen wir grundsätzlich davon aus, dass ein simples Drop-In-Replacement möglich ist. Beim Prüfen der Images im Chainguard Catalog mussten wir jedoch schnell feststellen, dass Chainguard nur ein Image für Dependency-Track anbietet. Es gibt nicht wie bei Dependency-Track ein Image für das Frontend und eines für das Backend. Nach anfänglicher Verwirrung konnte mit einem Blick in das Chainguard Image schnell festgestellt werden, dass sie nur das Backend Image anbieten. Hier wäre es wünschenswert, wenn Chainguard das Naming der bestehenden Images übernehmen würde, damit es zu weniger Verwirrung führen würde. Das Frontend Image betreiben wir weiterhin mit dem offiziellen Image.

deptrack:
  apiServer:
    image:
      pullPolicy: IfNotPresent
-     repository: docker.io/dependencytrack/apiserver
-     tag: "4.13.5"
+     repository: chainguard/puzzle-partner.com/dependency-track
+     tag: "v4.13.5"

Nachdem das neue Container Repository gesetzt worden war, konnten wir Dependency Track neu deployen und die Applikation lief anschliessend ohne Probleme mit dem neuen Image weiter. Der erste Versuch hat gezeigt, dass ein simples Drop-In-Replacement innert weniger Minuten möglich ist. Die Frage, was wir nun damit erreicht haben, haben wir damit aber noch nicht beantwortet.

Dazu schauen wir uns einmal die Vulnerability Scans von Trivy genauer an.

docker.io/dependencytrack/apiserver:4.13.5

Total 64 Issues, davon 1 Critical und 2 High

chainguard/puzzle-partner.com/dependency-track:v4.13.5

Total 0 Issues

Auf den ersten Blick ist dies eine enorme Verbesserung zur Verkleinerung der Angriffsfläche. Jedoch sind solche Vulnerability Scans immer ein wenig mit Vorsicht zu geniessen. Erstens bedeutet es nicht, dass, wenn nichts gefunden wurde, auch keine Vulnerabilities vorhanden sind, und zweitens bedeutet das Vorhandensein von Vulnerabilities nicht automatisch, dass das System angreifbar ist und in der Folge die Vulnerabilities überhaupt relevant sind.

Als Beispiel können wir hier das höchstbewertete CVE, welches im API Server gefunden wurde, genauer betrachten. Dabei handelt es sich um cve-2025-58050, einen Buffer Overflow in einer Regex-Parsing-Library. Bereits das Scoring des CVE reicht von 6.5 Moderate (RedHat/V3) bis 9.1 Critical (Aqua Security). Darüber hinaus ist es ohne Kenntnisse des Applikationscodes kaum zu sagen, ob Dependency-Track jemals Gebrauch von dieser C Library macht, und ob diese Funktion mit einem malicious Regex Pattern überhaupt aufgerufen wird.

Jenkins

Der nächste Test fand mit den Jenkins Images statt. Wie bereits am Anfang erwähnt, bauen wir eigene Images, welche im Vergleich zur Basisversion noch mit zusätzlichen Tools und Plugins ausgestattet sind. Bei diesem Replacement hatten wir im Vorfeld mit diversen Schwierigkeiten gerechnet, wie wir es von anderen gehärteten Images kennen. Zum Beispiel: Stripped Images mit fehlenden Tools oder Read-Only Dateisysteme und anderen Berechtigungsproblemen.

Jenkins Controller

Für unseren Jenkins Controller mussten wir nur das Base Image im Dockerfile ersetzen. Anschliessend konnten wir unser Custom Image erfolgreich bauen und deployen.

# install jmt tool
- FROM docker.io/jenkins/jenkins:2.528.1 AS builder
+ FROM chainguard/puzzle-partner.com/jenkins:v2.528.1 AS builder

...

docker.io/jenkins/jenkins:2.528.1

Total 163 Issues, davon 8 High

chainguard/puzzle-partner.com/jenkins:v2.528.1

Total 0 Issues

Jenkins Inbound Agent

Als Nächstens wollten wir unser Custom Jenkins Inbound Agent Image auf das Chainguard Image migrieren.
Auch für den Inbound Agent werden weitere Packages über den Debian Package Manager installiert.

Hier das originale Dockerfile:

FROM docker.io/jenkins/inbound-agent:3341.v0766d82b_dec0-1-jdk21

USER root

# No interactive frontend during docker build
ENV DEBIAN_FRONTEND=noninteractive \
    DEBCONF_NONINTERACTIVE_SEEN=true

#========================
# Miscellaneous packages
# Includes minimal runtime used for executing non GUI Java programs
#========================
RUN apt-get -qqy update \
    && apt-get -qqy --no-install-recommends install \
      ca-certificates \
      tzdata \
      wget \
      curl \
      gnupg2 \
      bc \
      procps \
      libx11-xcb1 \
      s3cmd \
      unzip \
      autoconf \
      automake \
      patch \
      bison \
      bzip2 \
      g++ \
      gcc \
      make \
      libffi-dev \
      libtool \
      libreadline-dev \
      libsqlite3-dev \
      zlib1g-dev \
      libc6-dev \
      libc6 \
      libyaml-dev \
      openssl \
      graphviz \
      subversion \
    && rm -rf /var/lib/apt/lists/* /var/cache/apt/* \
    && cd /bin && mv sh sh.old && ln -s bash sh

#===================
# Timezone settings
# Possible alternative: https://github.com/docker/docker/issues/3359#issuecomment-32150214
#===================
ENV TZ "Europe/Zurich"
RUN echo "${TZ}" > /etc/timezone \
    && dpkg-reconfigure --frontend noninteractive tzdata

RUN chgrp -R 0 /home/jenkins \
    && chmod -R g+w /home/jenkins \
    && chmod -R ug+X /home/jenkins

Ein Drop In Replacement des Base Images funktioniert aus folgenden Gründen nicht:

  • Chainguard verwendet als Base Image Wolfi – im Gegensatz zu dem offiziellen Jenkins Image, welches auf Debian Linux basiert
  • Als Package Manager kommt apk anstelle von apt zum Einsatz

Zum Glück bietet Chainguard eine Anleitung zum Migrieren von Dockerfiles an. Ausserdem auch ein Tool, welches bestehende Dockerfiles zu Chaingaurd kompatiblen Dockerfiles konvertiert. Somit muss man sich nicht selbst um die Anpassungen der FROM Tags und der Namen der Linux Packages kümmern.
An dieser Stelle haben wir uns auch noch gleich die Zeit genommen und die nicht mehr explizit verwendeten Packages wie gcc oder g++ entfernt.

Hier das migrierte Dockerfile:

FROM chainguard/puzzle-partner.com/jenkins-inbound-agent:2.528-dev

USER root

#========================
# Miscellaneous packages
# Includes minimal runtime used for executing non GUI Java programs
#========================
RUN apk add --no-cache bc \
    bzip2 \
    ca-certificates \
    curl \
    gnupg \
    graphviz \
    make \
    openssl \
    patch \
    procps \
    tzdata \
    unzip \
    wget

#===================
# Timezone settings
# Possible alternative: https://github.com/docker/docker/issues/3359#issuecomment-32150214
#===================
ENV TZ "Europe/Zurich"
RUN echo "${TZ}" > /etc/timezone

RUN chgrp -R 0 /home/jenkins \
    && chmod -R g+w /home/jenkins \
    && chmod -R ug+X /home/jenkins

USER 65534

pitc-cicd/jekins-inbound-agent:latest

Total 1341 Issues, davon 4 Critical 147 High

pitc-cicd/jekins-inbound-agent:chainguard

Total 0 Issues

Danach haben wir das neue Inbound Agent Image gebaut und in unsere Test Jenkins Instanz deployt. An dieser Stelle waren wir sehr gespannt, ob nun unsere Test Pipelines direkt in einen Fehler laufen infolge von Berechtigungsproblemen. Zu unserem Erstaunen liefen alle Tests der Pipeline erfolgreich durch. Auch wenn diese Unit-Test Pipelines nur einen kleinen Teil der benötigten Tools abdecken, sind wir doch auch hier sehr zuversichtlich, dass für die restlichen Projekte, welche Jenkins als Build Server benutzen, keine weiteren Probleme auftreten.

Hier sehen wir ebenfalls eine Reduktion der CVEs auf Null. Im Gegensatz zu Dependency-Track müssen wir die CVEs in einem anderen Kontext betrachten. Jenkins ist ein Build Server, welcher per Design die Ausführung von Code erlaubt. Dies macht das System umso anfälliger, da die Kontrolle, was und mit welchen Berechtigungen auf dem System ausgeführt wird, sehr schwer ist.

1341 vs 0

Ist ein Container Image mit 1341 CVEs jetzt unsicherer als eines mit 0 CVEs? Wahrscheinlich ja, aber es kommt immer auf den Kontext an. Macht die Applikation überhaupt Gebrauch von dieser anfälligen Library oder Tools?
Diese Fragen zu beantworten, ist nicht einfach und ein zeit- sowie kostenintensiver Prozess. Allein für Jenkins wurden in den letzten 10 Jahren rund 700 CVEs gemeldet. Und Jenkins stellt nur eine von vielen Applikationen dar, welche wir intern betreiben. Die Chainguard Images sind daher eine sehr grosse Hilfe, die Angriffsfläche eines Images zu reduzieren, und damit die Gewissheit zu haben, keine Container Images mit aktuell bekannten CVEs zu betreiben. Auch die einfache Migration von bestehenden Container Images bestärkt uns, weiterhin diesen Weg zu gehen.

 

Roman Bichsel

Möchtest du auch von Chainguard Images profitieren?

Wir sind bereit, dein Projekt voranzutreiben. Lass uns gemeinsam deine Ziele erreichen und dein Unternehmen weiterbringen.
Roman Bichsel Profilbild
Roman Bichsel
Inside Sales