Jede GCP-Sicherheitsgeschichte beginnt mit derselben Szene. Eine Datei sa-key.json liegt in einer CI-Variable, einer angepinnten Slack-Nachricht und seit 2023 in irgendjemandes ~/Downloads. Sie hat eine Eigenschaft, die dir Angst machen sollte: Sie läuft nie ab. Wer diese Datei besitzt, ist dein Service-Account — mit allen Rollen, die du ihm je gegeben hast — bis sich jemand daran erinnert, sie zu rotieren. Niemand erinnert sich.
Langlebige Service-Account-Schlüssel sind der mit Abstand häufigste Weg, wie ein Google-Cloud-Projekt vollständig kompromittiert wird. Kein raffinierter Exploit — eine geleakte JSON-Datei. 2026 brauchst du sie für fast nichts mehr, und dies ist der Praxis-Leitfaden, um sie loszuwerden: wie der schlüssellose Austausch tatsächlich funktioniert, wie du GitHub Actions ohne Secrets verdrahtest, die eine Konfigurationszeile, die über Sicherheit oder Katastrophe entscheidet, und wie du migrierst, ohne eine einzige Pipeline zu zerbrechen.

Warum ein langlebiger Schlüssel 2026 ein Risiko ist
Ein Service-Account-Schlüssel ist ein statisches Credential mit drei fatalen Eigenschaften:
- Er läuft nie ab. Ein Passwort hat eine Rotationsrichtlinie; ein Schlüssel keine. Er ist gültig, bis er manuell widerrufen wird.
- Er wandert. JSON lässt sich kopieren und einfügen. Er landet in der Git-Historie, in CI-Logs, auf Laptops und in Screenshots — jedes davon ein neuer Ort, an dem er leaken kann.
- Er verbirgt, wer ihn genutzt hat. Audit-Logs zeigen den Service-Account handeln, nie den Menschen oder die Maschine, die den Schlüssel vorgelegt hat. Nach einem Leak kannst du Angreifer-Traffic nicht von legitimem unterscheiden.
Ein Schlüssel in einem öffentlichen Repo bedeutet vollen Zugriff auf alles, was dieser Account erreichen kann — oft mehr, als sich jemand daran erinnert, vergeben zu haben. Die Lösung heißt nicht „häufiger rotieren“. Sie heißt: den Schlüssel gar nicht erst existieren lassen.
Was Workload Identity Federation wirklich ist
Hier die Übersetzung aus dem Doku-Deutsch. Dein externes System besitzt bereits eine Identität, die es beweisen kann: GitHub Actions stellt jedem Workflow-Lauf ein signiertes OIDC-Token aus, AWS signiert seine Instanz-Identität, eine On-Premises-Maschine kann jeden OIDC-Provider betreiben. Workload Identity Federation bringt Google Cloud bei, diesem Token zu vertrauen und es gegen ein kurzlebiges GCP-Zugriffstoken zu tauschen. Es wird kein Schlüssel erzeugt. Kein Secret verlässt Google. Nichts Statisches existiert, das leaken könnte.
Drei Bausteine erledigen die Arbeit:
- Workload Identity Pool — eine Vertrauensgrenze, die eine oder mehrere externe Identitätsquellen enthält.
- Provider — der konkrete Aussteller, dem du vertraust (GitHubs OIDC-Endpunkt, AWS, eine OIDC-URL), plus die Art, wie seine Claims gelesen werden.
- Attribute Mapping + Condition — wie die Felder des externen Tokens (
repository,repository_owner,sub) auf Google-Attribute gemappt werden und welche davon überhaupt tauschen dürfen.
Der Ablauf von Anfang bis Ende:

