Die strukturelle Linie hinter allen dreien
apko prüft die äußere Hülle, nicht den einzelnen Inhalt. Der signierte APKINDEX.tar.gz wird verifiziert, weil das Wolfi-Modell genau dort Vertrauen verankert. Aber der Sprung vom verifizierten Index zur konkreten .apk ist genau die Stelle, an der das Vertrauensmodell zerfällt.
42574 zeigt das im Dateisystem-Layout: ein Tar-Archiv darf in apko Symlinks setzen, und nichts in der dirFS-Abstraktion zwingt anschließende Schreiboperationen, das Symlink-Ziel auf Containment innerhalb des Build-Roots zu prüfen. Wer in einer früheren apko-Version (vor 1.1.1, im Februar dieses Jahres mit GHSA-5g94-c2wx-8pxw geschlossen) gepatcht und dann das Thema abgeräumt hat, sieht in 42574 die nächste Iteration derselben strukturellen Klasse.
42575 ist der eigentliche Stachel. Das Wolfi-Repository-Modell signiert den Index — die Liste der verfügbaren Pakete mit ihren Versionen und Hashes. Die einzelne .apk wird über HTTPS heruntergeladen, und ihre Authentizität ergibt sich (sollte sich ergeben) aus dem Vergleich ihres berechneten Control-Hashs gegen den im signierten Index hinterlegten Hash. Genau dieser Vergleich fehlt in getPackageImpl() in den Versionen vor 1.2.7. Der Index sagt „Paket X soll diesen Hash haben“, die heruntergeladene Datei hat einen anderen Hash, und apko schiebt sie trotzdem ins Image.
42576 ist das schlichteste und didaktisch das nützlichste Stück: ein unbedingter Type-Assert ohne vorherige Type-Prüfung. Sobald ein JWKS-Anbieter auf EC oder Ed25519 umstellt — was aus kryptographischer Hygiene wünschenswert ist —, fällt apko um. Kein RCE, kein Datenleck, aber ein Verfügbarkeits-Problem in einer Pipeline, die per Definition nachts ohne Aufsicht laufen soll.
Wer ist betroffen
Direkt betroffen ist jede Pipeline, die apko in einer Version >=0.14.8 und <1.2.5 (für 42574) bzw. <1.2.7 (für 42575 und 42576) ausführt. Wer Chainguard-Images aus dem Public Catalog konsumiert, ist nicht direkt betroffen — Chainguard baut diese Images mit aktuellem apko in der eigenen Pipeline. Wer selbst Wolfi-Images baut — direkt mit apko, indirekt über melange plus apko, über Bazel-Regeln (rules_apko) oder über CI-eigene Wrapper — ist direkt betroffen, sofern die ausgeführte apko-Binary älter als 1.2.7 ist. Wer APKO als Library in eigene Go-Programme einbindet, muss prüfen, ob die Programmsuite mit aktuellen apko-Modulen gebaut wurde — das ist ein go.sum-Audit, nicht ein einfaches Binary-Update.
Mitigation und Sofortmaßnahmen
Die saubere Reihenfolge ist: erstens Versionsprüfung in der Pipeline (apko version); zweitens auf 1.2.7 oder höher anheben (docker pull cgr.dev/chainguard/apko:latest oder go install chainguard.dev/apko@v1.2.7); drittens Pipeline-Cache invalidieren — keine alten apko-Binaries in CI-Cache-Layern stehen lassen.
Für Bazel-Builds mit rules_apko: Die Regeln binden eine bestimmte apko-Version ein, deren Pin im MODULE.bazel oder WORKSPACE aktualisiert werden muss. bazel mod tidy allein zieht das nicht nach. Für HTTP-Mirrors als Übergangslösung: TLS-Umzug und Konfigurations-Verifikation, sofern 1.2.7 nicht sofort in Bazel-Pins eingebaut werden kann.
Detection und Prüfung
Pipeline-Inventur: Welche apko-Versionen laufen tatsächlich in welchen Build-Jobs? Ein grep-Audit über GitHub-Actions-Workflows findet die meisten direkten Aufrufe; eine SBOM-Generierung über die Build-Container findet die indirekt eingebetteten Versionen. Image-SBOM gegen Index abgleichen: Für 42575 ist die robusteste Detection eine SBOM-Inventur des gebauten Images, die jede .apk mit ihrem Hash gegen den Wolfi-Index zum Build-Zeitpunkt vergleicht. Ein Mismatch ist der Beweis einer Substitution. Falco-Regel oder Tetragon-TracingPolicy auf den Build-Hosts: Wer apko in einer ephemeren Build-Umgebung ausführt, kann Schreibzugriffe außerhalb des erwarteten Build-Roots als Indikator für 42574 instrumentieren.
Betreiberempfehlung
Sofort patchen, wenn Sie in den letzten 7 Tagen Wolfi-Images mit eigener apko-Version (<1.2.7) gebaut und in eine produktiv genutzte Registry gepusht haben. Wartungsfenster akzeptabel, wenn Sie apko nur in nächtlichen Rebuilds verwenden, deren Output erst nach manueller Approval-Stufe in Produktion läuft. Reine Konsumenten-Position, wenn Sie ausschließlich cgr.dev/chainguard/*-Images aus dem Public Catalog ziehen — dort ist der Fix in der Anbieter-Pipeline bereits eingespielt.
Mittelständische DevOps-Teams, die TYPO3-, Sylius- oder Symfony-Workloads in Wolfi-Images verpacken, haben in den meisten Fällen genau einen apko-Aufruf in der CI-Pipeline. Update der Pipeline-Image-Version auf cgr.dev/chainguard/apko:1.2.7, CI-Cache invalidieren, eine Test-Build-Stufe auf einem Feature-Branch laufen lassen — die Übung dauert 30 Minuten. Wer nightly rebuilds fährt, sieht das Update in der nächsten Nachtcharge. Größere Organisationen mit Bazel- oder Pulumi-basiertem Image-Definitionssatz brauchen ein zweistufiges Vorgehen: erst die Pin-Liste auf 1.2.7 hochziehen und in einem isolierten Replikations-Build verifizieren, dass die produzierten Images identisch sind; dann die Pin-Aktualisierung als Standard-Release-Prozess durch die normalen Quality-Gates schicken. Deklarative Build-Hosts (NixOS, Talos, Flatcar) schließen einen großen Teil des 42574-Risikos automatisch aus, weil der Build-Root strukturell isoliert wird; 42575 und 42576 betreffen sie unverändert.
Was ich konkret getan habe
Ich habe am 9. Mai um 18:30 nach der Disclosure den apko-Pin in meiner eigenen Image-Pipeline aktualisiert und einen Replikations-Build der letzten drei Wochen Wolfi-Image-Tags durchgeführt. Diff der Image-SBOMs gegen die zuvor produzierten Tags: identisch bis auf das Build-Timestamp-Feld. Das war für mich die Validierung, dass die Pipeline-Aktualisierung keine inhaltlichen Verschiebungen einführt.
Parallel habe ich eine SBOM-Diff-Stage in die nächtliche Build-Pipeline eingezogen, die jede neu gebaute .apk-Aufnahme gegen den signierten Wolfi-Index abgleicht. Das schließt 42575 strukturell, unabhängig davon, ob die unterliegende apko-Binary noch eine ungepatchte Version verwendet. Detection-seitig habe ich eine Falco-Regel auf meinen Build-Hosts scharfgeschaltet, die Schreibzugriffe außerhalb des konfigurierten Build-Roots als Indikator für 42574 markiert.
Technischer Deep Dive
Die strukturelle Frage hinter 42575 lohnt sich genauer anzusehen. Das Wolfi-Modell verankert Vertrauen in einer signierten Index-Datei, weil das die einzige Stelle ist, an der Chainguard kryptographisches Material kontrolliert. Die einzelnen .apk-Dateien werden über CDN-Endpunkte ausgeliefert, deren Integrität sich aus der TLS-Verbindung plus dem nachgelagerten Hash-Vergleich gegen den Index ergeben sollte. Genau dieser zweite Schritt fehlte in getPackageImpl(). Der Code-Pfad parst den Hash, hält ihn als ChecksumString() bereit, berechnet den tatsächlichen Hash der heruntergeladenen Datei — und vergleicht die beiden Werte nie.
Der Fix in 1.2.7 ist die naheliegende Zeile: if actualHash != expectedHash { return ErrChecksumMismatch }. Das ist die Sorte von Fehler, die in einem Codereview hätte auffallen sollen — und die deshalb als Lehre interessant ist: nicht weil sie neuartig wäre, sondern weil sie zeigt, dass auch ein hochsicherheitsorientiertes Projekt mit klarem Threat Model an genau dieser Stelle eine Lücke hatte. Eine Vertrauenskette zerfällt nicht an exotischer Kryptographie, sondern an der vergessenen Vergleichszeile zwischen zwei korrekt berechneten Werten.
42574 hat eine ähnliche didaktische Klarheit: dirFS als Abstraktion soll genau das verhindern, was 42574 ermöglicht — Schreiben außerhalb des Roots. Aber MkdirAll und Symlink ohne Containment-Prüfung in den Pfad-Operationen lassen die Abstraktion an genau der Stelle leck werden, an der sie schützen sollte. Jede Filesystem-Abstraktion, die Symlinks zulässt und gleichzeitig Containment verspricht, muss diese Symlinks vor jeder Operation auflösen.
Fazit
apko ist eine bemerkenswert klare und gut auditierbare Code-Basis. Dass drei Lücken auf einmal kommen, ist kein Zeichen schlechter Qualität, sondern eines aktiv geprüften Projekts: 42574 setzt eine Linie fort, die mit GHSA-5g94-c2wx-8pxw im Februar dieses Jahres begonnen hat; 42575 ist die Sorte struktureller Verifikations-Lücke, die in jedem Lieferketten-Werkzeug erscheinen kann; 42576 ist die Sorte Type-Assertion-Panic, die sich in jeder Go-Codebase findet, sobald jemand die Eingabe variiert.
Die Frage lautet nicht, ob apko ein vertrauenswürdiges Werkzeug ist. Sie lautet, ob Sie die zweite Vertrauensstufe — den Vergleich der einzelnen Komponente gegen den signierten Index — in Ihrer eigenen Pipeline rekonstruieren oder darauf vertrauen, dass das Werkzeug es vollständig und ausnahmslos für Sie tut. Diese Woche haben wir gesehen, dass es das nicht immer tut. Die strukturelle Antwort ist eine eigene Verifikations-Stage, nicht der nächste Patch.
Persönlicher Hintergrund und technische Details zur Härtung von Wolfi-Build-Pipelines: ole-hartwig.eu.