Wir zeigen mehrere Wege, Microservices untereinander zu authentifizieren – vom einfachsten, aber unsichersten und schwer zu pflegenden Ansatz bis zu den empfohlenen Architekturen.

In einer Microservice-Architektur dreht sich alles um Services, die einander aufrufen. Aber wie hält man das sicher? Wie stellt man sicher, dass nur die eigenen Services die jeweils anderen aufrufen können? Wie also authentifizieren?
Aus Browser-Anwendungen kennen Sie das Prinzip: Der Nutzer gibt seine Zugangsdaten ein, der Browser schickt sie an den Service, und dieser sendet ein Session-Token zurück, das den Nutzer für eine bestimmte Zeit authentifiziert. Bei einem Microservice gibt es jedoch keinen Menschen, der ein Passwort oder einen Multi-Faktor-Schlüssel eingeben könnte.
Im Folgenden beschreibe ich, wie das geht – mit Fokus auf Server, die auf Diensten der Google Cloud Platform wie Cloud Run oder Google Kubernetes Engine (GKE) laufen. Die Client-Services können in GCP, on-premises oder in AWS betrieben werden.
Anlass für diesen Artikel ist, dass sich API Gateway und Cloud Endpoints rasant weiterentwickeln und über starke Authentifizierungsfunktionen verfügen, im Vergleich zueinander und zu konkurrierenden Diensten aber auch ihre Grenzen haben. Es gibt viele Wege, Microservices untereinander zu authentifizieren. Ich beginne mit dem einfachsten, aber unsichersten und am schlechtesten zu wartenden Ansatz und arbeite mich zu den empfohlenen Architekturen vor.
Der Übersichtlichkeit halber konzentriere ich mich auf Microservices, bei denen Sie beide Seiten unter Kontrolle haben. Dieselben Prinzipien gelten aber auch, wenn der Client-Service außerhalb Ihrer Organisation liegt.
## **Die Grundlagen: Header, Keys und Proxys**
Auch wenn es einfachere und kompliziertere Wege gibt: Authentifizierung zwischen Services will sehr sorgfältig durchdacht sein. Der grundsätzliche Ablauf sieht so aus:
- Der Client signiert ein Token mit einem geheimen Schlüssel.
- Das Standardformat dafür ist der [JSON Web Token](https://jwt.io/).
- Das Token wird im HTTP-Authorization-Header übergeben:
```
Authorization: Bearer
```
wobei das base64-codierte Token ist.
- Der Server validiert das Token, indem er einen Service abfragt. Alternativ nimmt ein Reverse Proxy die Anfrage entgegen und validiert das Token, bevor er sie an den eigentlichen Server weiterreicht.
- In GCP stellt die Plattform diesen Service bereit.
Dieser Artikel zeigt mehrere Wege dorthin – von einfach und unsicher bis hin zu vollständigeren Lösungen.
## **Zu einfach: Selbstverwaltete "API-Keys"**
Eine simple Lösung, oft genutzt von Teams, die das ganze Spektrum der Möglichkeiten nicht kennen, ähnelt dem Login per Benutzername und Passwort: Man hinterlegt im Client-Service eine geheime Zeichenkette – einen "API-Key" – als Zugangsdaten und prüft diese serverseitig.
```
APIKEY=Microservice1:78eb9a45897f
```
## **Grenzen dieses Ansatzes**
Sicher ist das nicht.
**Keys, die nach außen gelangen**
Keys gelangen auf mehr Wegen nach außen, als man sich ausmalen kann.
Um das zu verhindern, sollten Sie Geheimnisse nicht in Git oder einer anderen Versionskontrolle ablegen – das führt viel zu schnell zu einer versehentlichen Offenlegung. Nutzen Sie stattdessen einen Secret-Manager-Dienst wie Google Cloud Secret Manager oder Hashicorp Vault. Das Grundproblem bleibt allerdings: Der Client-Service muss Zugangsdaten speichern, um auf den Secret Manager zuzugreifen.
**Key-Management**
Sie brauchen serverseitig eine Datenbank zur Speicherung der Keys und eine Schicht, die einen empfangenen Key auf Korrektheit prüft. Damit auch aus dieser Schicht keine Keys nach außen dringen, sollte der Client nicht den API-Key selbst übergeben, sondern nur einen Hash, den der Server gegen einen gespeicherten Hash abgleicht. Das alles zu pflegen ist aufwendig. Und unsicher obendrein, denn Sie können kaum genug Know-how und Zeit investieren, um wirklich alle Lücken zu schließen. Sicherheitssysteme gehören – wo immer möglich – in die Hände von Spezialisten.
**Rotation**
Da Lecks unvermeidlich sind, gehört regelmäßiges Rotieren des Keys zur Best Practice: einen neuen erzeugen und den alten nach einer gewissen Zeit ungültig machen. Das verlangt clientseitig einen automatisierten Mechanismus, der einen neuen Key anfordert (und diese Anforderung mit dem alten Key authentifiziert!). Serverseitig brauchen Sie einen Mechanismus, der bei Bedarf neue Keys ausstellt und alte zu einem festgelegten Zeitpunkt invalidiert.
Solche Dinge sind immer komplizierter, als man zunächst denkt: Sie wollen etwa eine maximale Anzahl gleichzeitig gültiger Key-Versionen erzwingen, denn zwei gültige Versionen sind für die Rotation nötig, hundert davon sind ein Leck mit Ansage.
## **Service-Account-Keys des Cloud-Providers**
Warum Hashing, Validierung, Rotation und Ablauflogik selbst bauen? Ein deutlich besserer Weg ist, einen Service Account anzulegen und eine Key-Datei aus Google Cloud Identity and Access Management (IAM) herunterzuladen. Den Key erstellen Sie auf der [Service-Account-Seite](https://console.cloud.google.com/iam-admin/serviceaccounts).


Laden Sie die JSON-Datei herunter und setzen Sie unbedingt ein Ablaufdatum – [eine neue Funktion in Google Cloud.](https://cloud.google.com/iam/docs/service-accounts#key-expiry) Das JSON sieht etwa so aus. (Keine Sorge, ich habe den Text gründlich geschwärzt 😁, und den Key habe ich ohnehin längst deaktiviert!)
```
{
"type": "service_account",
"project_id": "myproject",
"private_key_id": "ded9d97108b…..5cfd179e95e0e1",
"private_key": " — — -BEGIN PRIVATE KEY — — -\nMIIEvKIBADABNBg….QDA6woGjE4Q — — -END PRIVATE KEY — — -\n",
"client_email": "[email protected]",
"client_id": "106482...4210366919",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/kubeflowpipeline%40joshua-playground.iam.gserviceaccount.com"
}
```
## **Grenzen dieses Ansatzes**
Klingt gut, oder? Doch (Sie ahnen es bereits) auch das ist nicht so sicher und komfortabel, wie wir es gern hätten.
Genau wie selbstgebaute API-Keys kann auch eine Service-Account-Key-Datei nach außen gelangen, also müssen Sie sie rotieren. Google hilft Ihnen, Keys zuverlässig ablaufen zu lassen, und stellt APIs bereit, um neue zu beziehen – nutzen müssen Sie diese Funktionen aber selbst.
Später erklären wir, wie Sie ganz auf eine Key-Datei verzichten, indem Sie den Service Account direkt in Ihre Client-Anwendungen integrieren. Doch zunächst klären wir, was Sie mit dem Service Account anfangen – egal, ob über die Key-Datei oder die fest integrierte Variante.
## **Authentifizierung im Anwendungscode**
Zur Authentifizierung verwendet der Client-Service seinen Service Account.
Das geschieht [über OpenID Connect (OIDC)](https://cloud.google.com/endpoints/docs/openapi/service-account-authentication), wobei signierte JSON Web Tokens (JWT) übergeben werden. Diese Tokens [sind nur kurz gültig](https://cloud.google.com/iam/docs/creating-short-lived-service-account-credentials) – Stunden, nicht Wochen –, was das Risiko bei einem Leak minimiert.
Sie können das in Ihrem eigenen Code umsetzen, mit Software-Bibliotheken auf Client- und Serverseite.
- Zunächst signiert der Client mithilfe [einer Software-Bibliothek](https://developers.google.com/identity/protocols/oauth2/service-account) und des Service-Account-Keys ein JWT zur Anforderung des Zugriffs.
- Mit diesem Token fordert er beim Google-Authentifizierungsserver ein weiteres Token an – ein Access-Token. Der Authentifizierungsserver prüft, ob der Service Account das Anfrage-JWT tatsächlich signiert hat, und schickt daraufhin ein Access-Token zurück, das dies bestätigt.
- Mit diesem Access-Token ruft der Client Ihren Microservice auf.
- Ihr Microservice prüft mithilfe einer Software-Bibliothek, ob das Access-Token tatsächlich vom Google-Service validiert und signiert wurde.
[Der Ablauf entspricht weitgehend diesem hier](https://developers.google.com/identity/protocols/oauth2#serviceaccount) – mit dem Unterschied, dass nicht eine Google-API aufgerufen wird, sondern Ihr eigener Microservice.

## **Grenzen dieses Ansatzes**
**Kopplung mit Ihrem Server**
Diese Lösung verlangt Code in Ihrem Server. Da Sie womöglich mehrere Microservices mit denselben Anforderungen betreiben, müssen Sie diese Authentifizierungsschicht in mehreren Codebasen pflegen und absichern.
Später zeigen wir, wie Sie diesen Code aus Ihrer Anwendung heraushalten. Doch zunächst sehen wir uns an, wie sich Key-Dateien ganz vermeiden lassen.
## **Service Accounts direkt integrieren: GCP**
Wenn Sie den Client-Service in GCP betreiben, sollten Sie keinen Service-Account-Key verwenden. Starten Sie den Service stattdessen mit einem fest hinterlegten Service Account.
Bei einer Google-Compute-Engine- (GCE-)Instanz nutzen Sie etwa
```
gcloud compute instances create [INSTANCE_NAME] --service-account [SERVICE_ACCOUNT_EMAIL] ...
```
um den Service Account anzugeben; analog funktioniert das für Cloud Run und andere GCP-Dienste, von denen aus Ihr Client-Microservice weitere Microservices aufruft.
Da es keine Key-Dateien gibt, müssen Sie sich auch keine Sorgen um Leaks machen. Stattdessen erzeugt der [Metadata-Server](https://cloud.google.com/compute/docs/instances/verifying-instance-identity) ein signiertes Instance-Token, das die Identität des Service Accounts bestätigt. (Und die Anfrage an den Metadata-Server verlässt nie die physische Instanz, auf der die VM läuft.)
## **In Kubernetes**
Kubernetes hat ein eigenes System von Service Accounts, das in einem Kubernetes-spezifischen Authentifizierungsmechanismus aufgeht. Dieses ist von der GCP-IAM-Schicht getrennt. Läuft Ihr Client-Service also auf Google Kubernetes Engine, nutzen Sie [Workload Identity](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity), um Ihrer Kubernetes-Schicht einen GCP-IAM-Service-Account zuzuordnen. Workload Identity fängt alle Aufrufe von GKE an GCP-APIs transparent ab, leitet sie als Proxy weiter und reichert sie mit dem Access-Token an.
Läuft Ihr Client auf AWS Elastic Kubernetes Service, können Sie ebenfalls eine IAM-Rolle zuweisen, um an GCP-Flows teilzunehmen – wie im nächsten Abschnitt beschrieben.
## **Rollen direkt integrieren: AWS und Workload Identity Federation**
Läuft der Client-Service in AWS, können Sie ihn nicht mit einem GCP-Service-Account starten – wohl aber mit dem AWS-Pendant, der Role. Sie starten Ihre Lambda mit [einer Execution Role](https://docs.aws.amazon.com/lambda/latest/dg/lambda-intro-execution-role.html) oder Ihre EC2-Instanz mit einer [Role (verpackt in einem "Instance Profile")](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use_switch-role-ec2.html).
GCP kann dieser Rolle nicht direkt vertrauen. Daher schlagen Sie mit [Workload Identity Federation](https://cloud.google.com/iam/docs/workload-identity-federation) (WIF) eine Brücke zwischen AWS und GCP ([Artikel](https://medium.com/google-cloud/keyless-api-authentication-launching-gcp-workloads-from-aws-b715b4e6c99a)).
Der Ablauf sieht so aus:
- Zunächst signiert der Client-Service in AWS mithilfe seiner Rolle ein Token (Token 1).
- Mit Token 1 fordert er ein zweites, von AWS IAM signiertes Token an (Token 2).
- Mit Token 2 bittet er GCP WIF um die Signatur eines Access-Tokens (Token 3). Google WIF ist so vorkonfiguriert, dass es der angegebenen AWS-Rolle vertraut. Da AWS bestätigt hat, dass die Anfrage von dieser Rolle stammt, signiert WIF das Access-Token (Token 3) und schickt es zurück.
- Der Client-Service nutzt Token 3 nun genauso, wie ein GCP-basierter Client-Service ein Access-Token verwenden würde; ab hier verläuft alles identisch.

Wirkt umständlich, erspart Ihnen aber, geheime und leicht durchsickernde Zeichenketten kreuz und quer durchs Internet zu schicken – in diesem Fall sogar in eine andere Cloud.
## **Authentifizierung von Google an einen AWS-Workload**
Dieser Artikel dreht sich vorrangig um Services, die in Google laufen, doch [gtoken](https://github.com/doitintl/gtoken) verdient hier eine kurze Erwähnung. Es ist das Gegenstück zur Workload Identity Federation: Es authentifiziert einen GKE-Workload für Aufrufe an AWS-APIs, indem es dem Aufruf eine temporäre AWS-Identität verleiht.
## **Authentifizierungs-Proxy: API Gateway**
Wie eingangs erwähnt, bleibt eine Einschränkung: Der letzte Schritt – die Validierung, dass die Signatur tatsächlich vom Google-autorisierten Principal stammt – läuft in Ihrem eigenen Anwendungscode. Besser ist es, wo immer möglich auf bewährte, fertig produktisierte Systeme von Sicherheitsexperten zurückzugreifen.
Werfen Sie deshalb einen Blick auf [API Gateway](https://cloud.google.com/api-gateway) – eine robuste, konfigurierbare Schicht für die [Service-zu-Service-Authentifizierung](https://cloud.google.com/api-gateway/docs/authenticate-service-account), die Sie nicht selbst pflegen müssen.
API Gateway arbeitet als Proxy. Es stellt eine öffentliche Adresse bereit und sitzt zwischen dem Client und Ihrem Serverless-Service auf Cloud Run, Cloud Functions oder App Engine. Es nimmt das Token entgegen, ruft Google-Dienste zur Authentifizierung der Anfrage auf und reicht sie dann an Ihr Serverless-Backend weiter. Um die Verbindung vom API Gateway zum Backend abzusichern, fügt Google einen speziellen Header ein, den nur Google selbst kontrolliert und den kein Angreifer setzen kann.
## **Grenzen dieses Ansatzes**
API Gateway funktioniert allerdings nicht mit GKE, da es eng mit den Schnittstellen der von Google gemanagten Serverless-Dienste verzahnt ist.
## **Authentifizierungs-Proxy: Cloud Endpoint**
Wie also authentifizieren Sie für GKE und sichern dabei trotzdem die Strecke von der Authentifizierungsschicht zum Backend-Service ab? Dafür verwenden Sie den Extensible Service Proxy mit Google Cloud Endpoints – einen etwas älteren Dienst, auf dem API Gateway aufbaut und den es erweitert.
ESP (mittlerweile in v2) ist ein Container, der eine öffentliche Adresse bereitstellt und Anfragen authentifiziert. Für GKE [deployen Sie ihn als Pod](https://cloud.google.com/endpoints/docs/openapi/get-started-kubernetes-engine-espv2) in Ihrem Cluster. (Übrigens: Die Dokumentation nennt zwar nur die neueren VPC-native-/IP-Alias-Cluster, ESP funktioniert aber auch mit den älteren routenbasierten Clustern.)
Auch die Verbindung zwischen ESPv2 und Ihren Kubernetes-Services innerhalb des Clusters muss abgesichert sein. Das gelingt auf Cluster-Netzwerkebene, indem Sie außer dem ESP keine öffentlichen Adressen exponieren, oder mit ausgefeilteren Lösungen wie mutual TLS oder [Istio Security](https://istio.io/latest/docs/concepts/security/).
Für noch mehr Sicherheit deployen Sie ESPv2 als Sidecar, sodass Proxy und Anwendung (Kubernetes Deployment) gemeinsam im sicheren "localhost"-Bereich eines Pods laufen. (Auch wenn das nicht der primäre Deployment-Modus für ESPv2 ist, wird er [in diesem YAML-Rezept](https://github.com/GoogleCloudPlatform/endpoints-samples/blob/master/gke/echo.yaml) auf dem offiziellen Google-Cloud-GitHub-Account unterstützt.)
## **Fazit: Sichern Sie Ihre Microservices ab!**
Nicht jeder darf Ihre APIs aufrufen. Früher löste man das mit Netzwerkgrenzen, in der Cloud mit VPCs. Doch moderne Architekturen integrieren über Cloud-Konten hinweg, zwischen Cloud-Anbietern und mit Nicht-Cloud-Systemen. Und selbst innerhalb der VPC wollen Sie eine zusätzliche Sicherheitsschicht, die genau auf die einzelne Client-zu-Server-Verbindung zugeschnitten ist: Jeder Endpunkt muss dem anderen vertrauen.
Die Herausforderung lautet also:
- Authentifizieren, ohne dass sensible Dateien herumliegen, die nach außen dringen können.
- Authentifizierung an vertrauenswürdige Dienste delegieren – und sie nicht mit Anwendungscode verzahnen.
In diesem Artikel habe ich einige Wege dahin beschrieben, mit zunehmender Sicherheit und Wartbarkeit, aber auch mit dem Bedarf, sich in immer mehr Technologien einzuarbeiten. Diese Einarbeitung lohnt sich allemal – und ist deutlich günstiger, als einem Hack zum Opfer zu fallen.