EC2 인스턴스 ID를 스크립트에서 가져오는 법 — IMDSv2가 V1보다 안전한 이유
서버 내부에서 실행 중인 스크립트가 자신이 어느 인스턴스인지 알아야 할 때가 있다. 로그에 인스턴스 ID를 찍거나, 태그를 조회하거나, Auto Scaling 그룹에서 자기 자신을 식별해야 할 때 — 이 시나리오에서 EC2 인스턴스 메타데이터 서비스(IMDS)가 유일한 현실적 선택지다. 문제는 기본 설정 그대로 두면 SSRF 취약점 하나로 IAM 자격증명까지 탈취당할 수 있다는 점이다.
TL;DR — IMDSv1 vs IMDSv2 핵심 비교
| 항목 | IMDSv1 | IMDSv2 |
|---|---|---|
| 인증 방식 | 없음 (단순 GET) | 세션 토큰 (PUT → GET) |
| SSRF 취약점 노출 | 높음 | 낮음 (토큰 발급에 PUT 필요) |
| TTL 제어 | 불가 | 가능 (1초 ~ 21600초) |
| 강제 적용 방법 | 해당 없음 | HttpTokens=required |
| 기본 엔드포인트 | http://169.254.169.254 | |
인스턴스 메타데이터 서비스(IMDS)가 동작하는 방식
IMDS는 인스턴스 내부에서만 접근 가능한 링크-로컬 HTTP 엔드포인트(169.254.169.254)다. 하이퍼바이저 계층에서 라우팅을 차단하기 때문에 인스턴스 외부에서는 원칙적으로 도달할 수 없다. 인스턴스 ID, AMI ID, IAM 역할 자격증명, 네트워크 인터페이스 정보 등 부트스트랩에 필요한 거의 모든 런타임 정보를 여기서 가져온다.
IMDSv1은 단순 GET 요청만으로 모든 데이터를 반환한다. 애플리케이션에 SSRF(Server-Side Request Forgery) 취약점이 있으면 공격자는 그 취약점을 통해 http://169.254.169.254/latest/meta-data/iam/security-credentials/까지 도달해 임시 자격증명을 탈취할 수 있다. 실제로 2019년 Capital One 침해 사고의 핵심 경로가 이 패턴이었다.
IMDSv2는 세션 지향(session-oriented) 방식으로 이 문제를 구조적으로 차단한다. 데이터를 읽기 전에 반드시 PUT 요청으로 세션 토큰을 먼저 발급받아야 한다. 일반적인 SSRF 공격은 GET만 가능한 경우가 많고, 설령 PUT이 가능하더라도 X-aws-ec2-metadata-token-ttl-seconds 헤더를 정확히 포함해야 토큰이 발급된다. 이 추가 단계가 자동화된 SSRF 익스플로잇의 성공률을 크게 낮춘다.
(X-aws-ec2-metadata-token-ttl-seconds: 21600) IMDS-->>Script: TOKEN=AQAEAAA... Script->>IMDS: GET /latest/meta-data/instance-id
(X-aws-ec2-metadata-token: TOKEN) IMDS-->>Script: i-0abcdef1234567890
- PUT /latest/api/token — TTL을 헤더로 지정해 세션 토큰 요청. 이 단계 없이는 데이터에 접근 불가.
- 토큰 발급 — IMDS가 지정된 TTL 동안 유효한 토큰 반환.
- GET /latest/meta-data/instance-id — 발급받은 토큰을
X-aws-ec2-metadata-token헤더에 포함해 실제 데이터 요청. - 인스턴스 ID 반환 — 토큰이 유효하면 값 반환, 없거나 만료되면 401 응답.
IMDSv2로 인스턴스 ID 조회하기 — 실전 CLI 및 스크립트
가장 먼저 확인할 것은 해당 인스턴스가 IMDSv2 전용 모드(HttpTokens=required)로 설정되어 있는지 여부다. 현재 설정을 모른 채 스크립트를 작성하면 나중에 정책이 강화될 때 조용히 깨진다.
1단계: 현재 IMDS 설정 확인
인스턴스의 메타데이터 옵션을 먼저 확인한다. HttpTokens가 optional이면 V1도 허용 중이라는 뜻이다 — 보안 관점에서 이 상태는 방치하면 안 된다.
aws ec2 describe-instances \
--instance-ids i-0abcdef1234567890 \
--query 'Reservations[0].Instances[0].MetadataOptions' \
--output json \
--region us-east-1
출력 예시:
{
"State": "applied",
"HttpTokens": "optional",
"HttpPutResponseHopLimit": 1,
"HttpEndpoint": "enabled",
"HttpProtocolIpv6": "disabled",
"InstanceMetadataTags": "disabled"
}
HttpTokens: optional은 V1이 살아있다는 신호다. 프로덕션에서 이 상태를 발견하면 즉시 다음 단계로 넘어가야 한다.
2단계: IMDSv2 전용 모드로 전환
인스턴스를 재시작할 필요 없이 실행 중에 적용 가능하다. 단, 이 변경 후 V1 방식으로 메타데이터를 읽는 기존 스크립트나 에이전트가 있다면 즉시 실패한다 — 변경 전에 의존성을 반드시 점검해야 한다.
aws ec2 modify-instance-metadata-options \
--instance-id i-0abcdef1234567890 \
--http-tokens required \
--http-endpoint enabled \
--region us-east-1
3단계: 인스턴스 내부에서 IMDSv2로 인스턴스 ID 조회
인스턴스에 SSH로 접속하거나, 부트스트랩 스크립트(User Data) 안에서 아래 패턴을 사용한다. 두 단계를 반드시 순서대로 실행해야 한다.
# 1. 세션 토큰 발급 (TTL: 21600초 = 6시간)
TOKEN=$(curl -s -X PUT \
"http://169.254.169.254/latest/api/token" \
-H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
# 2. 토큰을 사용해 인스턴스 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"
TTL은 스크립트 실행 시간에 맞게 조정한다. 단발성 부트스트랩이라면 60초로도 충분하다. 장기 실행 데몬이라면 토큰 만료 전에 재발급하는 로직이 필요하다.
4단계: Python 스크립트에서 사용하는 패턴
🔽 Python 예제 펼치기 (boto3 없이 순수 HTTP)
import urllib.request
def get_instance_id():
# 세션 토큰 발급
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': '60'}
)
with urllib.request.urlopen(token_req, timeout=2) as resp:
token = resp.read().decode('utf-8')
# 인스턴스 ID 조회
id_url = 'http://169.254.169.254/latest/meta-data/instance-id'
id_req = urllib.request.Request(
id_url,
headers={'X-aws-ec2-metadata-token': token}
)
with urllib.request.urlopen(id_req, timeout=2) as resp:
return resp.read().decode('utf-8')
if __name__ == '__main__':
print('Instance ID:', get_instance_id())
외부 라이브러리 없이 표준 라이브러리만 사용하는 이유가 있다 — 부트스트랩 초기 단계에서는 pip 환경이 아직 구성되지 않은 경우가 많다. timeout을 짧게 설정하는 것도 중요하다. EC2가 아닌 환경에서 실수로 실행됐을 때 스크립트가 수십 초씩 블로킹되는 상황을 막는다.
실수로 배우는 패턴 — IMDSv2 전환 후 조용히 깨지는 경우
증상: HttpTokens=required로 전환한 직후, CloudWatch 에이전트 또는 AWS CLI가 자격증명을 찾지 못한다는 오류를 낸다. 콘솔에서 인스턴스는 정상으로 보이고, 애플리케이션 로그에는 Unable to locate credentials만 찍힌다.
처음엔 IAM 역할 문제라고 생각하기 쉽다. 역할이 붙어 있는지 확인하고, 정책을 다시 검토하고, 인스턴스를 재시작해도 증상이 그대로다.
실제 원인은 따로 있다. 해당 에이전트나 SDK 버전이 IMDSv1 방식으로 자격증명을 가져오도록 하드코딩되어 있거나, 환경 변수 AWS_EC2_METADATA_SERVICE_ENDPOINT가 잘못 설정되어 있는 경우다. IMDSv2 전환 후 V1 요청은 401 Unauthorized를 반환하는데, 일부 구버전 SDK는 이 응답을 '자격증명 없음'으로 해석해 상위 레이어에서 오해를 유발한다.
진단은 간단하다. 인스턴스 내부에서 토큰 없이 V1 방식으로 직접 요청해보면 된다:
# V1 방식으로 시도 — HttpTokens=required 상태라면 401 반환
curl -s -o /dev/null -w "%{http_code}" \
http://169.254.169.254/latest/meta-data/instance-id
401이 나오면 IMDSv2 전용 모드가 정상 적용된 것이다. 이 상태에서 에이전트 오류가 난다면 에이전트 버전을 업그레이드하거나, 해당 소프트웨어의 IMDSv2 지원 여부를 공식 문서에서 확인해야 한다.
IMDSv2 강제 적용은 '보안 스위치'가 아니라 '의존성 감사 트리거'다. 전환 전에 인스턴스에서 메타데이터 엔드포인트를 호출하는 모든 소프트웨어 목록을 먼저 만들어야 한다.
신규 인스턴스 런치 시 IMDSv2 기본 적용 — Launch Template 설정
기존 인스턴스를 하나씩 수정하는 것보다 Launch Template 수준에서 기본값을 강제하는 것이 운영 효율이 높다. 새로 생성되는 모든 인스턴스가 처음부터 HttpTokens=required로 시작한다.
aws ec2 create-launch-template \
--launch-template-name imdsv2-enforced \
--version-description 'IMDSv2 required' \
--launch-template-data '{
"MetadataOptions": {
"HttpTokens": "required",
"HttpEndpoint": "enabled",
"HttpPutResponseHopLimit": 1
}
}' \
--region us-east-1
HttpPutResponseHopLimit을 1로 유지하는 것이 중요하다. 이 값을 높이면 컨테이너 내부에서도 호스트의 IMDS에 접근할 수 있게 되어 공격 표면이 넓어진다. 컨테이너가 인스턴스 메타데이터에 접근해야 한다면 ECS Task Role이나 EKS IRSA처럼 컨테이너 수준의 자격증명 메커니즘을 사용하는 것이 올바른 설계다.
계정 전체 IMDSv1 사용 현황 감사
어느 인스턴스가 아직 V1을 허용하는지 계정 전체를 한 번에 확인하려면 아래 명령을 사용한다. 리전별로 실행해야 한다.
aws ec2 describe-instances \
--query 'Reservations[*].Instances[?MetadataOptions.HttpTokens==`optional`].[InstanceId,MetadataOptions.HttpTokens]' \
--output table \
--region us-east-1
이 쿼리는 HttpTokens=optional인 인스턴스만 필터링해 출력한다. 결과가 비어 있으면 해당 리전의 모든 인스턴스가 IMDSv2 전용이거나 IMDS가 비활성화된 상태다.
IMDSv2로 인스턴스 ID 조회 — 마무리 및 다음 단계
EC2 인스턴스 메타데이터 서비스를 통해 인스턴스 ID를 가져오는 것은 단순한 API 호출이지만, IMDSv2 적용 여부는 실질적인 보안 경계를 결정한다. V1을 허용한 채로 두는 것은 SSRF 취약점이 존재할 때 IAM 자격증명까지 노출되는 경로를 열어두는 것과 같다.
운영 체크리스트 요약:
- 신규 인스턴스: Launch Template에
HttpTokens=required기본 설정 - 기존 인스턴스:
describe-instances로 V1 허용 인스턴스 감사 후 순차 전환 - 스크립트: PUT → GET 두 단계 패턴으로 통일, TTL은 사용 목적에 맞게 조정
- 컨테이너 워크로드: IMDS 직접 접근 대신 ECS Task Role / EKS IRSA 사용
공식 문서 참조: AWS EC2 Instance Metadata Service 공식 문서
핵심 용어 정리
| 용어 | 설명 |
|---|---|
| IMDS | Instance Metadata Service. EC2 인스턴스 내부에서만 접근 가능한 링크-로컬 HTTP 엔드포인트(169.254.169.254). 인스턴스 ID, IAM 자격증명 등 런타임 정보를 제공. |
| IMDSv2 | 세션 토큰 기반의 IMDS 버전 2. PUT으로 토큰 발급 후 GET 요청에 토큰을 포함해야 데이터 접근 가능. |
| SSRF | Server-Side Request Forgery. 서버가 공격자가 지정한 내부 URL로 요청을 보내도록 유도하는 공격. IMDS V1 탈취의 주요 경로. |
| HttpTokens | EC2 메타데이터 옵션 파라미터. optional(V1 허용) 또는 required(V2 전용) 중 하나. |
| HttpPutResponseHopLimit | PUT 요청의 TTL 홉 수 제한. 1로 설정하면 컨테이너에서 호스트 IMDS로의 접근을 차단하는 효과. |
댓글
댓글 쓰기