Cara Mendapatkan Instance ID EC2 via Metadata: IMDSv2 vs IMDSv1

Saat menulis skrip otomasi yang berjalan di dalam EC2 — entah itu untuk self-registration ke load balancer, pengiriman log, atau bootstrap konfigurasi — kamu butuh Instance ID tanpa harus hardcode atau bergantung pada variabel eksternal. Instance Metadata Service (IMDS) adalah cara standarnya, tapi banyak skrip lama masih pakai IMDSv1 yang punya celah keamanan serius yang sering diabaikan sampai terjadi insiden nyata.

TL;DR: IMDSv1 vs IMDSv2 untuk Mengambil Instance ID

Aspek IMDSv1 IMDSv2
Mekanisme Auth Tidak ada — request langsung Session token berbasis TTL
Risiko SSRF Tinggi — attacker bisa baca metadata via app rentan Mitigasi signifikan — butuh PUT request terlebih dahulu
Perintah curl Satu langkah, tanpa header tambahan Dua langkah: ambil token, lalu ambil data
Rekomendasi AWS Deprecated untuk deployment baru Standar yang direkomendasikan
Enforcement Bisa dinonaktifkan per instance Bisa di-enforce via IMDSv2-only mode

Bagaimana Instance Metadata Service Bekerja

IMDS adalah HTTP endpoint lokal yang hanya bisa diakses dari dalam instance itu sendiri, di alamat 169.254.169.254. Ini adalah link-local address — tidak routable ke luar instance, tidak melewati internet gateway, dan tidak bisa diakses dari instance lain meski berada di subnet yang sama.

Data yang tersedia mencakup Instance ID, AMI ID, tipe instance, availability zone, IAM role credentials, dan banyak lagi. Semua ini disajikan sebagai plain HTTP tanpa TLS — yang berarti keamanannya bergantung sepenuhnya pada siapa yang bisa membuat request ke alamat tersebut.

graph LR subgraph IMDSv1["IMDSv1 — Tanpa Autentikasi"] A1["Skrip / Aplikasi"] -->|"GET /latest/meta-data/instance-id"| B1["IMDS Endpoint 169.254.169.254"] B1 -->|"i-1234567890abcdef0"| A1 ATK["Attacker via SSRF"] -->|"GET request (tidak terautentikasi)"| B1 end subgraph IMDSv2["IMDSv2 — Session Token"] A2["Skrip / Aplikasi"] -->|"1. PUT /latest/api/token X-aws-ec2-metadata-token-ttl-seconds: 21600"| B2["IMDS Endpoint 169.254.169.254"] B2 -->|"2. Token: abc123..."| A2 A2 -->|"3. GET /latest/meta-data/instance-id X-aws-ec2-metadata-token: abc123..."| B2 B2 -->|"4. i-1234567890abcdef0"| A2 ATK2["Attacker via SSRF"] -->|"GET only — tidak bisa lakukan PUT request"| BLOCK["Ditolak / Timeout"] end
  1. IMDSv1 (kiri): Aplikasi atau skrip langsung mengirim GET request ke endpoint metadata. Tidak ada autentikasi. Jika ada celah SSRF di aplikasi, attacker bisa meminta aplikasi untuk fetch URL tersebut atas namanya.
  2. IMDSv2 (kanan): Sebelum bisa membaca metadata, client harus melakukan PUT request untuk mendapatkan session token. PUT request tidak bisa di-forward oleh sebagian besar proxy dan SSRF sederhana — ini yang menjadi lapisan pertahanannya.
  3. Token TTL: Token IMDSv2 punya masa berlaku yang kamu tentukan sendiri (dalam detik). Untuk skrip satu kali jalan, TTL pendek sudah cukup.

Mengapa IMDSv1 Berbahaya: Skenario SSRF Nyata

