Cloud Guardrails statt Cloud Gatekeeping
Warum zentrale Freigabeprozesse in der Cloud Schatten-Infrastruktur züchten und wie technisch durchgesetzte Leitplanken – SCPs, Permission Boundaries, Config Rules, Budgets – Teams Bewegungsfreiheit zurückgeben.
Ein Freigabeprozess mit drei Tagen Wartezeit erzeugt keine Kontrolle. Er erzeugt Schatten-Infrastruktur. Das Team, das auf die Freigabe für eine DynamoDB-Tabelle wartet, wartet nicht wirklich – es weicht aus: auf den privaten AWS-Account eines Kollegen, auf die Kreditkarte des Projektleiters, auf den „temporären" Bucket im Sandbox-Account, der zwei Jahre später immer noch produktive Daten enthält. Der Prozess, der Risiken verhindern sollte, verschiebt sie genau dorthin, wo niemand mehr hinschaut: kein Review, kein zentrales Logging, keine Kostenkontrolle, keine Backups.
In einem Projekt im letzten Jahr habe ich das aus der Nähe gesehen. Ein zentrales Cloud-Team, ein Jira-Board, jede neue Ressource ein Ticket. Offizielle Durchlaufzeit: drei Tage. Tatsächliche Durchlaufzeit bei Rückfragen: eine Woche und mehr. Das Cloud-Team war weder böswillig noch faul – es war schlicht zu klein für zwölf Produktteams, und jedes Ticket brauchte Kontext, den nur das anfragende Team hatte. Mir ist dabei aufgefallen, dass die eigentliche Diskussion nie „erlauben oder verbieten" war. Sie war fast immer: „In welcher Region? Mit welchem Tagging? Wer zahlt das?" Also Fragen, die sich als Regel formulieren lassen. Und was sich als Regel formulieren lässt, kann eine Maschine durchsetzen – ohne Wartezeit.
Was der Ticket-Prozess eigentlich schützt
Bevor man den Prozess abschafft, lohnt der Blick darauf, welche berechtigten Sorgen er trägt. In dem Projekt waren es im Kern vier:
- Kosten: Niemand soll versehentlich einen teuren Cluster in einer exotischen Instanzgröße starten.
- Datenhoheit: Daten bleiben in europäischen Regionen, Punkt.
- Sicherheitsgrundlinien: Kein öffentlicher S3-Bucket, kein abgeschaltetes CloudTrail, keine weit offenen Security Groups.
- Rechte-Eskalation: Wer Rollen anlegen darf, darf sich sonst beliebig weitreichende Rechte selbst ausstellen.
Alle vier Sorgen sind real. Aber keine davon braucht einen Menschen, der drei Tage später ein Ticket liest. Sie brauchen Grenzen, die zum Zeitpunkt des API-Aufrufs gelten – nicht zum Zeitpunkt der Freigabe. Der Ticket-Prozess prüft eine Absichtserklärung; was danach wirklich passiert, prüft er nie wieder. Die Leitplanke prüft jede einzelne Aktion, jedes Mal.
AWS selbst hat für dieses Denken inzwischen einen Namen: Control Tower, seit Sommer 2019 allgemein verfügbar, nennt seine vorkonfigurierten Regeln wörtlich „Guardrails". Der Begriff ist also kein Schlagwort von Konferenzfolien, sondern kommt aus der Produktpraxis der Plattform. Interessanter als der Name ist die Unterscheidung, die Control Tower dabei trifft: preventive Guardrails verhindern eine Aktion, detective Guardrails erkennen eine Abweichung. Diese Unterscheidung trägt die gesamte Architektur.
flowchart LR
A[Team ändert<br/>Infrastruktur] --> B{Präventiv:<br/>SCP / Boundary}
B -->|verstößt| C[API-Aufruf sofort<br/>abgelehnt]
B -->|erlaubt| D[Ressource<br/>entsteht]
D --> E{Detektivisch:<br/>AWS Config}
E -->|konform| F[Nichts passiert]
E -->|Abweichung| G[Alarm oder<br/>Auto-Korrektur]
Harte Grenzen: Service Control Policies
Die äußerste Leitplanke sind Service Control Policies in AWS Organizations. Eine SCP gilt für ganze Accounts oder Organizational Units und schlägt jede IAM-Policy im Account – selbst der Account-Administrator kommt nicht daran vorbei. Genau deshalb gehören hier nur die Regeln hinein, die wirklich nie verhandelbar sind. In dem Projekt waren das zwei: erlaubte Regionen und eine kurze Liste verbotener Services.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyOutsideAllowedRegions",
"Effect": "Deny",
"NotAction": [
"iam:*",
"organizations:*",
"route53:*",
"cloudfront:*",
"support:*"
],
"Resource": "*",
"Condition": {
"StringNotEquals": {
"aws:RequestedRegion": ["eu-central-1", "eu-west-1"]
}
}
},
{
"Sid": "DenyUnapprovedServices",
"Effect": "Deny",
"Action": ["gamelift:*", "robomaker:*", "chime:*"],
"Resource": "*"
}
]
}
Zwei Details daran sind mir wichtig. Erstens die NotAction-Ausnahmen: IAM, CloudFront oder Route 53 sind globale Services, ihre API-Aufrufe laufen technisch über us-east-1 – wer sie in der Regionssperre vergisst, sperrt sich selbst aus. Das haben wir auf die harte Tour gelernt, zum Glück in einem Test-Account. Zweitens der Charakter der Liste verbotener Services: Sie ist kurz und begründet, keine Positivliste von hundert Einträgen. Eine SCP, die alles verbietet außer dem, was jemand einmal beantragt hat, ist nur das alte Gatekeeping in JSON-Form. Die Leitplanke markiert den Rand der Straße, nicht die einzige erlaubte Spur.
Wichtig für das Verständnis: Eine SCP vergibt selbst keine Rechte. Sie definiert die Obergrenze dessen, was IAM-Policies im Account überhaupt wirksam erlauben können. Innerhalb dieser Grenze bewegen sich die Teams frei – und genau das ist der Punkt.
Selbstbedienung bei IAM: Permission Boundaries
Die unbequemste Frage kam in dem Projekt vom Sicherheitsverantwortlichen: „Wenn Entwickler eigene IAM-Rollen anlegen dürfen, können sie sich dann nicht beliebige Rechte geben?" Ohne Gegenmaßnahme: ja. Wer iam:CreateRole und iam:AttachRolePolicy hat, kann eine Rolle mit Administratorrechten bauen und sie übernehmen. Deshalb war IAM historisch das letzte Revier des zentralen Teams – und der häufigste Grund für Wartezeit, denn jede Lambda-Funktion, jede Pipeline braucht eine Rolle.
Seit 2018 gibt es dafür Permission Boundaries: eine Managed Policy, die als Obergrenze an eine Rolle geheftet wird. Die effektiven Rechte sind der Schnitt aus Boundary und angehefteten Policies. Der Trick liegt in der Selbstverstärkung – die Boundary erlaubt das Anlegen neuer Rollen nur dann, wenn die neue Rolle wieder dieselbe Boundary trägt:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowWorkloadServices",
"Effect": "Allow",
"Action": [
"s3:*", "dynamodb:*", "lambda:*", "sqs:*",
"logs:*", "cloudwatch:*", "apigateway:*"
],
"Resource": "*"
},
{
"Sid": "DenyCreateWithoutBoundary",
"Effect": "Deny",
"Action": ["iam:CreateRole", "iam:CreateUser"],
"Resource": "*",
"Condition": {
"StringNotEquals": {
"iam:PermissionsBoundary": "arn:aws:iam::111122223333:policy/workload-boundary"
}
}
},
{
"Sid": "DenyBoundaryTampering",
"Effect": "Deny",
"Action": [
"iam:DeleteRolePermissionsBoundary",
"iam:DeletePolicy",
"iam:CreatePolicyVersion"
],
"Resource": "arn:aws:iam::111122223333:policy/workload-boundary"
}
]
}
Damit konnten die Teams Rollen für ihre Services anlegen, ohne dass jemand freigeben musste – aber keine Rolle konnte je mehr als die workload-boundary erlaubt. Im CDK, mit dem wir die Infrastruktur ohnehin beschrieben haben, ist das eine Zeile pro Rolle:
import * as iam from '@aws-cdk/aws-iam';
const boundary = iam.ManagedPolicy.fromManagedPolicyName(
this,
'WorkloadBoundary',
'workload-boundary'
);
const serviceRole = new iam.Role(this, 'OrderServiceRole', {
assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
permissionsBoundary: boundary,
});
Die drei Tage Wartezeit für „bitte eine Rolle für den Order-Service" waren damit weg. Nicht, weil jemand schneller freigab, sondern weil es nichts mehr freizugeben gab: Die Grenze war schon gezogen, bevor die Anfrage entstand.
Erkennen statt nur verhindern: AWS Config
Präventive Regeln haben eine Lücke: Sie können nur verbieten, was sich als IAM-Bedingung ausdrücken lässt. „Kein S3-Bucket ohne Verschlüsselung" oder „keine Security Group mit 0.0.0.0/0 auf Port 22" sind Eigenschaften einer Ressource, keine API-Aktionen – dafür braucht es die zweite Sorte Leitplanke. AWS Config zeichnet den Zustand aller Ressourcen auf und prüft ihn laufend gegen Regeln. Bei Abweichung: Alarm, und wo es gefahrlos möglich ist, automatische Korrektur.
import * as config from '@aws-cdk/aws-config';
import * as budgets from '@aws-cdk/aws-budgets';
new config.ManagedRule(this, 'BucketsNotPublic', {
identifier: 'S3_BUCKET_PUBLIC_READ_PROHIBITED',
});
new config.ManagedRule(this, 'VolumesEncrypted', {
identifier: 'ENCRYPTED_VOLUMES',
});
new budgets.CfnBudget(this, 'TeamMonthlyBudget', {
budget: {
budgetType: 'COST',
timeUnit: 'MONTHLY',
budgetLimit: { amount: 500, unit: 'USD' },
},
notificationsWithSubscribers: [
{
notification: {
notificationType: 'ACTUAL',
comparisonOperator: 'GREATER_THAN',
threshold: 80,
},
subscribers: [
{ subscriptionType: 'EMAIL', address: 'team-orders@example.com' },
],
},
],
});
Das Budget gehört für mich in dieselbe Kategorie, auch wenn es technisch ein anderer Service ist. Die Kostenangst war der emotionale Kern des alten Freigabeprozesses – und ein Budget-Alarm bei 80 Prozent beantwortet sie besser als jedes Ticket, weil er auf tatsächliche Ausgaben reagiert statt auf geschätzte. In dem Projekt bekam jedes Team sein eigenes Budget mit eigener Benachrichtigung. Die Diskussionen über Kosten wurden dadurch nicht weniger, aber sie fanden mit Zahlen statt mit Vermutungen statt, und sie fanden im Team statt – nicht zwischen Team und Torwächter.
Der zweite Gewinn von Config ist unscheinbarer: die Historie. Wenn eine Security Group heute anders aussieht als gestern, zeigt Config, wann sich was geändert hat – das hat uns bei mehr als einer Fehlersuche Stunden gespart, ganz unabhängig von den Regeln. Ein Freigabeprozess weiß nur, was beantragt wurde; Config weiß, was tatsächlich passiert ist.
Bei der Auto-Korrektur bin ich zurückhaltender geworden. Eine Regel, die eine öffentliche Bucket-Policy sofort wieder entfernt, ist ein Sicherheitsgewinn. Eine Regel, die nachts Ressourcen löscht, deren Tags nicht stimmen, hat uns einmal einen Morgen Fehlersuche gekostet, weil ein legitimer Prototyp verschwunden war. Erkennen und melden ist fast immer richtig; automatisch eingreifen nur dort, wo die Korrektur garantiert nichts zerstört.
Leitplanken sind Code, keine Klick-Konfiguration
Der Punkt, an dem solche Setups in meiner Erfahrung kippen, ist nicht die Technik, sondern die Pflege. Eine SCP, die jemand vor einem Jahr in der Konsole zusammengeklickt hat, ist genauso undurchsichtig wie der alte Freigabeprozess – nur dass jetzt niemand mehr weiß, warum Port 22 gesperrt ist und wer das ändern darf. Leitplanken sind Infrastruktur, und für Infrastruktur gilt, was ich schon beim Review von Infrastruktur-Code beschrieben habe: Sie gehört versioniert, reviewt und nachvollziehbar geändert.
Konkret heißt das bei uns: SCPs, Boundary-Policies und Config-Rules stehen als Code im Repository, Änderungen laufen als Pull Request, und im Review sitzt neben dem Plattform-Team auch jemand aus einem Produktteam. Der Pull Request ersetzt das Ticket – mit einem entscheidenden Unterschied: Diskutiert wird eine dauerhafte Regel für alle, nicht eine Einzelfreigabe für einen. Die Diskussion lohnt sich also, ihr Ergebnis wirkt weiter. Wie sich solche Regeln zusätzlich schon beim Synthetisieren der eigenen Stacks prüfen lassen, habe ich in Policy as Code für CDK beschrieben – beides zusammen ergibt zwei Prüfpunkte: einen früh im Build, einen unumgehbar an der Konto-Grenze.
Nebenbei: Das Muster ist nicht AWS-spezifisch. Im Kubernetes-Umfeld passiert gerade dasselbe mit Open Policy Agent und Gatekeeper – Regeln als Code, durchgesetzt am Admission Controller statt im Meeting. Wer beide Welten betreibt, erkennt die Denkfigur sofort wieder.
flowchart TD Root[Organization Root] --> Sec[OU Security<br/>Logging, Audit] Root --> Work[OU Workloads] Root --> Sand[OU Sandbox] Work --> A1[Account Team Orders] Work --> A2[Account Team Billing] SCP1[SCP: EU-Regionen,<br/>Service-Sperrliste] -.gilt für.-> Work SCP2[SCP: zusätzlich<br/>Kostengrenzen eng] -.gilt für.-> Sand
Die Organisationsstruktur selbst ist Teil der Leitplanken: Eine Sandbox-OU mit engen Grenzen und einer Aufräum-Regel nimmt dem Experimentieren das Risiko, eine Workload-OU trägt die verbindlichen Regeln. Wer heute mit einer Multi-Account-Struktur anfängt, bekommt mit Control Tower genau dieses Gerüst vorkonfiguriert – die Guardrails dahinter sind dieselben SCPs und Config-Rules, nur fertig verdrahtet.
Was am Ende anders ist
Nach einem halben Jahr mit diesem Setup war das Jira-Board des Cloud-Teams fast leer – nicht weil weniger passierte, sondern weil das meiste keine Freigabe mehr brauchte. Die Tickets, die blieben, waren die interessanten: Anträge, eine Leitplanke zu ändern. Genau da gehört menschliche Aufmerksamkeit hin. Und das Plattform-Team hatte plötzlich Zeit für die Arbeit, die vorher liegen blieb: bessere Vorlagen, schärfere Regeln, ein neuer Account in einer Stunde statt in einer Woche.
Ein paar Beobachtungen nehme ich mit. Der Freigabeprozess und die Leitplanke beantworten dieselbe Frage – „was darf hier passieren?" – aber zu verschiedenen Zeitpunkten: der Prozess einmal vorher auf Basis einer Beschreibung, die Leitplanke bei jeder Aktion auf Basis der Wirklichkeit. Das macht Leitplanken nicht nur schneller, sondern ehrlicher. Zweitens: Die Trennung in präventiv und detektivisch ist keine Feinheit, sondern die zentrale Entwurfsentscheidung. Alles, was hart verboten ist, gehört in SCPs und Boundaries; alles, was Eigenschaften von Ressourcen betrifft, in Config-Rules mit Alarm. Wer versucht, alles präventiv zu erzwingen, baut das Gatekeeping in JSON nach. Und drittens: Schatten-Infrastruktur ist kein Disziplinproblem der Teams, sondern ein Symptom. Sie verschwindet nicht durch strengere Prozesse, sondern durch einen offiziellen Weg, der schneller ist als der Umweg.
Kommentare