DevOps & Monitoring

Leere Secrets in GitHub Actions Fork-Pull-Requests beheben

GitHub Actions hält Secrets bei Pull Requests aus Forks standardmäßig zurück. Erfahren Sie sichere Muster wie workflow_run, um Zugangsdaten zu verwalten, ohne die Sicherheit zu gefährden.

Illustration von sicheren und unsicheren CI-Pipeline-Stadien
Für diesen Artikel erstellte Illustration

Automatisch aus dem englischen Original übersetzt.

Wenn eine Continuous-Integration-Pipeline auf internen Branches erfolgreich durchläuft, aber sofort scheitert, sobald ein Beitrag von einem externen Fork eingeht, liegt die Ursache oft nicht an einem Konfigurationsfehler. GitHub Actions blockiert den Zugriff auf Repository-Secrets während dieser spezifischen Ereignisse absichtlich, um Diebstahl von Zugangsdaten zu verhindern. Das Verständnis dieser Sicherheitsgrenze ist entscheidend für die Aufrechterhaltung robuster Pipelines in Open-Source- oder kollaborativen Umgebungen, ohne sensible Daten nicht vertrauenswürdigem Code preiszugeben.

Was passiert ist

Das Kernproblem tritt auf, wenn ein Job, der auf Standard-Branches korrekt funktioniert, bei einem Pull Request aus einem geforkten Repository leere Zugangsdaten vorfindet. Dieses Verhalten ist kein Bug oder eine Fehlkonfiguration, sondern ein bewusstes Sicherheitsfeature. GitHub Actions beschränkt das Standard-Token auf schreibgeschützte Berechtigungen und hält benutzerdefinierte Secrets bei diesen Läufen vollständig zurück. Das System bietet keine Einstellung zur Überschreibung dieser Einschränkung an, da dies eine kritische Schwachstelle erzeugen würde.

Der Fehler zeigt sich subtil. Anstatt einer expliziten Fehlermeldung, dass Secrets fehlen, wird die Secret-Variable als leerer String interpoliert. Nachgelagerte Tools schlagen dann mit ihren eigenen generischen Fehlercodes fehl, z. B. HTTP 401 Unauthorized oder Warnungen über fehlende erforderliche Eingaben. Diese Mehrdeutigkeit führt dazu, dass Ingenieure oft Zeit damit verschwenden, Netzwerkverbindungen oder Tool-Versionen zu debuggen, anstatt das zugrunde liegende Berechtigungsmodell zu erkennen. Ein einfacher diagnostischer Schritt kann das Problem bestätigen, indem geprüft wird, ob das Ereignis von einem Fork stammt und ob der Secret-Wert nicht leer ist, was sofortige Klarheit schafft.

Dieses Design existiert, weil der Code, der in einem Fork-Pull-Request ausgeführt wird, vom Mitwirkenden gesteuert wird, nicht vom Repository-Besitzer. Wären Secrets verfügbar, könnte ein böswilliger Akteur ein Build-Skript oder eine Testdatei so modifizieren, dass Deploy-Keys und API-Tokens in einem einzigen Commit exfiltriert werden. Indem GitHub die Vertrauensgrenze am Punkt der Codeausführung zieht, stellt es sicher, dass nicht vertrauenswürdiger Code niemals Zugriff auf sensible Umgebungsvariablen erhält. Die einzigen Trigger, die vollständige Secrets übertragen, sind solche, die im Kontext des Basis-Repositories laufen, wie bestimmte Ziel-Ereignisse oder nachfolgende Workflow-Läufe.

Wichtige Details

  • GitHub Actions hält alle Repository-Secrets von pull_request-Ereignissen zurück, die von Forks stammen.
  • Der GITHUB_TOKEN wird automatisch auf schreibgeschützte Berechtigungen für diese Ereignisse beschränkt.
  • Fehler erscheinen als generische Authentifizierungsfehler oder Warnungen wegen fehlender Eingaben, nicht als explizite Secret-Probleme.
  • Der pull_request_target-Trigger stellt Secrets bereit, checkt jedoch standardmäßig den Basis-Code aus, was eine sorgfältige Handhabung erfordert, um die Ausführung nicht vertrauenswürdigen PR-Codes zu vermeiden.
  • Dependabot-Pull-Requests haben ebenfalls keinen Zugriff auf Standard-Repository-Secrets und erfordern eine separate Konfiguration im Dependabot-Secret-Store.
  • Die Verwendung von workflow_run ermöglicht es einem zweiten, vertrauenswürdigen Workflow, auf Secrets zuzugreifen, nachdem ein initialer nicht vertrauenswürdiger Build abgeschlossen ist.

Hintergrund