Serangan Server-Side Request Forgery (SSRF) terjadi ketika attacker bisa membuat server melakukan HTTP request ke URL yang mereka tentukan. Dengan IMDSv1, jika aplikasi web kamu punya endpoint yang menerima URL dan melakukan fetch — misalnya fitur 'preview link' atau webhook — attacker cukup mengirimkan http://169.254.169.254/latest/meta-data/iam/security-credentials/ sebagai target.

Hasilnya? Temporary credentials IAM role yang melekat pada instance tersebut, lengkap dengan AccessKeyId, SecretAccessKey, dan Token. Ini bukan skenario teoritis — insiden besar di industri cloud terjadi persis dengan pola ini.

IMDSv2 tidak menghilangkan SSRF, tapi memutus rantai eksploitasinya. Attacker masih bisa meminta aplikasimu fetch URL eksternal, tapi mereka tidak bisa minta aplikasimu melakukan PUT request untuk mendapatkan token terlebih dahulu — karena sebagian besar SSRF hanya bisa melakukan GET.

Satu detail yang sering terlewat: IMDSv2 juga menolak request yang mengandung header X-Forwarded-For. Ini secara eksplisit memblokir skenario di mana request melewati proxy layer sebelum sampai ke metadata endpoint.

Cara Mengambil Instance ID dengan IMDSv2

Berikut cara yang benar untuk mendapatkan Instance ID dari dalam instance EC2 menggunakan IMDSv2. Prosesnya dua langkah: ambil token sesi, lalu gunakan token tersebut untuk query metadata.

Menggunakan curl (Shell Script)

# Langkah 1: Ambil session token IMDSv2
# TTL diset 21600 detik (6 jam) — sesuaikan dengan kebutuhan skrip kamu
TOKEN=$(curl -s -X PUT \
  'http://169.254.169.254/latest/api/token' \
  -H 'X-aws-ec2-metadata-token-ttl-seconds: 21600')

# Langkah 2: Gunakan token untuk mengambil Instance ID
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"

Untuk skrip yang berjalan singkat, TTL 21600 detik lebih dari cukup. Jika skrip kamu berjalan lama atau dipanggil berulang kali, pertimbangkan untuk menyimpan token dan memeriksa apakah masih valid sebelum melakukan PUT lagi.

Menggunakan Python (boto3 tidak diperlukan)

import urllib.request

def get_instance_id():
    # Langkah 1: Ambil IMDSv2 token
    token_url = 'http://169.254.169.254/latest/api/token'
    token_request = urllib.request.Request(
        token_url,
        method='PUT',
        headers={'X-aws-ec2-metadata-token-ttl-seconds': '21600'}
    )
    with urllib.request.urlopen(token_request, timeout=2) as response:
        token = response.read().decode('utf-8')

    # Langkah 2: Ambil Instance ID menggunakan token
    metadata_url = 'http://169.254.169.254/latest/meta-data/instance-id'
    metadata_request = urllib.request.Request(
        metadata_url,
        headers={'X-aws-ec2-metadata-token': token}
    )
    with urllib.request.urlopen(metadata_request, timeout=2) as response:
        return response.read().decode('utf-8')

instance_id = get_instance_id()
print(f'Instance ID: {instance_id}')

Perhatikan timeout=2 — ini penting. Jika skrip dijalankan di luar EC2 (misalnya saat testing lokal), tanpa timeout kamu akan menunggu lama sebelum request gagal.

Menggunakan AWS CLI (ec2-metadata helper)

Jika AWS CLI sudah terinstal di instance, ada cara alternatif menggunakan Instance Metadata Query Tool, tapi cara paling portabel tetap via curl atau HTTP langsung karena tidak bergantung pada versi CLI yang terinstal.

