Start / Seminare / DevSecOps und sichere Software-Lieferketten
Modul
Pipelines härten und Berechtigungen reduzieren
4 Kapitel in diesem Modul-Video · Laufzeit
Transkript
Der gesprochene Text dieses Moduls zum Mitlesen, Überfliegen und Durchsuchen. Ein Klick auf einen Zeitstempel springt an die Stelle im Video.
Pipelines härten und Berechtigungen reduzieren
0:00 Machen Sie sich einmal klar, was Ihre Pipeline eigentlich darf. Sie liest den gesamten Quellcode. Sie hält die Zugangsdaten zur Registry. Sie darf in die Produktion ausrollen. Damit hat sie mehr Rechte als jeder einzelne Mensch im Team — und im Unterschied zu jedem Menschen wird sie von niemandem gegengelesen. In den meisten Projekten darf die Workflow-Datei jeder ändern, während für eine Änderung an der Anwendung ein Review nötig ist.
0:25 Dieses Missverhältnis räumen wir in diesem Modul auf.
Pipelines härten und Berechtigungen reduzieren
0:29 Drei Themen, alle mit unmittelbarem praktischen Nutzen. Erstens: Pipeline-Code als das behandeln, was er ist — sicherheitskritischer Code mit weitreichenden Rechten. Zweitens: Zugangsdaten und externe Bestandteile, also die Frage, was Sie einbinden und wie Sie sich gegenüber der Cloud ausweisen. Und drittens die Maschinen, auf denen das alles läuft: Runner, Caches und der heikle Fall, dass ein Beitrag aus einem fremden Fork kommt.
Pipeline-Code als sicherheitskritischer Bestandteil
0:56 Beginnen wir mit den Rechten. Das ist der Teil, der am schnellsten geht und am meisten bringt — und der fast überall zu großzügig eingestellt ist, weil die Voreinstellung einmal bequem war und danach niemand mehr hingesehen hat. Die Empfehlung ist erfreulich schlicht: Standardberechtigung des Build-Tokens auf Lesen setzen, und je Job dort anheben, wo tatsächlich mehr gebraucht wird.
1:18 Der Nutzen ist ebenso schlicht — es begrenzt den Schaden eines offengelegten Tokens. Denken Sie daran, wie viel in einem Build-Lauf passiert: Dutzende fremde Actions, Paketmanager, Skripte. Wenn eines davon an das Token kommt, entscheidet allein diese Einstellung darüber, ob der Angreifer lesen oder schreiben kann. Der Unterschied zwischen „konnte den Code sehen" und „konnte den Code ändern" ist beträchtlich.
1:42 Das Muster ist in zwei Zeilen erklärt. Oben eine Vorgabe für alle Jobs: nur lesen. Darunter hebt genau ein Job an, nämlich der, der veröffentlicht — er braucht Schreibrecht auf die Paketablage und die Möglichkeit, ein Identitätstoken ausstellen zu lassen. Letzteres brauchen wir gleich für OpenID Connect. Wichtig ist, was Sie hier nicht sehen: Der Build-Job hat kein einziges Schreibrecht.
2:06 Er baut, er testet, und er gibt sein Ergebnis weiter. Wenn Sie heute Abend nur eine Sache an Ihrer Pipeline ändern, dann diese. Warum überhaupt trennen? Weil Rechte immer für den ganzen Job gelten. Solange Bauen, Prüfen, Veröffentlichen und Ausrollen in einem Job stecken, trägt dieser Job die Summe aller nötigen Rechte — und damit trägt auch jeder Schritt darin diese Summe, einschließlich der fremden Actions.
2:31 Getrennte Jobs erlauben getrennte Rechte, das ist der ganze Gedanke. Der dritte Punkt hängt daran: Freigaberegeln für Produktionsumgebungen wirken erst, wenn das Deployment ein eigener Schritt ist. Und der vierte schließt an Modul vier an — die Workflow-Datei gehört unter CODEOWNERS. Die interessante Spalte ist die rechte, denn sie beschreibt, was im Schadensfall nicht möglich ist.
2:54 Der Build-Job kann den Quellcode lesen und sonst nichts — wird er kompromittiert, ist das ärgerlich, aber begrenzt. Der Prüfschritt darf Befunde schreiben, kommt aber nicht an die Registry. Der Veröffentlichungsschritt darf in die Registry schreiben, aber den Quellcode nicht ändern. Und das Ausrollen erreicht die Zielumgebung, nicht die Registry.
3:14 Diese vier Zeilen beschreiben ein Prinzip, das älter ist als jede Pipeline: Wer viel darf, sollte wenig Gelegenheit haben — und wer viel Gelegenheit hat, wenig dürfen. Der erste Punkt ist die häufigste Ursache zu weiter Rechte: Die Standardberechtigung bleibt auf Schreiben, weil ein einzelner Job sie braucht. Der zweite ist der Ein-Job-Workflow, der alles erledigt — bequem zu schreiben, teuer im Schadensfall.
3:38 Der dritte ist das Missverhältnis vom Anfang: Jeder darf die Workflow-Datei ändern, während der Anwendungscode Review braucht. Und der vierte ist eine Falle, die man erst nach dem Einrichten bemerkt — Umgebungsfreigaben sind konfiguriert, aber ein zweiter Workflow rollt am selben Ziel vorbei aus. Prüfen Sie, wie viele Wege in Ihre Produktion führen.
Zugangsdaten und externe Komponenten
3:58 Im zweiten Kapitel geht es um zwei Dinge, die in dieselbe Richtung zielen: Was Sie einbinden, soll unveränderlich sein — und wie Sie sich ausweisen, soll kurzlebig sein. Beides nimmt einem Angreifer die Zeit, die er für einen Angriff braucht. Hier steht eine Aussage, die überraschend eindeutig ist: Die Referenzierung einer Action über den vollständigen Commit-SHA ist derzeit die einzige Möglichkeit, sie als unveränderliches Release zu verwenden.
4:25 Der Grund ist kryptografischer Natur. Ein Tag ist ein Name und kann umgehängt werden — genau die Bedrohung aus Modul zwei, nur diesmal beim Zulieferer. Ein SHA bezeichnet einen Inhalt; damit er etwas anderes bezeichnet, bräuchte es eine Kollision. Das ist der Unterschied zwischen „ich vertraue darauf, dass der Herausgeber den Tag nicht bewegt" und „es ist technisch festgelegt".
4:49 Zwei Muster in einem Beispiel. Oben die gepinnte Action — statt einer Versionsmarke der vollständige Commit-SHA. Unten das Muster, das mich mehr beschäftigt: Der Titel eines Pull Requests ist eine Eingabe, die ein Fremder bestimmt. Steht ein solcher Wert direkt in einem Kommando, wird er beim Erzeugen des Skripts eingesetzt — und was dort steht, wird ausgeführt.
5:09 Über eine Umgebungsvariable dagegen wird der Wert gelesen und ist nicht Teil des Skripts. Die Fußzeile bringt es auf den Punkt: Genau dieser Unterschied verhindert die Einschleusung. Dieses Muster kennen Sie aus einem anderen Zusammenhang, und das hilft beim Verstehen. Es ist dieselbe Klasse von Problem wie eine Einschleusung in eine Datenbankabfrage: Daten werden zu Code, weil sie an der falschen Stelle eingesetzt werden.
5:33 Die Lösungen sind entsprechend verwandt — entweder der Wert wandert in eine Variable, die nur gelesen wird, oder er wird einer Action als Argument übergeben, die ihn gar nicht erst in eine Shell gibt. Der vierte Punkt ist ein Nebeneffekt des Pinnens, der oft übersehen wird: Der Abhängigkeitsgraph liest die Einbindungen und macht die Actions dadurch überhaupt erst sichtbar.
5:54 Der Gedanke ist elegant: Statt einen dauerhaften Cloud-Schlüssel zu hinterlegen, weist sich der Lauf gegenüber der Cloud aus und bekommt dafür ein kurzlebiges Token. Der entscheidende Satz steht im zweiten Punkt — was nicht gespeichert ist, kann auch nicht aus einem Protokoll oder einem Cache entweichen. Sie beseitigen damit nicht nur ein Risiko, sondern eine ganze Klasse von Vorfällen.
6:16 Dazu kommt die feinere Steuerung: Der Zugriff lässt sich an Repository, Branch und Umgebung binden. Und Bewertungswerkzeuge, die wir gestern kennengelernt haben, markieren fehlendes Pinning und zu weite Tokenrechte ausdrücklich als Risiko. Der erste Punkt ist der Grund für die ganze Pinning-Diskussion: Eine Action hängt an einem beweglichen Tag, den der Herausgeber jederzeit umhängen kann — mit oder ohne böse Absicht.
6:40 Der zweite ist eine unangenehme Folge des Pinnens, die man kennen muss: Aktualisierungsmeldungen entstehen für versionierte Referenzen, nicht für gepinnte SHAs. Sie brauchen also einen eigenen Weg, Ihre Pins aktuell zu halten. Der dritte ist die Einschleusung von eben. Und der vierte ist schlichte Trägheit — Cloud-Zugangsdaten liegen dauerhaft herum, obwohl kurzlebige Token möglich wären.
Runner, Caches und Beiträge aus Forks
7:03 Im dritten Kapitel geht es um die Maschine selbst. Der Runner ist der Ort, an dem fremder Code mit Ihren Rechten ausgeführt wird — jede Abhängigkeit, jedes Build-Plugin, jede eingebundene Action. Wie viel dieser Code anrichten kann, hängt davon ab, was er auf der Maschine vorfindet und was er dort hinterlassen darf. Die Formulierung in der Dokumentation ist bemerkenswert direkt: Selbst betriebene Runner haben keine Zusicherung, in kurzlebigen, sauberen virtuellen Maschinen zu laufen.
7:31 Daraus folgt die Möglichkeit einer dauerhaften Kompromittierung — was ein Lauf hinterlässt, steht dem nächsten zur Verfügung. Das ist der entscheidende Unterschied zu einem bereitgestellten Runner, der für jeden Job neu entsteht. Selbst betriebene Runner haben durchaus gute Gründe: Zugang zum internen Netz, besondere Hardware, Kosten. Nur gehört dann die Lebensdauer der Maschine zur Sicherheitsbetrachtung.
7:55 Drei Schichten, und die dritte überrascht viele. Das Dateisystem ist naheliegend — Reste bleiben liegen. Der Cache ist der aus Modul zwei bekannte Fall: ein vergifteter Eintrag wirkt in jedem folgenden Build. Die Prozessliste ist der subtile Punkt: Laufen mehrere Jobs gleichzeitig auf einer Maschine, können sie unter Umständen die Prozesse der anderen sehen — und damit Geheimnisse, die als Argument übergeben wurden.
8:19 Genau deshalb ist die Empfehlung, einen Runner nicht von mehreren Repositories gemeinsam nutzen zu lassen. Wir vertiefen hier, was in Modul vier schon anklang. Privilegierte Workflows teilen den Cache des Hauptzweigs und haben Zugang zu Geheimnissen — und deshalb gilt die Regel ohne Ausnahme: Ungeprüfter Pull-Request-Code wird dort nicht ausgecheckt.
8:39 Der dritte Punkt gibt die Bauanleitung: Für getrennte Rechte ist der nachgelagerte Ablauf dem privilegierten Auslöser vorzuziehen. Also erst prüfen, ohne Rechte, dann in einem zweiten Schritt mit Rechten weiterarbeiten. Und der vierte schließt den Kreis: Artefakte aus fremden Läufen sind wie fremder Code zu behandeln, auch wenn sie aus dem eigenen Haus zu kommen scheinen.
9:00 Eine Entscheidungsübung, und das Lernziel ist bewusst so formuliert: bewerten anhand von Isolation, Lebensdauer und Netzzugang — nicht anhand der Kosten allein. Kosten sind ein legitimes Argument, sie sind nur selten das einzige. Der Hinweis nimmt eine Diskussion vorweg, die in dieser Übung immer aufkommt: Ja, man kann nach dem Job aufräumen.
9:21 Aber Aufräumen ist etwas anderes als Isolation — wenn während des Laufs ein Geheimnis abgeflossen ist, hilft die sauberste Aufräumroutine nicht mehr. Die Antwort sind kurzlebige Runner je Job. Der erste Punkt ist der häufigste Aufbaufehler: Ein selbst betriebener Runner bedient mehrere Repositories und teilt damit deren Geheimnisse.
9:40 Der zweite ist der eben besprochene Trugschluss mit der Aufräumroutine. Der dritte ist eine bewusste Abwägung, die oft unbewusst getroffen wird — der Cache wird über Branch-Grenzen geteilt, weil das Bauen sonst länger dauert; damit wird ein Angriffsweg gegen ein paar Minuten eingetauscht. Und der vierte ist eine Empfehlung, die fast niemand befolgt: Protokolle regelmäßig auf ungeschwärzte Geheimnisse durchsehen, besonders nach Läufen mit ungültigen Eingaben.
Übung
10:06 In der Übung härten Sie die Pipeline der Deichwacht. Fünf Schritte, alle an einem Nachmittag machbar — und am Ende sollten Sie im Protokoll nachsehen können, welche Rechte der Lauf tatsächlich noch anfordert. Dieser Blick ins Protokoll ist der Beleg, dass die Änderung gewirkt hat. Drei Ziele, und alle drei sind überprüfbar. Alle externen Actions auf einen Commit-SHA gepinnt — das lässt sich mit einem Blick in die Datei verifizieren.
10:31 Bauen und Ausrollen getrennt — ebenfalls sichtbar. Und das Registry-Passwort durch kurzlebige Token ersetzt, was bedeutet, dass Sie ein Geheimnis weniger verwalten. Der Hinweis ist der Abschlusstest der Übung: Sehen Sie nach der Umstellung nach, welche Rechte der Lauf noch anfordert. Wenn dort weiterhin Schreibrechte auftauchen, die niemand erklären kann, ist die Aufgabe noch nicht erledigt.
10:55 Die Schritte bauen aufeinander auf und sind einzeln klein. Standardberechtigung auf Lesen, je Job gezielt anheben. Build und Deployment trennen. Alle externen Actions pinnen — nehmen Sie sich dafür eine halbe Stunde, es ist stumpfe, aber wirksame Arbeit. Fremde Eingaben aus Kommandos in Umgebungsvariablen verlegen; suchen Sie gezielt nach Kontextwerten in Kommandozeilen. Und zuletzt die Cloud-Zugangsdaten ersetzen.
11:21 Die Fußzeile nennt das Ziel in einem Satz: Am Ende sollte der Build-Job kein einziges Schreibrecht mehr besitzen. Der erste Punkt beschreibt, wie Härtungsarbeit rückgängig gemacht wird: Die Rechte werden gesenkt, ein Lauf schlägt fehl, und statt das fehlende Recht gezielt zu ergänzen, dreht jemand pauschal zurück. Planen Sie ein, dass die ersten Läufe scheitern.
11:43 Der zweite ist eine Feinheit beim Pinnen — wer den SHA nimmt, auf den ein Tag gerade zeigt, muss wissen, dass der Tag später woanders hinzeigen kann; der SHA bleibt richtig, der Vergleich mit dem Tag wird falsch. Der dritte ist Formalismus: getrennte Workflows, die dieselben Geheimnisse sehen. Und der vierte: Niemand prüft nach, ob es enger geworden ist.
Lieber mit Trainer? Dieses Modul ist Teil unserer Team-Schulungen — mit Übungen, Ihrem eigenen Code und Fragen, die ein Video nicht beantwortet. Mehr erfahren →