Der externe Workload legt sein OIDC-Token vor, der Provider validiert und mappt die Claims, Googles Security Token Service (STS) gibt ein kurzlebiges Token zurück, und dieses Token impersoniert einen Service-Account, der auf genau die Rollen beschränkt ist, die du vergeben hast. Wenn der Job endet, stirbt das Token. Es gibt nichts zu rotieren, weil es nichts zu speichern gibt.
Praxis: GitHub Actions zu GCP ganz ohne Schlüssel
Der häufigste Wunsch ist ein Deployment aus GitHub ohne ein GCP_SA_KEY-Secret. Hier das komplette Setup.
Zuerst Pool und einen GitHub-Provider erstellen:
# Ein Pool pro Vertrauensgrenze (z. B. gesamte CI)gcloud iam workload-identity-pools create github-pool \ --location=global \ --display-name="GitHub Actions"
# Ein Provider, der GitHubs OIDC-Aussteller vertrautgcloud iam workload-identity-pools providers create-oidc github-provider \ --location=global \ --workload-identity-pool=github-pool \ --issuer-uri="https://token.actions.githubusercontent.com" \ --attribute-mapping="google.subject=assertion.sub,attribute.repository=assertion.repository,attribute.repository_owner=assertion.repository_owner" \ --attribute-condition="assertion.repository_owner == 'deine-org'"Dieses letzte Flag ist das entscheidende. Merke es dir — wir kommen im nächsten Abschnitt darauf zurück, denn es falsch zu setzen ist genau der Weg, wie Leute ihr Projekt an Fremde verschenken.
Als Nächstes einem bestimmten Repo erlauben, einen Deploy-Service-Account zu impersonieren:
PROJECT_NUMBER=$(gcloud projects describe my-project --format='value(projectNumber)')
gcloud iam service-accounts add-iam-policy-binding \ deployer@my-project.iam.gserviceaccount.com \ --role=roles/iam.workloadIdentityUser \ --member="principalSet://iam.googleapis.com/projects/${PROJECT_NUMBER}/locations/global/workloadIdentityPools/github-pool/attribute.repository/deine-org/my-app"Zuletzt der Workflow — beachte, dass nirgends ein Schlüssel steht:
permissions: contents: read id-token: write # erlaubt dem Runner, ein OIDC-Token anzufordern
jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - id: auth uses: google-github-actions/auth@v2 with: project_id: my-project workload_identity_provider: projects/${{ env.PROJECT_NUMBER }}/locations/global/workloadIdentityPools/github-pool/providers/github-provider service_account: deployer@my-project.iam.gserviceaccount.com - run: gcloud run deploy my-app --source .Kein Secret zum Leaken, kein Schlüssel zum Rotieren. Der Runner beweist, wer er ist, Google gibt ein Token zurück, das in Minuten abläuft, und das Deployment läuft.
Die Falle, in die alle tappen: attribute_condition
Das ist der Teil, den die Quickstarts überspringen, und der Grund, diesen Artikel zu lesen.
Wenn du den Provider erstellst, entscheidet die attribute_condition, welche externen Identitäten überhaupt ein Token tauschen dürfen. Lässt du sie weg, hast du Google gesagt, dem gesamten Aussteller zu vertrauen. Bei GitHub ist der Aussteller token.actions.githubusercontent.com — der Tokens für jedes öffentliche Repository der Welt signiert. Jeder Fremde kann einen Workflow forken, ein Token anfordern und deinen Service-Account impersonieren.