# AWS CLI v2 mendukung IMDSv2 secara otomatis
aws ec2 describe-instances \
  --instance-ids $(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: 60')" \
    'http://169.254.169.254/latest/meta-data/instance-id') \
  --query 'Reservations[0].Instances[0].InstanceId' \
  --output text \
  --region us-east-1

Meng-enforce IMDSv2: Menonaktifkan IMDSv1 di Instance

Menggunakan IMDSv2 di skrip saja tidak cukup jika IMDSv1 masih aktif — instance tetap rentan jika ada aplikasi lain yang berjalan di dalamnya yang masih pakai V1. Langkah yang benar adalah meng-enforce IMDSv2-only mode di level instance.

Saat Launch: Konfigurasi via AWS CLI

aws ec2 run-instances \
  --image-id ami-0abcdef1234567890 \
  --instance-type t3.micro \
  --metadata-options 'HttpTokens=required,HttpEndpoint=enabled' \
  --region us-east-1 \
  --count 1

Untuk Instance yang Sudah Berjalan

# Ganti INSTANCE_ID dengan ID instance target
aws ec2 modify-instance-metadata-options \
  --instance-id i-1234567890abcdef0 \
  --http-tokens required \
  --http-endpoint enabled \
  --region us-east-1

Parameter kunci di sini adalah --http-tokens required. Nilai required berarti semua request ke IMDS harus menyertakan token IMDSv2 yang valid — request IMDSv1 tanpa token akan ditolak dengan HTTP 401.

Verifikasi Status IMDS di Instance

aws ec2 describe-instances \
  --instance-ids i-1234567890abcdef0 \
  --query 'Reservations[0].Instances[0].MetadataOptions' \
  --output json \
  --region us-east-1

Output yang kamu inginkan:

{
    "State": "applied",
    "HttpTokens": "required",
    "HttpEndpoint": "enabled",
    "HttpPutResponseHopLimit": 1,
    "InstanceMetadataTags": "disabled"
}

HttpPutResponseHopLimit: 1 adalah default yang penting — ini memastikan token PUT request tidak bisa di-forward lebih dari satu hop, memblokir skenario di mana container di dalam instance mencoba mengakses metadata host melalui network hop tambahan.

Meng-enforce IMDSv2 di Seluruh Akun via SCP atau IAM

Enforcement per-instance tidak skalabel untuk akun dengan ratusan instance. Pendekatan yang lebih kuat adalah menggunakan Service Control Policy (SCP) di AWS Organizations untuk mencegah launch instance baru tanpa IMDSv2-required, atau menggunakan AWS Config rule untuk mendeteksi instance yang masih mengizinkan IMDSv1.

🔽 Klik untuk melihat contoh SCP: Wajibkan IMDSv2 saat Launch
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "RequireIMDSv2",
      "Effect": "Deny",
      "Action": "ec2:RunInstances",
      "Resource": "arn:aws:ec2:*:*:instance/*",
      "Condition": {
        "StringNotEquals": {
          "ec2:MetadataHttpTokens": "required"
        }
      }
    }
  ]
}

SCP ini menolak ec2:RunInstances jika parameter MetadataHttpTokens tidak di-set ke required. Ini berlaku untuk semua principal di account yang terkena SCP tersebut, termasuk administrator — pastikan kamu sudah menguji di environment non-produksi terlebih dahulu.

Pengalaman Lapangan: Diagnosis yang Salah Arah

Ada satu pola kegagalan yang sering muncul saat migrasi dari IMDSv1 ke IMDSv2: skrip bootstrap yang berjalan di dalam container Docker di atas EC2 tiba-tiba gagal mengambil metadata setelah instance di-enforce ke IMDSv2-only.

Gejala yang terlihat: curl ke 169.254.169.254 timeout, bukan HTTP 401. Diagnosis pertama yang salah: firewall atau security group memblokir traffic. Padahal security group tidak mengontrol traffic ke link-local address.

Penyebab sebenarnya: HttpPutResponseHopLimit masih di nilai default 1. Ketika container melakukan PUT request untuk mendapatkan token, response dari IMDS harus melewati satu network hop tambahan (dari host ke container), sehingga hop limit terlampaui dan token tidak pernah sampai ke container.

Perbaikannya: naikkan hop limit ke 2 untuk instance yang menjalankan container.

aws ec2 modify-instance-metadata-options \
  --instance-id i-1234567890abcdef0 \
  --http-tokens required \
  --http-endpoint enabled \
  --http-put-response-hop-limit 2 \
  --region us-east-1

