- Published on
EKS Pod에서 IMDS 401 뜰 때 IRSA·HopLimit 해결
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서론
EKS에서 Pod가 AWS API를 호출할 때 가장 흔히 기대하는 흐름은 IRSA(IAM Roles for Service Accounts) 를 통해 Pod 단위로 IAM 권한을 부여하는 것입니다. 그런데 현실에서는 애플리케이션/SDK가 자격 증명을 찾지 못해 IMDS(169.254.169.254) 로 떨어지고, 이 과정에서 401 Unauthorized 를 만나면서 장애가 발생하곤 합니다.
특히 다음과 같은 증상이 함께 나타납니다.
- 애플리케이션 로그에
IMDS 401또는EC2MetadataError: failed to fetch token류 메시지 sts:AssumeRoleWithWebIdentity가 호출되지 않거나, 호출되기 전에 메타데이터로 먼저 시도- 동일 이미지를 EC2에서 실행하면 되는데 EKS Pod에서만 실패
이 글에서는 IMDS 401이 왜 나는지(원인 분해) → IRSA가 실제로 적용됐는지 확인 → IMDSv2 토큰/홉 제한(HopLimit) 및 노드 IMDS 설정 점검 → 재발 방지 체크리스트 순으로 정리합니다.
> 참고: IRSA 자체 점검은 ExternalDNS 사례로도 유사합니다. 필요한 경우 EKS ExternalDNS가 Route53 생성 실패할 때 IRSA 점검 글도 함께 보시면 좋습니다.
IMDS 401의 의미: “접근은 됐는데 인증이 안 됨”
401 Unauthorized 는 네트워크가 막혀서 못 간 게 아니라(그건 타임아웃/연결 실패), IMDS가 요청을 받았지만 인증/요구사항을 충족하지 못했다는 신호입니다. EKS 환경에서 이 401은 보통 아래 범주 중 하나로 귀결됩니다.
- IMDSv2 강제 + 토큰 미제공
- 노드가 IMDSv2를 강제(
HttpTokens=required)하고 있는데, 컨테이너/SDK가 IMDSv1 방식(토큰 없이)으로 호출 → 401
- HopLimit(IPv4 hop limit) 때문에 토큰/메타데이터 흐름이 깨짐
- IMDS 응답이 1 hop 밖으로 못 나가도록 설정되어(기본 1) Pod 네트워크 경로에서 실패 → SDK에 따라 401/403/타임아웃 등 다양하게 보일 수 있음
- EKS는 일반적으로 Pod가 노드의 IMDS에 접근할 수 있지만, CNI/네트워크 경로/설정에 따라 hop 제한이 문제를 일으킬 수 있습니다.
- IRSA가 미적용/오동작하여 SDK가 IMDS로 폴백
- 원래는 WebIdentity(서비스어카운트 토큰)로 STS를 호출해야 하는데, 환경변수/토큰 마운트/어노테이션/Trust Policy 문제로 실패 → SDK가 다음 우선순위로 IMDS를 시도
핵심은 “IMDS를 때리는 것 자체가 문제”라기보다, Pod가 IRSA로 자격 증명을 제대로 얻지 못해 IMDS로 떨어지는 상황을 먼저 끊는 것입니다.
1단계: Pod가 정말 IRSA를 쓰고 있는지 빠르게 확인
(1) 서비스어카운트 어노테이션 확인
IRSA는 ServiceAccount에 아래 어노테이션이 있어야 합니다.
kubectl -n <ns> get sa <sa-name> -o yaml | sed -n '1,120p'
기대값 예:
metadata:
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::<ACCOUNT_ID>:role/<IRSA_ROLE_NAME>
없다면 Pod는 IRSA를 쓸 수 없고, SDK가 다른 경로(환경변수/프로파일/IMDS)로 자격 증명을 찾게 됩니다.
(2) Pod에 WebIdentity 환경변수/토큰이 들어왔는지 확인
IRSA가 정상 주입되면 Pod 내부에 보통 다음이 존재합니다.
AWS_ROLE_ARNAWS_WEB_IDENTITY_TOKEN_FILE- 토큰 파일 경로에 실제 JWT 존재
kubectl -n <ns> exec -it <pod> -- sh -c 'env | egrep "AWS_ROLE_ARN|AWS_WEB_IDENTITY_TOKEN_FILE"; ls -l $AWS_WEB_IDENTITY_TOKEN_FILE; head -c 30 $AWS_WEB_IDENTITY_TOKEN_FILE; echo'
여기서 환경변수가 없거나 토큰 파일이 없다면, IRSA 주입 자체가 안 된 겁니다.
(3) SDK가 어떤 자격 증명 체인을 타는지 로그로 확인
AWS SDK는 자격 증명 공급자 체인을 순서대로 시도합니다. 언어별로 디버그 옵션을 켜면 “왜 IMDS로 갔는지”가 보입니다.
Java (AWS SDK v2) 예
JAVA_TOOL_OPTIONS="-Dsoftware.amazon.awssdk.http.service.impl=apache" \
-Dorg.slf4j.simpleLogger.log.software.amazon.awssdk.auth=debug
Python (boto3/botocore) 예
export BOTOCORE_LOG_LEVEL=debug
python app.py
로그에서 AssumeRoleWithWebIdentity 시도가 보이지 않고 IMDS 로 바로 가면, IRSA 구성이 먼저 의심 포인트입니다.
2단계: IRSA 구성이 맞는데도 IMDS 401이 뜨는 경우
IRSA가 정상인데도 애플리케이션이 IMDS를 시도하는 케이스가 있습니다.
- 오래된 SDK/라이브러리가 WebIdentity를 지원하지 않음
- 애플리케이션이 명시적으로 EC2 메타데이터를 호출하도록 구현됨
- 컨테이너 이미지에 들어있는 AWS CLI/SDK가 너무 구버전
이때는 “IRSA로 가게 만들기” 또는 “IMDS 호출 자체를 차단/대체”가 필요합니다.
(1) SDK/CLI 업데이트
예를 들어 Python이라면 boto3/botocore 업데이트만으로 WebIdentity 지원/버그가 해결되는 경우가 많습니다.
pip install -U boto3 botocore
Node.js도 마찬가지로 v3 모듈에서 WebIdentity 처리가 개선되어 있습니다.
(2) IMDS 접근을 명시적으로 끄기(권장)
Pod가 IRSA를 사용한다면, 보안/명확성 측면에서 IMDS를 아예 보지 않게 하는 것이 좋습니다.
export AWS_EC2_METADATA_DISABLED=true
Deployment 예:
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
template:
spec:
serviceAccountName: myapp-sa
containers:
- name: myapp
image: <image>
env:
- name: AWS_EC2_METADATA_DISABLED
value: "true"
이렇게 하면 IRSA가 깨졌을 때도 IMDS로 조용히 폴백하지 않고, 더 빠르고 명확하게 실패합니다(원인 파악이 쉬워짐).
3단계: IMDSv2 토큰과 401 — 노드 메타데이터 옵션 점검
노드(EC2)의 IMDS 설정이 IMDSv2 토큰 필수로 되어 있으면, 토큰 없이 접근하는 모든 요청은 401이 됩니다.
(1) 노드에서 MetadataOptions 확인
aws ec2 describe-instances \
--instance-ids <node-instance-id> \
--query 'Reservations[0].Instances[0].MetadataOptions'
주요 필드:
HttpTokens:required/optionalHttpPutResponseHopLimit: 1~64
(2) IMDSv2 토큰 요구가 문제라면
원칙적으로는 SDK를 IMDSv2 지원 버전으로 올리거나 IRSA로 전환하는 게 정석입니다. 임시로 HttpTokens=optional 로 낮추는 건 보안상 권장되지 않습니다.
다만 운영 환경에서 “당장 장애를 막아야 하는데 레거시가 IMDSv1만 지원”이라면, 위험을 인지한 상태에서 제한적으로 검토할 수 있습니다(가능하면 빠르게 제거).
4단계: HopLimit(HTTP PUT response hop limit)과 Pod에서의 IMDS 접근
HttpPutResponseHopLimit 는 IMDS 응답이 몇 hop까지 갈 수 있는지 제한합니다. 기본값 1은 일반적인 EC2 프로세스에는 충분하지만, 컨테이너 네트워킹/프록시/특수 라우팅이 들어가면 hop이 늘어 문제가 될 수 있습니다.
(1) 증상
- IMDSv2 토큰 요청(
PUT /latest/api/token)이 실패 - 어떤 환경에서는 401, 어떤 환경에서는 연결 실패/타임아웃으로 보임
- 노드에서 curl은 되는데 Pod에서만 실패
(2) Pod에서 IMDSv2 토큰 요청 재현
Pod 내부에서 아래로 확인합니다.
kubectl -n <ns> exec -it <pod> -- sh -c '
set -e
TOKEN=$(curl -sS -X PUT "http://169.254.169.254/latest/api/token" \
-H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
echo "TOKEN length: ${#TOKEN}"
curl -sS -H "X-aws-ec2-metadata-token: $TOKEN" \
http://169.254.169.254/latest/meta-data/iam/security-credentials/ || true
'
여기서 토큰 발급 자체가 401/실패하면 IMDSv2/네트워크/HopLimit/차단 정책을 의심해야 합니다.
(3) 해결: 노드의 HopLimit 상향
Launch Template 또는 인스턴스 메타데이터 옵션에서 HttpPutResponseHopLimit 를 2 이상으로 올리는 방식이 일반적입니다.
CLI로는 인스턴스 단위 수정이 가능합니다.
aws ec2 modify-instance-metadata-options \
--instance-id <node-instance-id> \
--http-put-response-hop-limit 2
하지만 EKS Managed Node Group를 사용한다면 노드 재생성 시 원복될 수 있으므로, 근본적으로는 Launch Template에 MetadataOptions를 반영해야 합니다.
> 주의: HopLimit을 무작정 크게 올리면 IMDS 접근 범위가 넓어져 보안 표면이 커질 수 있습니다. 필요한 최소치(대개 2)로 조정하고, 가능하면 IRSA로 IMDS 의존을 제거하세요.
5단계: “IRSA인데 왜 STS가 아니라 IMDS로 가?” 흔한 함정
(1) ServiceAccountName 누락
Deployment에서 serviceAccountName 을 빼먹으면 기본 SA를 사용합니다.
kubectl -n <ns> get pod <pod> -o jsonpath='{.spec.serviceAccountName}'; echo
원하는 SA가 아니라면 매니페스트 수정이 필요합니다.
(2) Trust Policy/OIDC Provider 불일치
IRSA Role의 Trust 관계가 클러스터 OIDC와 맞지 않으면 AssumeRoleWithWebIdentity 가 실패합니다. 이 경우 애플리케이션은 다른 자격 증명을 찾다가 IMDS를 시도할 수 있습니다.
Role trust policy에서 아래를 점검하세요.
- Federated가 해당 OIDC provider ARN인지
sub조건이system:serviceaccount:<ns>:<sa>와 일치하는지aud가sts.amazonaws.com인지
(3) 네트워크 이슈로 STS 호출이 실패 → 폴백
IRSA는 STS로 나가야 하므로, NAT/라우팅/DNS 문제가 있으면 AssumeRole이 실패합니다. 이때도 SDK가 IMDS를 시도할 수 있습니다.
EKS에서 네트워크/STS/DNS 문제를 함께 다룬 글로는 EKS TLS handshake timeout 해결 - IRSA·VPC·CoreDNS 도 참고할 만합니다.
6단계: 실전 해결 시나리오별 처방전
시나리오 A) IRSA 미적용 → IMDS 401
- SA에
eks.amazonaws.com/role-arn어노테이션 추가 - Deployment에
serviceAccountName지정 - Pod 재배포
- Pod 내부에서
AWS_WEB_IDENTITY_TOKEN_FILE확인
추가로, IMDS 폴백을 막기 위해:
env:
- name: AWS_EC2_METADATA_DISABLED
value: "true"
시나리오 B) 레거시 SDK가 IMDSv1만 지원 → 401
- SDK/CLI 업그레이드가 최우선
- 불가피하면 임시로
HttpTokens=optional검토(보안 리스크 인지) - 장기적으로 IRSA +
AWS_EC2_METADATA_DISABLED=true
시나리오 C) Pod에서만 토큰 요청 실패(의심: HopLimit)
- Pod 내부에서 IMDSv2 토큰 발급 curl로 재현
- 노드
HttpPutResponseHopLimit을 2로 상향(Launch Template 반영) - 동시에 가능하면 IMDS 의존 제거(IRSA 강제)
7단계: 재발 방지 체크리스트
- Pod는 반드시 IRSA 사용(서비스어카운트 어노테이션/지정 확인)
- 애플리케이션/SDK가 WebIdentity 지원 버전인지 확인
-
AWS_EC2_METADATA_DISABLED=true로 IMDS 폴백 차단(권장) - 노드 IMDS 설정 점검:
HttpTokens=required유지(보안), HopLimit 최소 상향 - STS/DNS/NAT 경로 정상 여부 확인(AssumeRoleWithWebIdentity가 실제로 나가는지)
결론
EKS Pod에서 IMDS 401이 뜨는 상황은 대부분 “IRSA가 깨져서 SDK가 IMDS로 떨어졌거나”, 또는 “IMDSv2 토큰/홉 제한 같은 노드 메타데이터 정책과 충돌했거나” 둘 중 하나입니다.
가장 안정적인 방향은 다음 조합입니다.
- IRSA를 확실히 적용해 Pod가 STS(WebIdentity)로만 자격 증명을 얻도록 만들고
AWS_EC2_METADATA_DISABLED=true로 IMDS 폴백을 차단하며- 부득이하게 IMDS가 필요한 워크로드만 HopLimit/IMDSv2 정책을 최소 범위에서 조정
이 3가지를 기준으로 진단하면, “왜 401이 나는지”가 로그/설정에서 명확히 드러나고 재발도 크게 줄일 수 있습니다.