Die gefährliche Variante — ohne Condition:
# GEFAHR: vertraut jedem Repo, für das GitHub je ein Token signiert--issuer-uri="https://token.actions.githubusercontent.com" \--attribute-mapping="google.subject=assertion.sub"# (keine --attribute-condition)Die sichere Variante — auf deine Organisation gesperrt, dann auf dem Binding auf ein Repo eingegrenzt:
# SICHER: nur Tokens aus Repos, die du besitzt, dürfen überhaupt tauschen--attribute-condition="assertion.repository_owner == 'deine-org'"Zwei unabhängige Schranken schützen dich: Die Condition auf dem Provider entscheidet, wer ein Token tauschen darf, und der principalSet auf dem IAM-Binding entscheidet, wer davon einen bestimmten Service-Account impersonieren darf. Setze beide. Ein Provider ohne Condition ist die Nummer-eins-Fehlkonfiguration bei Workload Identity Federation — und genau das, wonach ein Angreifer scannt.
Migration von bestehenden Schlüsseln ohne Ausfallzeit
Du kannst keine Schlüssel löschen, die du nicht siehst. Beginne mit einer Inventur über alle Service-Accounts:
for sa in $(gcloud iam service-accounts list --format='value(email)'); do gcloud iam service-accounts keys list --iam-account="$sa" \ --managed-by=user --format="table(name, validAfterTime)"done--managed-by=user ist wichtig: Es filtert die von Google verwalteten Schlüssel heraus (die in Ordnung sind und sich selbst rotieren), sodass du nur die selbst erstellten siehst, die du tatsächlich abschaffen musst.
Migriere dann einen Workload nach dem anderen, niemals alles auf einmal:
- Federation parallel aufbauen. Erstelle Pool, Provider und Binding. Der alte Schlüssel funktioniert weiter — noch ist nichts kaputt.
- Eine Pipeline umstellen auf schlüssellose Authentifizierung und prüfen, dass sie deployt.
- Deaktivieren, nicht löschen. Beobachte die Audit-Logs auf alles, was ihn noch nutzt:
gcloud logging read \ 'protoPayload.authenticationInfo.principalEmail="deployer@my-project.iam.gserviceaccount.com" AND protoPayload.metadata.@type:"ServiceAccountKey"' \ --freshness=7d --limit=20- Löschen, wenn es ruhig ist. Wenn den Schlüssel einen ganzen Zyklus lang nichts genutzt hat, entferne ihn.
Deaktivieren-vor-Löschen gibt dir ein kostenloses Rollback: Falls etwas Vergessenes den Schlüssel doch noch braucht, aktivierst du ihn in Sekunden wieder, statt einen Produktionsausfall zu bekämpfen.
Schlüssel unwiederbringlich machen
Schlüssel zu entfernen ist wertlos, wenn nächsten Sprint jemand einen neuen erstellt. Schließe die Tür auf Organisationsebene:
gcloud resource-manager org-policies enable-enforce \ iam.disableServiceAccountKeyCreation \ --organization=YOUR_ORG_IDMit aktivierter Policy schlägt gcloud iam service-accounts keys create für alle fehl. Kombiniere sie mit einer Benachrichtigung auf das Audit-Event CreateServiceAccountKey, sodass jeder Versuch — etwa eine angefragte Policy-Ausnahme — sichtbar statt stillschweigend ist. Jetzt ist „keine Schlüssel“ ein erzwungener Zustand, keine Hoffnung.
Wenn schlüssellos noch nicht geht
Ehrlichkeit hält das Ganze glaubwürdig: Federation deckt nicht alles ab. Ein Legacy-SDK ohne OIDC-Unterstützung, eine Vendor-Integration, die nur eine Schlüsseldatei akzeptiert, ein Air-Gap-System, das Googles STS nicht erreichen kann — das gibt es weiterhin. Minimiere für diese Fälle stattdessen den Schaden:
- Beschränke den Service-Account des Schlüssels auf die absolut geringsten Rechte, mit denen er die Aufgabe erfüllen kann.
- Rotiere nach einem kurzen, automatisierten Zeitplan — ein Schlüssel, den du behalten musst, ist ein Schlüssel, den du ablaufen lassen musst.
- Speichere ihn im Secret Manager, niemals in einem Repo, einer CI-Variable oder einem geteilten Laufwerk.
Ein Schlüssel, den du behalten musst, sollte die seltene, dokumentierte Ausnahme sein — nicht der Standard, den du nie hinterfragt hast.
Die Feldregel
Behandle jeden langlebigen Service-Account-Schlüssel als einen Sicherheitsvorfall, der noch nicht passiert ist. Ersetze ihn durch Workload Identity Federation: ein externes OIDC-Token, getauscht gegen ein kurzlebiges GCP-Token, ohne dass je ein Secret gespeichert wird. Setze immer eine attribute_condition — ohne sie kann jedes Repo deine Identität tragen. Migriere einen Workload nach dem anderen mit Deaktivieren-vor-Löschen und erzwinge dann disableServiceAccountKeyCreation, damit Schlüssel nie wiederkehren. Mach das richtig, und die gefährlichste Datei in deiner Infrastruktur hört einfach auf zu existieren.

Wenn du eine GCP-Umgebung härtest — oder dieselbe schlüssellose Migration selbst durcharbeitest — melde dich. Ich teile, was ich baue und absichere, unter #GoogleCloud, #CloudSecurity und #GoogleCloudAmbassador.


