Ein AWS-Konto, in dem fünf Jahre lang gearbeitet wurde, hat eine Berechtigungslage, die niemand mehr im Kopf hat. Achtzehn Nutzer, von denen sechs nicht mehr im Unternehmen sind. Vierzig Rollen, von denen ein Dutzend zu Werkzeugen gehört, die abgeschafft wurden. Und irgendwo ein Zugangsschlüssel aus dem Jahr 2019, der noch funktioniert.
Der Grund ist nie Nachlässigkeit. Er ist immer derselbe Satz, der irgendwann unter Zeitdruck gefallen ist: „Gib ihm vorläufig Adminrechte, wir schneiden das später zu." Das Vorläufige hat dann fünf Jahre gehalten.
Dieser Artikel beschreibt das Aufräumen in der Reihenfolge, in der es tatsächlich funktioniert, und mit der Vorsichtsmaßnahme, ohne die es nachts etwas kaputt macht.
Womit man nicht anfängt
Die naheliegende erste Handlung ist, Rechte zu entziehen. Sie ist auch der zuverlässigste Weg, das Vorhaben nach zwei Wochen zu beenden.
Denn was dabei passiert, ist vorhersagbar: Irgendein nächtlicher Lauf, von dem niemand mehr wusste, schreibt nicht mehr in seinen Bucket, fällt drei Tage lang still aus, und am vierten steht die Frage im Raum, ob dieses Aufräumen wirklich sein musste. Danach fasst das Thema ein Jahr lang niemand mehr an.
Die Reihenfolge, die trägt, dreht das um: erst sehen, dann die Schlüssel, dann die Rechte. Die ersten beiden Schritte sind risikoarm und bringen den größeren Teil der Verbesserung.
Sehen, was da ist
AWS beantwortet zwei Fragen von sich aus, und zusammen ergeben sie die ganze Arbeitsgrundlage. Wer kann was? Und wer hat davon etwas benutzt?
Die erste Frage beantwortet der Zugangsbericht, und er ist eine einzige Zeile:
# Eine Zeile je Nutzer: Passwort, Schluessel, letzte Nutzung,
# Alter, MFA. Der beste Einstieg, den AWS von Haus aus hat.
aws iam generate-credential-report
aws iam get-credential-report --query Content --output text \
| base64 -d > zugangsbericht.csv
# Und je Rolle: welche Dienste die Rolle in 400 Tagen
# tatsaechlich angesprochen hat.
aws iam generate-service-last-accessed-details \
--arn arn:aws:iam::123456789012:role/deployDie zweite Frage beantwortet die Nutzungshistorie, und sie ist der eigentliche Schatz. Eine Rolle, die in vierhundert Tagen drei Dienste angesprochen hat, aber Rechte auf dreißig trägt, ist kein Streitfall mehr, sondern eine Rechnung.
Dazu kommt der Zugriffsanalysator, der die Frage beantwortet, die in einem gewachsenen Konto am unangenehmsten ist: Was ist von außerhalb des Kontos erreichbar? Buckets, die vor Jahren für einen Austausch mit einem Partner geöffnet wurden, stehen dort zuverlässig drin.
Das Ergebnis dieses Schritts ist eine Tabelle, kein Umbau. Sie kostet einen Tag und macht aus einer Meinungsfrage eine Faktenlage.
Der Schritt mit dem größten Effekt
Von allem, was man tun kann, hat ein einziger Schritt das beste Verhältnis: langlebige Zugangsschlüssel abschaffen.
Ein Schlüsselpaar in der Datei eines Entwicklers läuft nicht ab, wird nicht protokolliert, wenn es kopiert wird, und überlebt eine Kündigung mühelos. Die weitaus meisten veröffentlichten AWS-Vorfälle beginnen nicht mit einer zu weiten Berechtigung, sondern mit einem solchen Schlüssel in einem Repository oder auf einem Notebook.
Für Menschen ist der Ersatz das Identity Center: Anmeldung über den vorhandenen Verzeichnisdienst, Rollen statt Nutzer, Sitzungen mit Ablauf. Für Maschinen innerhalb von AWS sind es ohnehin Rollen. Bleibt der Fall, der die meisten Schlüssel erzeugt: die Bauläufe außerhalb.
Dafür gibt es seit Jahren einen Weg ohne jedes Geheimnis, und er ist in zwanzig Zeilen eingerichtet:
// GitHub bekommt keine Schluessel, sondern darf sich ausweisen.
// Der Vertrauensanker ist der Token des Laufs, kein Wert im Safe.
data "aws_iam_policy_document" "vertrauen" {
statement {
actions = ["sts:AssumeRoleWithWebIdentity"]
principals {
type = "Federated"
identifiers = [aws_iam_openid_connect_provider.github.arn]
}
// Ohne diese Bedingung darf JEDES Repository auf GitHub
// diese Rolle annehmen. Das ist der haeufigste Fehler
// beim Umstieg, und er faellt nicht auf, weil alles geht.
condition {
test = "StringEquals"
variable = "token.actions.githubusercontent.com:sub"
values = ["repo:firma/shop:ref:refs/heads/main"]
}
}
}Die Bedingung auf das Repository und den Zweig ist der Punkt, an dem dieser Umstieg entweder eine Verbesserung oder eine Verschlechterung ist. Ohne sie hat man einen Schlüssel durch eine Rolle ersetzt, die die halbe Welt annehmen darf.
Woher ein Schlüssel benutzt wird, bevor man ihn abschaltet
An dieser Stelle steht regelmäßig ein Schlüssel im Weg, bei dem niemand sagen kann, wer ihn verwendet. Er gehört einem Nutzer namens deploy, er ist vier Jahre alt, und die Antwort auf die Frage nach dem Besitzer lautet: „Den brauchen wir bestimmt noch."
Man muss das nicht raten. Das Protokoll weiß es, und die Abfrage dauert eine Minute:
# Wer hat mit genau diesem Schluessel gearbeitet, und von wo?
# Die Quell-IP im Ergebnis beantwortet die Besitzfrage meistens
# schneller als jede Rundmail.
aws cloudtrail lookup-events --lookup-attributes AttributeKey=AccessKeyId,AttributeValue=AKIA... --start-time "$(date -d '90 days ago' -Iseconds)" --query 'Events[].{zeit:EventTime,was:EventName,quelle:Username}'Drei Ergebnisse sind möglich, und alle drei sind gut. Kommt nichts zurück, wird der Schlüssel deaktiviert, nicht gelöscht: Deaktivieren ist in einer Sekunde umkehrbar, Löschen nicht. Kommt eine bekannte Adresse zurück, ist der Fall zugeordnet. Und kommt eine unbekannte zurück, hat das Aufräumen gerade seinen Anlass gefunden.
Die Zwischenstufe ist dabei das eigentliche Werkzeug: Ein deaktivierter Schlüssel, der vier Wochen lang niemandem fehlt, wird ohne Diskussion gelöscht. Das ist derselbe Gedanke wie beim Abschalten eines Zwischenspeichers, nur mit weniger Aufregung.
Rechte zurücknehmen, ohne nachts etwas umzuwerfen
Jetzt erst die Rechte, und auch hier gibt es eine Reihenfolge, die den Unterschied macht.
Zuerst das, was nie benutzt wurde. Die Nutzungshistorie liefert es frei Haus: Dienste, die eine Rolle in vierhundert Tagen nicht angesprochen hat, kann sie verlieren. Das ist der große, langweilige Teil, und er trägt kein Risiko, das eine Zahl nicht schon beantwortet hätte.
Dann die Ausnahmen mit Ablaufdatum. Jede Rolle, die aus einem konkreten Anlass mehr darf, bekommt den Anlass als Merkmal und ein Datum. Ohne Datum wird aus jeder Ausnahme wieder der Zustand, aus dem dieses Aufräumen entstanden ist.
Und nie ohne Rückweg. Jede Rücknahme wird in einem Zeitfenster gemacht, in dem jemand zusieht, und mit einer Änderung, die sich in einer Minute umkehren lässt. Ein Runbook mit drei Zeilen reicht: was geändert wurde, wie man es zurücknimmt, wen man ruft.
Was dabei nicht entsteht, ist eine perfekte Zuschneidung. Zweihundert einzeln zugeschnittene Berechtigungen sind nach einem Jahr genauso unverständlich wie das, was vorher da war, nur mit mehr Zeilen. Die verbreitete Auslegung von minimalen Rechten als Feinarbeit an jeder einzelnen Aktion ist der Grund, warum solche Vorhaben stecken bleiben.
Damit das Ergebnis nicht bei der nächsten Handarbeit an der Konsole zerfällt, gehört es in Code: Terraform-Drift.
Die Grenze oben ist stärker als die Regel unten
Das, was dauerhaft trägt, sitzt nicht an den einzelnen Rollen, sondern darüber.
Eine Dienstkontrollregel auf der Organisation sagt nicht, wer etwas darf, sondern was in diesem Konto niemand darf, auch der Administrator nicht. Drei oder vier solcher Sätze ersetzen Dutzende von Einzelregeln und bleiben über Jahre verständlich: keine Region außerhalb Europas, kein Abschalten des Protokolls, kein Löschen der Protokolldateien, kein Öffnen von Buckets nach außen.
Der Unterschied ist nicht theoretisch. Eine Berechtigung an einer Rolle wird beim nächsten Vorfall unter Druck erweitert, und niemand erinnert sich daran. Eine Grenze auf der Organisation muss man an einer Stelle ändern, an der es auffällt.
Dieselbe Ordnung gilt für die Konten selbst: Getrennte Konten für Produktion und Entwicklung sind die wirksamste Berechtigungsgrenze, die AWS anbietet, weil sie nicht auf Regeln beruht, sondern auf Trennung. Wer an dieser Stelle ohnehin aufräumt, sollte sie gleich mitziehen; das ist das Thema Landing Zone.
Dieselbe Übung eine Ebene höher, in der Anwendung selbst, steht in Zehn Lücken, die in jedem alten PHP-System stecken.
Eine Woche, und was danach bleibt
In der Praxis lässt sich das Beschriebene in einer Woche auf einen guten Stand bringen, und die Aufteilung ist regelmäßig dieselbe.
Ein Tag Inventur, zwei Tage für den Umstieg der Bauläufe und die Abschaltung der langlebigen Schlüssel, ein Tag für das Entfernen dessen, was nachweislich nie benutzt wurde, ein Tag für die drei oder vier Grenzen oben. Danach ist das Konto nicht perfekt zugeschnitten, aber es hat keine unbeaufsichtigten Schlüssel mehr, keine Rechte für Leute, die gegangen sind, und keinen Weg, das Protokoll abzuschalten.
Was bleibt, ist eine einzige Gewohnheit, und sie entscheidet, ob der Zustand hält: Jede Ausnahme bekommt ein Datum. Nicht eine Begründung, nicht einen Antrag, ein Datum. Alles andere wächst wieder zu. Wie ich Konten und Zugriffe ordne, steht auf einer eigenen Seite.
Dieser Artikel gehört zu einer Reihe über Systeme, die es schon gibt. Der Rückblick ordnet alle Artikel der Reihe nach Anlass.

