IMDSv2 erzwingen: Instanz-Credentials vor SSRF schützen

Written in

von

Ein einziger GET aus einer SSRF-Lücke kann reichen, um die Credentials deiner EC2-Instanz auszulesen, solange IMDSv1 aktiv ist. Eine EC2-Instanz mit Instanzprofil bekommt ihre Credentials nicht aus einer Datei, sondern vom Instance Metadata Service (IMDS) unter 169.254.169.254. Im Artikel „AWS IAM ohne Schmerzen: Rollen, Policies und häufige Fehler“ ging es um die Rolle dahinter. Hier geht es um den Weg, auf dem diese Credentials ausgeliefert werden: dort Rollen, hier Härtung.

Das Problem: ein GET reicht

Löst eine Anwendung auf der Instanz Requests zu einer URL aus, die ein Angreifer bestimmt (Server-Side Request Forgery, SSRF), kann diese URL auf die Metadaten-Adresse zeigen. Bei IMDSv1 genügt dafür ein einfacher Request ohne Token, so beschreibt es die AWS-Dokumentation: IMDSv1 ist eine „request/response method“. Unter iam/security-credentials/ liefert der Dienst die temporären Credentials der Instanzrolle, und mit denen arbeitet der Angreifer dann von außen weiter.

Was IMDSv2 ändert

IMDSv2 arbeitet mit Sitzungen. Zuerst schickt der Aufrufer einen PUT auf /latest/api/token und gibt per Header die Lebensdauer des Tokens an (eine Sekunde bis sechs Stunden). Jede weitere Abfrage trägt das Token im Header X-aws-ec2-metadata-token. Ist Token-Pflicht gesetzt, endet jede Abfrage ohne gültiges Token mit 401 (Unauthorized).

Dazu kommen zwei Schutzmechanismen. Ein PUT mit X-Forwarded-For-Header wird abgelehnt, das fängt offene Reverse Proxies ab. Und die Antwort auf den PUT hat standardmäßig ein Hop-Limit von 1, verlässt die Instanz also nicht. Der AWS-Sicherheitsblog nennt als abgedeckte Schwachstellen offene WAFs, offene Reverse Proxies, SSRF und falsch konfigurierte Layer-3-Firewalls. Wer über eine Lücke nur GET-Requests auslösen kann, kann das Token nicht anfordern, denn dafür braucht es den PUT.

Neue Instanzen: Terraform

Im Provider steht die Einstellung im Block metadata_options, für aws_instance und aws_launch_template mit denselben Attributnamen. Weitere typische Fehlkonfigurationen in Terraform-Code stehen in „Infrastructure as Code absichern: die typischen Terraform-Fehlkonfigurationen“.

resource "aws_instance" "app" {
ami = var.ami_id
instance_type = "t3.micro"
metadata_options {
http_endpoint = "enabled"
http_tokens = "required"
http_put_response_hop_limit = 1
}
}
resource "aws_launch_template" "app" {
name_prefix = "app-"
image_id = var.ami_id
instance_type = "t3.micro"
metadata_options {
http_endpoint = "enabled"
http_tokens = "required"
http_put_response_hop_limit = 2 # nur wenn Container auf dem Host laufen
}
}

Beide Blöcke bestehen terraform validate mit dem AWS-Provider 6.66.0. Laut Registry akzeptiert http_tokens die Werte optional und required, das Hop-Limit eine Zahl von 1 bis 64 mit Standard 1. Beim Launch Template steht optional als Standard in der Registry, bei aws_instance nennt sie keinen. Setz den Wert deshalb immer explizit. Amazon Linux 2023 deaktiviert IMDSv1 laut AWS schon ab Werk, aber als Zeile im Code ist die Einstellung reviewbar, als Image-Detail nicht.

Startet eine Auto Scaling Group aus dem Launch Template, gilt die neue Version nur für neue Instanzen. Bestehende bleiben, wie sie sind, bis du sie per API umstellst oder ersetzt.

Bestehende Instanzen prüfen

Welche Instanzen noch beide Versionen zulassen, zeigt describe-instances mit dem Filter aus der AWS-Dokumentation:

aws ec2 describe-instances \
--filters "Name=metadata-options.http-tokens,Values=optional" \
--query 'Reservations[].Instances[].[InstanceId,MetadataOptions.HttpTokens,MetadataOptions.HttpPutResponseHopLimit]' \
--output text

Der Filter gilt pro Region, also für jede genutzte Region wiederholen. Ob etwas auf einer Instanz tatsächlich noch IMDSv1 nutzt, sagt die CloudWatch-Metrik MetadataNoToken im Namespace AWS/EC2. Sie zählt die Zugriffe ohne Token:

aws cloudwatch get-metric-statistics \
--namespace AWS/EC2 --metric-name MetadataNoToken \
--dimensions Name=InstanceId,Value=i-0123456789abcdef0 \
--start-time "$(date -u -d '30 days ago' +%FT%TZ)" --end-time "$(date -u +%FT%TZ)" \
--period 3600 --statistics Sum

