- Published on
EKS ImagePullBackOff 403? ECR VPC 엔드포인트 정책 점검
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서브넷을 프라이빗으로 구성하고 NAT를 없앤 EKS에서 이미지 풀링이 갑자기 실패하면서 ImagePullBackOff가 뜨고, 이벤트에는 403 Forbidden 혹은 AccessDenied가 찍히는 케이스가 있습니다. 대부분은 node IAM role이나 IRSA 권한을 먼저 의심하지만, ECR을 VPC 엔드포인트(PrivateLink)로 붙여둔 환경에서는 VPC 엔드포인트 정책(Endpoint policy) 한 줄 때문에 403이 날 수 있습니다.
이 글에서는 “EKS에서 403으로 이미지 풀 실패”를 ECR VPC 엔드포인트 정책 관점에서 빠르게 확인하고 고치는 체크리스트를 정리합니다. (IAM/토큰 만료 이슈는 별도 글에서 다룬 적이 있어 여기서는 엔드포인트 중심으로 접근합니다.)
증상 패턴: “IAM은 맞는데 403”
다음과 같은 특징이 있으면 엔드포인트 정책을 강하게 의심합니다.
- 노드 IAM Role에
AmazonEC2ContainerRegistryReadOnly(또는 동등 권한)가 있는데도 풀 실패 - 같은 이미지가 퍼블릭 서브넷/NAT 있는 환경에서는 잘 풀리는데, 프라이빗 서브넷(엔드포인트 경유) 에서만 실패
- 이벤트에
403이 찍히며, 간혹 메시지에denied by endpoint policy가 섞여 나오거나 CloudTrail에서 VPCE 관련 단서가 보임
우선 Pod 이벤트부터 확인합니다.
kubectl describe pod <pod-name> -n <ns>
# Events 예시(상황에 따라 다름)
# Failed to pull image "...dkr.ecr.ap-northeast-2.amazonaws.com/...":
# rpc error: code = Unknown desc = failed to pull and unpack image ...:
# failed to resolve reference ...: unexpected status from HEAD request: 403 Forbidden
ECR 이미지 풀링이 실제로 호출하는 API (핵심)
EKS 노드(정확히는 kubelet/containerd)가 ECR에서 이미지를 받기 위해 하는 일은 대략 다음 순서입니다.
- ECR API에 토큰 요청:
ecr:GetAuthorizationToken - ECR API로 이미지 메타데이터/레이어 URL 조회:
ecr:BatchGetImage,ecr:GetDownloadUrlForLayer,ecr:BatchCheckLayerAvailability - 실제 레이어 다운로드는 ECR DKR(Registry) 도메인으로 HTTPS 다운로드
즉, 프라이빗 환경에서 안정적으로 동작하려면 보통 아래 엔드포인트가 필요합니다.
com.amazonaws.<region>.ecr.api(Interface endpoint)com.amazonaws.<region>.ecr.dkr(Interface endpoint)com.amazonaws.<region>.s3(Gateway endpoint)- ECR 레이어는 내부적으로 S3를 사용하므로, NAT 없이 완전 프라이빗이면 S3 경로도 필요합니다.
그리고 각 엔드포인트 정책이 위 액션들을 막지 않아야 합니다.
1단계: VPC 엔드포인트 존재/연결 상태 점검
먼저 VPC에 엔드포인트가 제대로 붙어 있는지 확인합니다.
aws ec2 describe-vpc-endpoints \
--filters "Name=vpc-id,Values=<vpc-id>" \
--query 'VpcEndpoints[].{Id:VpcEndpointId,Service:ServiceName,State:State,Type:VpcEndpointType,Subnets:SubnetIds}' \
--output table
체크 포인트:
ecr.api,ecr.dkr가 Interface로 존재하는가?- 상태가
available인가? - Pod가 도는 서브넷이 엔드포인트의
SubnetIds에 포함되는가? - 엔드포인트에 연결된 Security Group이 443 인바운드를 허용하는가?
- 보통 “노드 SG -> VPCE SG” 443 허용이 필요합니다.
2단계: Private DNS 설정 확인 (의외로 자주 놓침)
Interface endpoint는 PrivateDnsEnabled=true일 때 표준 AWS 도메인(*.amazonaws.com)을 VPC 내부 IP로 해석합니다.
aws ec2 describe-vpc-endpoints \
--vpc-endpoint-ids <vpce-id> \
--query 'VpcEndpoints[0].PrivateDnsEnabled'
false면 노드가 퍼블릭 ECR로 나가려다 NAT 없어서 타임아웃이 나거나, 라우팅이 꼬여 이상한 에러가 날 수 있습니다.- 이 글 주제는 403이지만, 환경에 따라 403/timeout이 섞여 나타나기도 합니다.
DNS 관련으로 더 깊게 파고들어야 한다면 다음 글도 도움이 됩니다.
3단계(핵심): 엔드포인트 정책이 ECR 액션을 막는지 확인
엔드포인트 정책은 “이 VPC 엔드포인트를 통해 나가는 트래픽이 어떤 AWS API를 호출할 수 있는지”를 제한합니다.
여기서 중요한 함정:
- 노드 IAM Role이 허용해도, VPCE 정책이 Deny/제한이면 최종적으로 403이 납니다.
- 특히 보안 강화를 위해 VPCE 정책을 “특정 리포지토리만 허용” 같은 형태로 작성했다가, 필요한 액션/리소스 스코프가 맞지 않아 막히는 경우가 많습니다.
엔드포인트 정책을 확인합니다.
aws ec2 describe-vpc-endpoints \
--vpc-endpoint-ids <vpce-id> \
--query 'VpcEndpoints[0].PolicyDocument' \
--output text | jq .
자주 깨지는 정책 패턴 1: GetAuthorizationToken을 리포지토리 ARN으로 제한
ecr:GetAuthorizationToken은 리포지토리 리소스가 아니라 계정 레벨 성격이 강해 Resource: "*"가 필요한 경우가 많습니다. 이를 특정 repo ARN으로 묶어두면 토큰 발급 단계에서 막힙니다.
잘못된 예(대표적인 실수):
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": ["ecr:GetAuthorizationToken"],
"Resource": "arn:aws:ecr:ap-northeast-2:123456789012:repository/my-repo"
}
]
}
권장 수정 방향(최소한 토큰은 Resource: "*"):
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowEcrAuth",
"Effect": "Allow",
"Principal": "*",
"Action": "ecr:GetAuthorizationToken",
"Resource": "*"
}
]
}
자주 깨지는 정책 패턴 2: ecr.dkr 엔드포인트에 API 액션을 넣거나(또는 반대)
ecr.api엔드포인트: ECR API 호출(토큰/메타데이터)ecr.dkr엔드포인트: 레지스트리(이미지 레이어 다운로드 경로)
정책을 “복붙”하다가 서비스 성격에 안 맞는 액션을 제한하면 특정 단계에서 403이 납니다. 실무에서는 두 엔드포인트 정책을 동일하게 두기보다, 최소 허용 액션을 엔드포인트별로 분리하는 편이 디버깅에도 유리합니다.
자주 깨지는 정책 패턴 3: 조건(Condition)으로 VPC/서브넷/프린시펄을 과하게 제한
예를 들어 aws:PrincipalArn 또는 aws:sourceVpce 같은 조건을 걸어두고, 노드 교체/ASG 롤링 후 ARN 패턴이 바뀌어 막히는 경우가 있습니다.
권한을 “정교하게” 만들수록 운영 변화(노드그룹 교체, 클러스터 업그레이드, 계정 구조 변경)에 취약해집니다. 먼저 장애를 풀고, 이후에 최소권한을 다시 조이는 순서를 추천합니다.
4단계: CloudTrail로 “VPCE 정책 Deny”를 확정하기
403이 IAM인지 VPCE인지 애매하면 CloudTrail이 빠릅니다. ECR 이벤트를 필터링해서 errorCode와 vpcEndpointId를 같이 봅니다.
aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=EventSource,AttributeValue=ecr.amazonaws.com \
--max-results 50 \
--query 'Events[].CloudTrailEvent' \
--output text | jq -r '. | fromjson | {eventTime, eventName, errorCode, errorMessage, vpcEndpointId, sourceIPAddress, userIdentity}'
포인트:
vpcEndpointId가 찍히면 “엔드포인트 경유”가 확실합니다.errorMessage에 endpoint policy 관련 문구가 뜨는 경우가 있습니다.
5단계: S3 엔드포인트 누락/정책 문제도 함께 점검
ECR 레이어 다운로드는 내부적으로 S3가 관여합니다. NAT 없이 프라이빗이라면 S3 Gateway endpoint 및 정책이 중요합니다.
- S3 엔드포인트가 없으면 보통 403보다는 timeout/네트워크 오류로 나타나지만,
- S3 엔드포인트 정책을 강하게 제한한 경우 403/AccessDenied 형태로도 나타날 수 있습니다.
S3 Gateway endpoint 확인:
aws ec2 describe-vpc-endpoints \
--filters "Name=vpc-id,Values=<vpc-id>" "Name=service-name,Values=com.amazonaws.<region>.s3" \
--query 'VpcEndpoints[].{Id:VpcEndpointId,State:State,RouteTables:RouteTableIds,Policy:PolicyDocument}'
체크 포인트:
- 프라이빗 서브넷이 연결된 라우트테이블이
RouteTableIds에 포함되는가? - 정책이
s3:GetObject등을 막고 있지 않은가?
6단계: “일단 살리기”용 기준 정책(디버깅 베이스라인)
운영 중 장애를 빨리 복구해야 한다면, 우선 VPCE 정책을 너무 타이트하게 잡지 말고 베이스라인으로 열어 원인을 분리하는 것이 좋습니다.
ECR API 엔드포인트 정책(베이스라인)
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowEcrApiForPull",
"Effect": "Allow",
"Principal": "*",
"Action": [
"ecr:GetAuthorizationToken",
"ecr:BatchCheckLayerAvailability",
"ecr:BatchGetImage",
"ecr:GetDownloadUrlForLayer"
],
"Resource": "*"
}
]
}
ECR DKR 엔드포인트 정책(베이스라인)
ECR DKR은 데이터 플레인 성격이라 정책을 단순화해 두고(또는 기본 허용), 실제 제어는 IAM/리포지토리 정책에서 하는 전략이 흔합니다.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowAllThroughDkrEndpoint",
"Effect": "Allow",
"Principal": "*",
"Action": "*",
"Resource": "*"
}
]
}
> 보안상 최종 형태로 추천하는 건 아닙니다. 다만 장애 상황에서 “VPCE 정책이 범인인지”를 빠르게 가르는 데 유용한 기준점입니다.
7단계: 리포지토리 정책(Repository policy)도 함께 확인
VPCE 정책을 완화했는데도 403이면, 리포지토리 정책이 특정 VPC/VPCE만 허용하도록 묶여 있을 수 있습니다.
aws ecr get-repository-policy --repository-name <repo> --output json | jq .
리포지토리 정책에서 aws:sourceVpce 같은 조건을 걸어둔 경우:
- 엔드포인트를 교체했거나(새 VPCE ID)
- 다른 VPC/서브넷에서 접근하거나
- 멀티클러스터/멀티계정 구조로 확장
하면서 예기치 않게 막힐 수 있습니다.
운영 팁: 재발 방지 체크리스트
- 노드그룹/ASG 롤링 시에도 유지되는 제약인지 확인(PrincipalArn 조건 남용 금지)
ecr.api,ecr.dkr,s3를 한 세트로 검증(하나 빠지면 증상이 섞여 나타남)- CloudTrail에
vpcEndpointId를 남기도록(기본적으로 남음) 장애 시 즉시 조회할 수 있게 런북에 명령어 포함 - 엔드포인트 정책 변경은 IaC(Terraform/CDK)로 관리하고 PR 리뷰에서 “ECR pull 필수 액션”을 체크리스트화
마무리
EKS에서 ImagePullBackOff + 403이 보이면 IAM/토큰 만료만 보다가 시간을 쓰기 쉽습니다. 하지만 NAT 없는 프라이빗 클러스터에서 ECR을 VPC 엔드포인트로 붙였다면, VPCE 정책이 최우선 의심 대상입니다.
정리하면, 빠른 해결 순서는 다음입니다.
- Pod 이벤트에서 403 확인
ecr.api/ecr.dkr엔드포인트 존재 + 서브넷/SG/Private DNS 확인- 엔드포인트 정책에서
GetAuthorizationToken포함 필수 액션이 막히지 않는지 확인 - CloudTrail로 VPCE 경유 및 Deny 근거 확보
- S3 엔드포인트/정책 및 리포지토리 정책까지 확장 점검
이 흐름대로만 점검하면 “IAM은 맞는데 왜 403이지?” 같은 케이스를 훨씬 빠르게 끝낼 수 있습니다.