Pelajarannya: timeout ke metadata endpoint di dalam container hampir selalu soal hop limit, bukan network policy.

graph TD subgraph HopLimit1["HttpPutResponseHopLimit = 1 (Default)"] C1["Container"] -->|"PUT /api/token (Hop 1: Container ke Host)"| H1["EC2 Host Network"] H1 -->|"Request diteruskan ke IMDS"| M1["IMDS 169.254.169.254"] M1 -->|"Response Token (Hop 1 habis di Host)"| H1 H1 -->|"Tidak bisa diteruskan ke Container"| FAIL["Token Tidak Sampai Curl Timeout"] end subgraph HopLimit2["HttpPutResponseHopLimit = 2"] C2["Container"] -->|"PUT /api/token"| H2["EC2 Host Network"] H2 --> M2["IMDS 169.254.169.254"] M2 -->|"Response Token (Hop 1: IMDS ke Host)"| H2 H2 -->|"Diteruskan ke Container (Hop 2: Host ke Container)"| C2 C2 -->|"Token Berhasil Diterima"| SUCCESS["Metadata Bisa Dibaca"] end
  1. Hop Limit = 1 (default): PUT request dari container melewati virtual network interface host, dihitung sebagai satu hop. Response dari IMDS kembali ke host, tapi tidak bisa diteruskan ke container karena hop limit sudah habis.
  2. Hop Limit = 2: Response bisa melewati dua hop — dari IMDS ke host, lalu dari host ke container. Token berhasil diterima.
  3. Untuk container yang berjalan di dalam container (nested), hop limit mungkin perlu dinaikkan lebih jauh, tapi ini jarang diperlukan dan harus dipertimbangkan dari sisi keamanan.

Cara Mendapatkan Instance ID EC2: Ringkasan dan Langkah Selanjutnya

Mengambil Instance ID via IMDS adalah operasi sederhana, tapi pilihan antara IMDSv1 dan IMDSv2 punya implikasi keamanan yang signifikan. IMDSv2 bukan sekadar 'versi lebih baru' — ini adalah perubahan model keamanan fundamental yang memutus kelas serangan SSRF yang telah menyebabkan beberapa breach besar di industri.

Langkah konkret yang bisa kamu ambil sekarang:

  • Audit semua skrip dan aplikasi yang berjalan di EC2 — cari penggunaan 169.254.169.254 tanpa header X-aws-ec2-metadata-token.
  • Enforce IMDSv2-only di semua instance yang ada menggunakan modify-instance-metadata-options.
  • Terapkan SCP untuk mencegah instance baru diluncurkan tanpa IMDSv2-required.
  • Jika menggunakan container di EC2, verifikasi HttpPutResponseHopLimit sudah di-set ke nilai yang tepat.
  • Pantau dengan AWS Config rule ec2-imdsv2-check untuk deteksi drift konfigurasi secara kontinu.

Referensi resmi: AWS EC2 Instance Metadata Service Documentation.

Glosarium

Istilah Penjelasan
IMDS Instance Metadata Service — HTTP endpoint lokal di 169.254.169.254 yang menyediakan informasi tentang instance EC2 yang sedang berjalan.
IMDSv2 Versi kedua IMDS yang menggunakan session-oriented authentication berbasis token PUT request sebelum bisa membaca metadata.
SSRF Server-Side Request Forgery — serangan di mana attacker membuat server melakukan HTTP request ke URL yang mereka kendalikan, termasuk endpoint internal.
HttpPutResponseHopLimit Parameter IMDS yang mengontrol berapa banyak network hop yang diizinkan untuk response token PUT request. Default 1, perlu dinaikkan untuk container.
Link-local address Alamat IP (169.254.0.0/16) yang hanya valid dalam satu network segment dan tidak bisa di-route ke jaringan lain.

Komentar

Postingan populer dari blog ini

EC2 Tidak Bisa Akses Internet di Custom VPC: Cara Pasang Internet Gateway dan Update Route Table