Um zu verstehen, warum diese Einschränkung existiert, muss man den Unterschied zwischen vertrauenswürdigen und nicht vertrauenswürdigen Code-Kontexten erkennen. Bei einem Standard-Branch-Push gehört der Code den Repository-Mitarbeitern, die bereits Zugang zu den Projektressourcen haben. Bei einem Fork-Pull-Request stammt der Code von einem externen Benutzer, der möglicherweise keine Schreibrechte hat. Würde das CI-System diesen externen Code mit vollen administrativen Rechten ausführen, würde es effektiv jedem Mitwirkenden die Möglichkeit geben, Zugangsdaten zu stehlen.

Die Lösung besteht darin, den Build-Prozess von den Phasen Deployment oder Integrationstest zu trennen. Die initiale Phase führt den nicht vertrauenswürdigen Code in einer isolierten Umgebung ohne Secrets aus. Sobald diese Phase Artefakte erzeugt, wie kompilierte Binärdateien oder Testberichte, kann eine zweite Phase diese Artefakte in einem vertrauenswürdigen Kontext konsumieren. Diese Trennung stellt sicher, dass der Code zwar getestet wird, aber nie direkt mit sensiblen Infrastruktur-Zugangsdaten interagiert, es sei denn, dies ist ausdrücklich genehmigt und isoliert.

Warum es wichtig ist

Für Teams, die Open-Source-Projekte verwalten oder externe Beiträge akzeptieren, kann das Ignorieren dieses Sicherheitsmodells zu schweren Verletzungen führen. Der Versuch, die Einschränkung zu umgehen, indem unsichere Trigger wie pull_request_target mit einem Checkout des Pull-Request-Heads verwendet werden, erzeugt ein "Pwn Request"-Szenario. In dieser Situation führt der CI-Runner beliebigen Code vom Mitwirkenden mit vollem Zugriff auf alle Repository-Secrets aus. Dieses Risiko ist besonders hoch in populären öffentlichen Repositories, wo Angreifer aktiv nach solchen Fehlkonfigurationen suchen, um Cloud-Zugangsdaten zu ernten.

Umgekehrt führt eine falsche Behandlung zu schlechten Erfahrungen für Mitwirkende. Wenn externe Entwickler Pull Requests einreichen, die aufgrund fehlender Zugangsdaten fehlschlagen, könnten sie annehmen, das Projekt sei schlecht gewartet oder defekt. Diese Reibung discourages Community-Teilnahme und erhöht die Wartungslast für Kernteammitglieder, die den Fehler manuell erklären müssen. Die Implementierung klarer Skip-Logik oder separater vertrauenswürdiger Workflows stellt sicher, dass Mitwirkende genaues Feedback erhalten, während die Produktionsumgebung sicher bleibt.

Zusätzlich übersehen viele Teams, dass einige Integrationstests tatsächlich keine Live-Secrets benötigen. Durch Ersetzen externer Service-Aufrufe durch lokale Stubs oder Service-Container können Teams umfassendere Tests innerhalb des nicht vertrauenswürdigen Fork-Kontexts durchführen. Dieser Ansatz reduziert die Notwendigkeit komplexer Multi-Workflow-Setups und beschleunigt Feedback-Loops für Mitwirkende. Es verschiebt den Fokus vom Management des Secret-Zugriffs hin zur Verbesserung der Testisolierung und Zuverlässigkeit.

Was Sie tun können

  • Prüfen Sie Ihre Workflow-Dateien, um Schritte zu identifizieren, die wirklich Secrets benötigen, versus solche, die lokale Stubs oder Service-Container verwenden können.
  • Implementieren Sie eine bedingte Prüfung in Ihren Jobs, um secret-abhängige Schritte bei Pull Requests aus Forks zu überspringen, unter Verwendung einer sichtbaren Notiz zur Erklärung des Skips.
  • Verwenden Sie das workflow_run-Ereignis, um einen zweiten Workflow auszulösen, der Artefakte vom initialen Build herunterlädt und secret-abhängige Aufgaben in einem vertrauenswürdigen Kontext ausführt.
  • Stellen Sie bei Verwendung von pull_request_target sicher, dass Sie niemals den SHA des Pull-Request-Heads auschecken; bleiben Sie immer beim Code des Basis-Branches, um Code-Injection zu verhindern.
  • Konfigurieren Sie separate Secrets für Dependabot, wenn Ihre automatisierten Dependency-Updates fehlschlagen, da sie den Haupt-Repository-Secret-Store nicht teilen.
  • Posten Sie Commit-Status manuell aus vertrauenswürdigen Workflows, um sicherzustellen, dass Mitwirkende die Ergebnisse secret-abhängiger Prüfungen in ihren Pull Requests sehen.

Weitere News

Alle News