EC2 Instance-ID per Metadaten abrufen: IMDSv2 vs. IMDSv1
Du schreibst ein Deployment-Skript, das auf dem EC2-Server selbst läuft, und brauchst die Instance-ID — ohne sie hardzukodieren oder aus externen Quellen zu ziehen. Der Instance Metadata Service (IMDS) liefert genau das. Aber wer IMDSv1 noch verwendet, hat eine stille Sicherheitslücke im System, die in realen SSRF-Angriffen bereits ausgenutzt wurde.
TL;DR: IMDSv2 vs. IMDSv1 beim Abruf der EC2 Instance-ID
| Merkmal | IMDSv1 | IMDSv2 |
|---|---|---|
| Authentifizierung | Keine — direkter GET-Request | Session-Token erforderlich (PUT → GET) |
| SSRF-Schutz | Nicht vorhanden | Token-Pflicht blockiert einfache SSRF-Angriffe |
| TTL-Kontrolle | Nicht anwendbar | Token-Lebensdauer konfigurierbar (1–21600 Sek.) |
| AWS-Empfehlung | Veraltet, nicht empfohlen | Empfohlen, erzwingbar per Instance-Konfiguration |
| Abwärtskompatibilität | — | IMDSv2 funktioniert auch wenn IMDSv1 deaktiviert ist |
Wie der Instance Metadata Service funktioniert
Der IMDS ist ein link-lokaler HTTP-Endpunkt, der ausschließlich von innerhalb der EC2-Instanz erreichbar ist. Die IP-Adresse 169.254.169.254 ist eine link-lokale Adresse gemäß RFC 3927 — sie ist nicht routbar und nicht über das Internet erreichbar. Jede Instanz hat ihren eigenen IMDS-Endpunkt, der Metadaten wie Instance-ID, Region, IAM-Rolle und Netzwerkinformationen bereitstellt.
IMDSv1 funktioniert mit einem einfachen GET-Request ohne jede Authentifizierung. Das klingt harmlos, solange der Request von der Instanz selbst kommt. Das Problem: Bei einem SSRF-Angriff (Server-Side Request Forgery) kann eine verwundbare Anwendung auf der Instanz dazu gebracht werden, diesen Request stellvertretend auszuführen — und gibt dabei IAM-Credentials oder andere sensible Metadaten preis.
IMDSv2 löst das durch ein Session-orientiertes Protokoll. Zuerst wird ein kurzlebiger Token per PUT-Request angefordert. Dieser Token muss bei jedem nachfolgenden Metadaten-Request im Header mitgesendet werden. Ein einfacher SSRF-Angriff, der nur GET-Requests erzeugen kann, scheitert an dieser Hürde.
TTL-Header: 21600s IMDS-->>Skript: Session-Token Skript->>IMDS: GET /latest/meta-data/instance-id
Token-Header gesetzt IMDS-->>Skript: i-1234567890abcdef0 Angreifer->>IMDS: GET /latest/meta-data/instance-id
kein Token (nur GET möglich) IMDS-->>Angreifer: HTTP 401 Unauthorized
- PUT /latest/api/token: Die Instanz fordert einen Session-Token an und setzt die gewünschte TTL im Header.
- Token-Rückgabe: IMDS antwortet mit einem temporären Token.
- GET /latest/meta-data/instance-id: Der eigentliche Metadaten-Request, mit Token im Header.
- Instance-ID-Rückgabe: IMDS liefert die Instance-ID zurück.
- SSRF-Versuch (IMDSv2): Ein Angreifer, der nur GET erzeugen kann, erhält keinen Token und damit keinen Zugriff.
Instance-ID per IMDSv2 abrufen — die sichere Methode
Der folgende Ablauf ist der empfohlene Weg für Skripte, die auf EC2-Instanzen laufen. Beide Schritte sind erforderlich — ohne gültigen Token liefert der IMDS-Endpunkt bei erzwungenem IMDSv2 einen HTTP 401-Fehler.
Schritt 1: Session-Token anfordern
TOKEN=$(curl -s -X PUT \
'http://169.254.169.254/latest/api/token' \
-H 'X-aws-ec2-metadata-token-ttl-seconds: 21600')
Der Header X-aws-ec2-metadata-token-ttl-seconds legt die Token-Lebensdauer in Sekunden fest. Der Maximalwert beträgt 21600 (6 Stunden). Für kurzlebige Skripte reichen deutlich kleinere Werte.
Schritt 2: Instance-ID mit Token abrufen
INSTANCE_ID=$(curl -s \
'http://169.254.169.254/latest/meta-data/instance-id' \
-H "X-aws-ec2-metadata-token: $TOKEN")
echo "Instance-ID: $INSTANCE_ID"
Kompaktes Einzeiler-Skript
INSTANCE_ID=$(curl -s \
-H "X-aws-ec2-metadata-token: $(curl -s -X PUT \
'http://169.254.169.254/latest/api/token' \
-H 'X-aws-ec2-metadata-token-ttl-seconds: 21600')" \
'http://169.254.169.254/latest/meta-data/instance-id')
echo "Instance-ID: $INSTANCE_ID"
Python-Variante (boto3-unabhängig)
🔽 Python-Skript anzeigen (IMDSv2)
import urllib.request
def get_instance_id():
# Schritt 1: Token anfordern
token_url = 'http://169.254.169.254/latest/api/token'
token_req = urllib.request.Request(
token_url,
method='PUT',
headers={'X-aws-ec2-metadata-token-ttl-seconds': '21600'}
)
with urllib.request.urlopen(token_req, timeout=2) as resp:
token = resp.read().decode('utf-8')
# Schritt 2: Instance-ID abrufen
metadata_url = 'http://169.254.169.254/latest/meta-data/instance-id'
metadata_req = urllib.request.Request(
metadata_url,
headers={'X-aws-ec2-metadata-token': token}
)
with urllib.request.urlopen(metadata_req, timeout=2) as resp:
return resp.read().decode('utf-8')
if __name__ == '__main__':
print(f'Instance-ID: {get_instance_id()}')
IMDSv2 auf Instanz-Ebene erzwingen
Das Skript auf IMDSv2 umzustellen reicht nicht, wenn IMDSv1 auf der Instanz noch aktiv ist. Solange http-tokens auf optional steht, akzeptiert der IMDS weiterhin tokenlose Requests. Das ist der Zustand, in dem viele ältere Instanzen noch laufen — und der Zustand, den Angreifer ausnutzen.
IMDSv1 lässt sich pro Instanz deaktivieren:
aws ec2 modify-instance-metadata-options \
--instance-id i-1234567890abcdef0 \
--http-tokens required \
--region us-east-1
Den aktuellen Status einer Instanz prüfen:
aws ec2 describe-instances \
--instance-ids i-1234567890abcdef0 \
--query 'Reservations[*].Instances[*].MetadataOptions' \
--output table \
--region us-east-1
Alle Instanzen in einer Region finden, bei denen IMDSv1 noch erlaubt ist — weil http-tokens auf optional steht:
aws ec2 describe-instances \
--filters 'Name=metadata-options.http-tokens,Values=optional' \
--query 'Reservations[*].Instances[*].[InstanceId,MetadataOptions.HttpTokens]' \
--output table \
--region us-east-1
IMDSv1 deaktivieren ist wie das Schloss an einer Tür, die bisher nur angelehnt war. Die Tür war immer da — sie war nur nie wirklich zu.
Typischer Fehler: Symptom, Fehldiagnose, eigentliche Ursache
Das Skript läuft lokal problemlos, schlägt aber in der CI/CD-Pipeline auf der Instanz fehl. Der Fehler sieht so aus:
curl: (22) The requested URL returned error: 401
Erste Reaktion: Netzwerkproblem, Firewall, falscher Endpunkt. Der IMDS-Endpunkt ist link-lokal und nicht über Security Groups steuerbar — das ist also keine Firewall-Frage.
Die eigentliche Ursache: Die Instanz wurde mit --http-tokens required gestartet oder nachträglich auf IMDSv2 umgestellt. Das Skript verwendet aber noch den alten IMDSv1-Request ohne Token. Der 401-Fehler ist das korrekte Verhalten von IMDSv2 bei fehlendem Token.
Fix: Den PUT-Schritt zur Token-Anforderung ergänzen, wie oben gezeigt. Danach funktioniert der Abruf auch auf Instanzen mit erzwungenem IMDSv2.
- IMDSv1-Request (kein Token): Skript sendet GET ohne Token-Header.
- HTTP 401: Instanz hat
http-tokens: required— der Request wird abgelehnt. - IMDSv2-Request (mit Token): PUT für Token, dann GET mit Token-Header.
- HTTP 200 + Instance-ID: Erfolgreiche Antwort.
IMDSv2 beim Start neuer Instanzen standardmäßig erzwingen
Für neue Instanzen lässt sich IMDSv2 direkt beim Launch als Pflicht setzen, sodass IMDSv1 von Anfang an nicht verfügbar ist:
aws ec2 run-instances \
--image-id ami-0abcdef1234567890 \
--instance-type t3.micro \
--metadata-options 'HttpTokens=required,HttpEndpoint=enabled' \
--count 1 \
--region us-east-1
Über AWS Organizations lässt sich per Service Control Policy (SCP) verhindern, dass Instanzen mit HttpTokens=optional gestartet werden — das ist der skalierbare Weg für größere Umgebungen. Die konkrete SCP-Struktur dafür ist in der AWS-Dokumentation zu SCPs und EC2 beschrieben.
Weitere nützliche Metadaten-Pfade
Wer den Token einmal hat, kann damit weitere Metadaten abrufen — immer mit demselben Token im Header:
# Availability Zone
curl -s \
-H "X-aws-ec2-metadata-token: $TOKEN" \
'http://169.254.169.254/latest/meta-data/placement/availability-zone'
# Region (aus der AZ ableiten oder direkt)
curl -s \
-H "X-aws-ec2-metadata-token: $TOKEN" \
'http://169.254.169.254/latest/meta-data/placement/region'
# IAM-Rollenname der Instanz
curl -s \
-H "X-aws-ec2-metadata-token: $TOKEN" \
'http://169.254.169.254/latest/meta-data/iam/security-credentials/'
Den Pfad /latest/meta-data/iam/security-credentials/ liefert nur den Rollennamen. Die eigentlichen temporären Credentials liegen unter /latest/meta-data/iam/security-credentials/ROLLENNAME — das ist genau der Pfad, den ein SSRF-Angriff über IMDSv1 kompromittieren kann.
Wrap-up: EC2 Instance-ID sicher abrufen mit IMDSv2
IMDSv2 ist kein optionales Upgrade — es ist die einzige vertretbare Methode für Produktionsumgebungen. Der Mehraufwand gegenüber IMDSv1 besteht aus einem einzigen zusätzlichen PUT-Request. Dafür schließt es eine Angriffsfläche, die in realen Incidents zu Credential-Diebstahl über SSRF geführt hat.
Nächste Schritte:
- Bestehende Instanzen mit dem
describe-instances-Befehl aufhttp-tokens: optionalprüfen. - Skripte und Deployment-Pipelines auf IMDSv2-Syntax aktualisieren.
- Neue Instanzen grundsätzlich mit
HttpTokens=requiredstarten. - Weiterführend: AWS-Dokumentation zu IMDS
Glossar
| Begriff | Bedeutung |
|---|---|
| IMDS | Instance Metadata Service — HTTP-Endpunkt auf 169.254.169.254, nur von der Instanz selbst erreichbar |
| IMDSv2 | Session-orientierte Version des IMDS, erfordert Token-basierte Authentifizierung per PUT/GET |
| SSRF | Server-Side Request Forgery — Angriff, bei dem eine Anwendung dazu gebracht wird, interne Requests stellvertretend auszuführen |
| http-tokens | Instanz-Metadaten-Option: optional erlaubt IMDSv1, required erzwingt IMDSv2 |
| Link-lokale Adresse | IP-Adresse im Bereich 169.254.0.0/16, nicht routbar außerhalb des lokalen Netzwerksegments |
Kommentare
Kommentar veröffentlichen