Juli 2026
M D M D F S S
 12345
6789101112
13141516171819
20212223242526
2728293031  

Die meisten CI/CD-Pipelines laufen mit viel mehr Rechten als sie brauchen. Ein Workflow, der eigentlich nur Tests ausführen soll, hat Schreibzugriff auf das Repository, kann Releases anlegen und liest nebenbei alle Secrets aus. Das fällt niemandem auf, solange nichts passiert. Wenn eine Dependency kompromittiert wird oder ein Pull Request von außen Code einschleust, entscheidet genau dieser Rechteumfang darüber, wie groß der Schaden wird.

Warum großzügige Rechte der Normalfall sind

Rechte werden vergeben, wenn etwas nicht funktioniert. Der Build scheitert an einer fehlenden Berechtigung, jemand setzt sie großzügig, und danach fasst sie keiner mehr an. Bei GitHub Actions kommt dazu, dass der Standard lange auf der großzügigen Seite lag: Ohne explizite Angabe bekam der GITHUB_TOKEN in vielen Repositories Schreibrechte auf so gut wie alles. Wer nie eine permissions-Angabe schreibt, erbt genau dieses Verhalten.

Least Privilege heißt, den umgekehrten Weg zu gehen: mit null Rechten starten und nur das freigeben, was der konkrete Job tatsächlich braucht. Das ist etwas mehr Arbeit beim Aufsetzen und deutlich weniger Angriffsfläche danach.

Der Standard auf lesend setzen

Der erste Schritt betrifft nicht einen einzelnen Workflow, sondern das ganze Repository. In den Repository-Einstellungen unter Actions lässt sich der Default-Token auf Read repository contents permission stellen. Ab diesem Punkt startet jeder Workflow ohne Schreibrechte, und jeder Job, der mehr braucht, muss es explizit anfordern. Das macht Rechte sichtbar, statt sie im Hintergrund zu verteilen.

Im Workflow selbst gehört dann ganz oben ein minimaler Block, der als Basis für alle Jobs dient:

permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm test

Dieser Job liest den Code aus und führt Tests aus. Mehr braucht er nicht, also bekommt er auch nicht mehr. Kein Schreibzugriff auf Contents, keine Rechte an Packages, Deployments oder Issues.

Rechte pro Job vergeben, nicht pro Workflow

Interessant wird es, sobald ein Workflow mehrere Jobs hat. Rechte lassen sich pro Job überschreiben, und genau das sollte man nutzen. Der Test-Job bleibt lesend, der Release-Job bekommt exakt die Schreibrechte, die er braucht, und nichts darüber hinaus.

permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm test
release:
needs: test
runs-on: ubuntu-latest
permissions:
contents: write
id-token: write
steps:
- uses: actions/checkout@v4
- run: npx semantic-release

Der contents: write-Scope existiert nur im Release-Job und nur für die Dauer dieses Jobs. Wird der Test-Job durch eine manipulierte Dependency kompromittiert, kann der Angreifer trotzdem nichts ins Repository schreiben, weil dieser Job diese Rechte schlicht nicht hat.

Keine langlebigen Cloud-Credentials im Klartext

Least Privilege endet nicht am Repository. Wer aus der Pipeline in AWS, GCP oder Azure deployed, sollte keine statischen Access Keys als Secret hinterlegen. Ein einmal geleakter Key gilt so lange, bis ihn jemand aktiv rotiert. Über id-token: write und OIDC tauscht der Workflow stattdessen ein kurzlebiges Token gegen eine Rolle beim Cloud-Provider. Das Token lebt nur für den Lauf und ist an genau dieses Repository gebunden.

  deploy:
    needs: test
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read
    steps:
      - uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789:role/deploy
          aws-region: eu-central-1

Auf der IAM-Seite bekommt diese Rolle wiederum nur die Aktionen, die das Deployment braucht. Least Privilege in der Pipeline und Least Privilege in der Cloud greifen hier ineinander. Beides großzügig zu lassen, hebt den Nutzen des jeweils anderen wieder auf.

Woran man merkt, dass es zu viel war

Ein guter Test ist, die Rechte bewusst zu klein zu setzen und zu schauen, wo der Workflow scheitert. Die Fehlermeldung zeigt genau, welcher Scope gefehlt hat, und den fügt man dann gezielt hinzu. Das ist mühsamer als einmal write-all zu setzen, aber am Ende steht eine Pipeline, deren Rechte man erklären kann. Genau das ist der Punkt: Jedes Recht sollte eine Begründung haben, die über „sonst ging es nicht“ hinausgeht.

Wer das Thema systematisch angehen will, findet auf digital-business.blog weitere Beiträge dazu, wie sich solche Prinzipien über die ganze Lieferkette hinweg verankern lassen, statt sie in einzelnen Workflows zu reparieren.

Hinterlasse einen Kommentar

Diese Seite verwendet Akismet, um Spam zu reduzieren. Erfahre, wie deine Kommentardaten verarbeitet werden..