CI/CD
GitHub-Actions-Cache: Wie er funktioniert und was er verbirgt
Der GitHub-Actions-Cache speichert Dateien zwischen Workflow-Läufen, sodass Jobs bereits erledigte Downloads überspringen. Das macht Builds schneller. Es bedeutet aber auch, dass Pakete in Ihren Build gelangen, ohne geladen zu werden, und das sehen die meisten Aufzeichnungen eines Builds nie.
Aktualisiert am
Wie der Cache funktioniert
Die Action actions/cache speichert am Ende eines Jobs eine Reihe von Pfaden unter einem Key und stellt sie zu Beginn
eines späteren Jobs wieder her. Ein typisches npm-Setup legt den Download-Cache des Paketmanagers im Cache ab und bildet den Key aus dem Lockfile:
- uses: actions/cache@v4
with:
path: ~/.npm
key: npm-${{ runner.os }}-${{ hashFiles('package-lock.json') }}
restore-keys: |
npm-${{ runner.os }}-
- run: npm ci
Beim Wiederherstellen sucht die Action zuerst nach einem exakten Treffer für key, dann nach einem Teiltreffer und dann
der Reihe nach nach jedem restore-keys-Präfix. Passen mehrere Caches zu einem Präfix, stellt sie den zuletzt erstellten
wieder her. Dieser Fallback hält Builds schnell, nachdem sich das Lockfile geändert hat, und bedeutet zugleich, dass ein Job mit einem
Cache starten kann, der für ein älteres Lockfile gebaut wurde.
Oft brauchen Sie die Action gar nicht. setup-node, setup-python, setup-java,
setup-go, setup-ruby und setup-dotnet haben Caching eingebaut. Bei Node.js cacht
setup-node die globalen Paketdaten von npm, Yarn und pnpm, nicht node_modules, und schaltet das
npm-Caching automatisch ein, wenn package.json npm als packageManager deklariert.
Scope und Limits
| Regel | Was GitHub tut |
|---|---|
| Welche Caches ein Lauf wiederherstellen kann | Die seines eigenen Branches und des Default-Branches; Pull Requests auch die ihres Base-Branches |
| Was ein Pull Request schreiben kann | Nur Caches im Scope seiner Merge-Ref, nicht im Scope des Default-Branches |
| Speicher | Standardmäßig 10 GB pro Repository; Organisations- und Enterprise-Inhaber können das erhöhen |
| Verdrängung | Einträge, auf die über 7 Tage nicht zugegriffen wurde, und die am längsten ungenutzten, wenn der Speicher voll ist |
| Secrets | Nicht cachen: Jeder mit Pull-Request-Zugriff kann Cache-Inhalte lesen |
Was der Cache vor Ihrer SBOM verbirgt
Ein Cache ist eine Abkürzung an der Paket-Registry vorbei. Das ist sein Zweck, und genau deshalb verwirrt er jede Aufzeichnung dessen, was ein Build verwendet hat:
- Kein Download, keine Spur. Aus einem Cache wiederhergestellte Pakete werden ohne Anfrage an die Registry installiert. Eine Aufzeichnung, die auf dem Netzwerkverkehr des Builds beruht, übersieht sie, sofern sie dem Cache nicht folgt.
-
Alter Inhalt unter neuem Key. Ein partieller
restore-keys-Treffer stellt einen Cache wieder her, der für ein früheres Lockfile gebaut wurde. Die Installation ergänzt oder ersetzt dann, was sich geändert hat, aber das, womit der Job begonnen hat, ist nicht das, was das aktuelle Lockfile beschreibt. - Mehr als Ihre Abhängigkeiten. Caches enthalten auch Build-Tools, Compiler und generierte Dateien, die in keinem Anwendungsmanifest stehen.
- Ein Scan kann es nicht erkennen. Eine Lockfile-basierte SBOM beschreibt, was im Lockfile steht, ob der Job genau das aus seinem Cache installiert hat oder nicht.
Wie sich das ins größere Problem einfügt, lesen Sie unter warum ein Lockfile keine Aufzeichnung des Ausgelieferten ist und transitive Abhängigkeiten.
Cache Poisoning
Caches werden von den Workflows eines Repositorys gemeinsam genutzt und sind damit ein Weg zwischen ihnen. Im Mai 2024 zeigte der
Sicherheitsforscher Adnan Khan, dass jeder, der Code im Kontext des Default-Branches ausführen kann, über einen
verwundbaren pull_request_target-Workflow oder eine kompromittierte Abhängigkeit Dateien in Workflows überschreiben kann,
die Caches wiederherstellen. Mit dem Cache-Token eines laufenden Jobs kann ein Angreifer den Cache des Repositorys so füllen, dass
GitHub legitime Einträge verdrängt, und dann vergiftete Einträge unter denselben Keys schreiben. Ein Release-Workflow, der sie
wiederherstellt, führt die Dateien des Angreifers mit seinen eigenen Secrets aus.
So verringern Sie das Risiko:
- Bauen Sie Releases aus einem sauberen Zustand oder aus Caches, die nur der Release-Workflow schreibt.
- Führen Sie Pull-Request-Code, dem Sie nicht vertrauen, nie im Kontext des Default-Branches aus.
- Halten Sie Secrets und Tokens aus gecachten Pfaden heraus.
- Behandeln Sie das, was ein Cache wiederhergestellt hat, als Build-Input, der dieselbe Prüfung braucht wie ein Download.
Wie CRACI mit Caches umgeht
CRACI ist ein GitHub-Actions-Runner, daher funktionieren actions/cache und die setup-*-Caches wie
bisher, zusammen mit Container-Layer-Caching. Caching ist auch ein Grund dafür, dass CRACI Builds etwa doppelt so schnell ausführt wie
GitHub-gehostete Runner, mit schnellerer Hardware und kürzeren Wartezeiten.
Was sich ändert, ist die Aufzeichnung. Der paketbewusste Proxy von CRACI zeichnet die Pakete auf, die ein Job lädt, und die Nachweise wandern mit den Caches mit: Wenn ein Job einen Cache wiederherstellt, übernimmt CRACI die ihm bekannten Abhängigkeiten in die SBOM dieses Jobs.
- Vollständigkeit pro Cache. Jeder Cache hat einen Vollständigkeitsstatus, genau wie jeder Job. Ein Job, der einen unvollständigen Cache wiederherstellt, erhält eine unvollständige SBOM, und ein von ihm erzeugter Cache trägt diesen Status weiter, sodass eine Lücke nie stillschweigend zu einer vollständigen Aufzeichnung wird.
- Kein Raten aus dem Archiv. CRACI liest keine Paketmanifeste im Cache-Archiv. Es weiß, was in einem Cache steckt, weil es den Job aufgezeichnet hat, der ihn erzeugt hat.
-
Ein Cache pro erzeugendem Job hält diese Nachweise zuverlässig, zusammen mit einer Egress-Richtlinie, die im erzeugenden Job auf
default: denygesetzt ist. Details stehen im Leitfaden zu Cache und SBOM-Vollständigkeit.
Wie die Aufzeichnung funktioniert, lesen Sie unter Build-Time-SBOMs, oder buchen Sie eine Demo, um einen Ihrer gecachten Workflows auf CRACI auszuführen.
GitHub-Actions-Cache: häufig gestellte Fragen
Wie lange bewahrt GitHub einen Actions-Cache auf?
GitHub entfernt Cache-Einträge, auf die über 7 Tage nicht zugegriffen wurde. Überschreitet ein Repository sein Speicherlimit, standardmäßig 10 GB, verdrängt GitHub außerdem Caches nach dem letzten Zugriff, die ältesten zuerst.
Cacht setup-node node_modules?
Nein. Der cache-Input von actions/setup-node cacht die globalen Paketdaten des Paketmanagers für npm, Yarn oder pnpm, nicht node_modules. Der Installationsschritt läuft trotzdem, liest die Pakete aber aus dem wiederhergestellten Cache, statt sie herunterzuladen.
Kann ein Pull Request Caches aus dem Main-Branch lesen?
Ja. Ein Workflow-Lauf kann Caches seines eigenen Branches und des Default-Branches wiederherstellen, und ein Pull Request auch Caches seines Base-Branches. In einem Pull Request erstellte Caches gelten nur für dessen Merge-Ref und lassen sich daher nicht in den Scope des Default-Branches schreiben.
Ist der Actions-Cache in Release-Workflows sicher?
Das ist eine Abwägung. 2024 veröffentlichte Forschung zeigte, dass jeder, der Code in einem Workflow des Default-Branches ausführen kann, Caches vergiften kann, die andere Workflows wiederherstellen, auch Release-Workflows. Workflows, die Releases veröffentlichen oder Deployment-Secrets enthalten, sind sicherer, wenn sie aus einem sauberen Zustand bauen oder aus Caches, die nur sie selbst schreiben.
Verändert ein wiederhergestellter Cache meine SBOM?
Das kann er. Aus einem Cache wiederhergestellte Pakete werden installiert, ohne heruntergeladen zu werden, sodass eine Aufzeichnung auf Basis des Netzwerkverkehrs sie übersieht, sofern sie den Cache nicht verfolgt. Und ein Scan des Lockfiles sagt Ihnen nicht, ob der Cache genau diese Versionen enthielt.
Behalten Sie Ihre Caches und eine vollständige SBOM
Lassen Sie Ihren Workflow auf CRACI laufen: Caches beschleunigen den Build weiterhin, und ihre Abhängigkeiten bleiben in der aufgezeichneten SBOM jedes Jobs.
Demo buchen