Erst wenn die Metrik bei null liegt, ist die Instanz laut AWS bereit für die Umstellung. Der Zeitraum sollte auch deine seltensten Jobs umfassen.

Umstellen

aws ec2 modify-instance-metadata-options \
--instance-id i-0123456789abcdef0 \
--http-tokens required \
--http-endpoint enabled

Wer --http-tokens setzt, muss --http-endpoint enabled mitgeben. Die Änderung wirkt laut AWS sofort und braucht keinen Neustart. Danach zeigt MetadataNoTokenRejected, ob doch noch etwas ohne Token anfragt. Steigt sie, ist entweder die Software zu aktualisieren oder mit --http-tokens optional der Rückweg offen. Beide Metriken schließen sich pro Instanz aus: mit optional sendet nur MetadataNoToken, mit required nur MetadataNoTokenRejected.

Container und Hop-Limit

Die Antwort auf den PUT hat standardmäßig ein Hop-Limit von 1. Für Container hat das eine Folge, die die AWS-Dokumentation ausdrücklich nennt: Der Weg in den Container gilt als zusätzlicher Hop, und bei Hop-Limit 1 kann die Antwort ausbleiben. AWS empfiehlt deshalb, Einstellungen wie die Region direkt in den Container zu reichen oder das Hop-Limit auf 2 zu erhöhen:

aws ec2 modify-instance-metadata-options \
--instance-id i-0123456789abcdef0 \
--http-put-response-hop-limit 2 \
--http-endpoint enabled

Hop-Limit 2 hebt allerdings die Grenze wieder an, die 1 gesetzt hat: Auch ein Container auf dem Host erreicht den Dienst und damit die Rolle des Knotens. Deshalb ist 1 der bessere Wert, solange kein Container das IMDS braucht. Wo Workloads eigene AWS-Rechte brauchen, gehören die nicht in die Rolle des Hosts.

Damit es so bleibt

Für neue Instanzen lässt sich required pro Konto und Region als Standard setzen, und wer die Durchsetzung einschaltet, dem verweigert AWS den Start von Instanzen mit IMDSv1:

aws ec2 modify-instance-metadata-defaults \
--http-tokens required \
--http-tokens-enforced enabled

Das ändert bestehende Instanzen nicht. Bei aktivem Enforcement scheitert außerdem der Rückweg auf optional mit UnsupportedOperation. Und wer ModifyInstanceMetadataOptions oder ModifyInstanceMetadataDefaults aufrufen darf, kann die Einstellung wieder lockern. Dafür nennt AWS IAM- und SCP-Bedingungsschlüssel wie ec2:MetadataHttpTokens.

Grenzen

Erstens: Software, die noch IMDSv1 nutzt, bricht bei required. Die AWS-Dokumentation nennt Mindestversionen der SDKs, etwa 1.11.678 beim AWS SDK for Java 1.x und 1.12.6 bei Boto3. Ältere Stände, Agenten und selbstgebaute Skripte mit curl ohne Token fallen bei Enforcement aus. Deshalb kommt erst die Metrik, dann die Umstellung.

Zweitens: Manche SDKs fallen auf IMDSv1 zurück, wenn der Token-Aufruf keine Antwort bekommt. In Containern mit Hop-Limit 1 zeigt sich das als Verzögerung, nicht als Fehler, und MetadataNoToken steigt dann durch genau diesen Fallback.

Drittens: MetadataNoToken belegt nur, was im Beobachtungszeitraum lief. Ein Job, der einmal im Quartal läuft, erscheint in 30 Tagen nicht.

Viertens: IMDSv2 schützt vor Aufrufen von außen über die Anwendung. Wer auf der Instanz selbst Code ausführt, holt sich das Token wie jeder andere Prozess. Die Rolle bleibt deshalb auf das Nötige begrenzt.

Fünftens: Wer im AMI imds-support v2.0 setzt, setzt damit auch das Hop-Limit 2 und kann den Schritt laut AWS nicht rückgängig machen.

Einordnung

IMDSv2 ist keine neue Schutzschicht um die Rolle, sondern nimmt SSRF den einfachsten Weg an ihre Credentials. Die Umstellung besteht aus vier Schritten: Bestand listen, Nutzung messen, pro Instanz umstellen, Standard und Durchsetzung im Konto setzen.

Fragen oder Anmerkungen zu diesem Setup: mm@mhm-dl.de.

Über das Anmeldeformular für den Newsletter trägst du deine E-Mail-Adresse ein, kreuzt die Einwilligung an und bestätigst die Anmeldung danach per Mail. Mit der Anmeldung willigst du ein, dass der Newsletter unter anderem folgende Themen behandelt: Schulungen zu Docker, Kubernetes, CI/CD, Git-Workflows und DevSecOps-Werkzeugen sowie die Anforderungen aus NIS2, CRA und DORA und deren technische Umsetzung.

Hinterlasse einen Kommentar

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