- Published on
EKS IRSA AccessDenied 12분 해결 체크리스트
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
EKS에서 AWS SDK를 쓰는 파드가 갑자기 AccessDenied 를 뿜으면, 대부분은 “IRSA가 붙어 있겠지”라는 믿음이 깨지는 순간입니다. 특히 롤/정책은 만들어졌는데도 sts:AssumeRoleWithWebIdentity 단계에서 막히거나, S3·SQS·Secrets Manager 같은 서비스 호출에서 권한이 거절되는 케이스가 흔합니다.
이 글은 IRSA 권한 오류를 12분 안에 복구하는 것을 목표로, 현장에서 가장 자주 터지는 원인들을 증상별로 역추적하는 방식으로 정리합니다. (원인만 잡히면 수정은 1~2줄로 끝나는 경우가 많습니다.)
문제 재현/로그가 DNS나 CoreDNS 이슈와 섞여 헷갈릴 때도 있는데, 네트워크 타임아웃과 권한 오류는 접근법이 다릅니다. DNS 쪽이 의심되면 별도로 AWS EKS CoreDNS CrashLoopBackOff와 DNS 타임아웃 해결도 함께 확인하세요.
0) 12분 타임박스: 먼저 결론부터
아래 4가지만 순서대로 확인하면, 대부분의 AccessDenied 는 빠르게 정리됩니다.
- 파드가 진짜 IRSA를 쓰는지: 서비스어카운트가 맞는지, 토큰이 마운트됐는지, 환경변수가 잡혔는지
- OIDC Provider가 클러스터와 일치하는지: EKS OIDC issuer URL과 IAM OIDC provider ARN 매칭
- 신뢰 정책(Trust policy)의
sub/aud조건이 정확한지:system:serviceaccount:ns:sa오타가 가장 흔함 - 권한 정책(permissions policy)이 실제 API를 허용하는지:
kms:Decrypt,secretsmanager:GetSecretValue,s3:ListBucket같은 빠진 액션
이제 실제로 12분 플로우로 들어갑니다.
1) 1~3분: 파드가 IRSA를 “실제로” 쓰는지 확인
IRSA는 서비스어카운트에 role ARN annotation 이 붙고, 파드가 그 SA를 사용하며, 토큰이 마운트되고, SDK가 AssumeRoleWithWebIdentity 를 타야 합니다.
1-1. 파드가 어떤 서비스어카운트를 쓰는지
kubectl -n myns get pod mypod -o jsonpath='{.spec.serviceAccountName}'
의도한 SA가 아니면, Deployment/Job 템플릿의 spec.template.spec.serviceAccountName 부터 수정해야 합니다.
1-2. 서비스어카운트에 IRSA annotation이 있는지
kubectl -n myns get sa mysa -o yaml
아래 형태가 있어야 합니다.
apiVersion: v1
kind: ServiceAccount
metadata:
name: mysa
namespace: myns
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/mysa-irsa-role
1-3. 파드 내부에서 IRSA 환경변수/토큰 파일 확인
kubectl -n myns exec -it mypod -- sh -lc 'env | grep -E "AWS_ROLE_ARN|AWS_WEB_IDENTITY_TOKEN_FILE|AWS_REGION"'
kubectl -n myns exec -it mypod -- sh -lc 'ls -l $AWS_WEB_IDENTITY_TOKEN_FILE && head -c 20 $AWS_WEB_IDENTITY_TOKEN_FILE'
AWS_WEB_IDENTITY_TOKEN_FILE가 없거나 파일이 없다면, IRSA 경로가 아니라는 뜻입니다.- 간혹 애플리케이션이 AWS SDK 대신 고정 키를 읽거나, 다른 Credential provider chain을 타는 경우도 있으니, 컨테이너 이미지/런타임 설정도 같이 보세요.
2) 3~6분: 에러가 “AssumeRole” 단계인지부터 분리
AccessDenied 라고 뭉뚱그려 보이지만, 크게 두 부류입니다.
- A. STS AssumeRoleWithWebIdentity 자체가 거절: 신뢰 정책, OIDC provider,
sub/aud조건 문제 - B. AssumeRole은 성공했는데 서비스 API에서 거절: 권한 정책(permissions) 문제
가장 빠른 방법은 파드에서 STS 호출을 “직접” 해보는 겁니다.
2-1. 파드에서 aws sts get-caller-identity
컨테이너에 AWS CLI가 없다면 디버그용 임시 파드를 띄우는 편이 빠릅니다.
kubectl -n myns run irsa-debug \
--image=public.ecr.aws/aws-cli/aws-cli:2.15.0 \
--serviceaccount=mysa \
--restart=Never -it --rm -- sh
쉘에서:
aws sts get-caller-identity
- 여기서
AccessDenied면 IRSA 신뢰 체인 문제(A) 입니다. - 여기서 성공하면 권한 정책 문제(B) 로 넘어갑니다.
3) 6~9분: OIDC Provider와 Trust policy를 가장 흔한 패턴으로 교정
3-1. 클러스터 OIDC issuer URL 확인
aws eks describe-cluster --name mycluster --query 'cluster.identity.oidc.issuer' --output text
출력 예시는 보통 https://oidc.eks.ap-northeast-2.amazonaws.com/id/XXXX 형태입니다. 이 값과 IAM의 OIDC provider가 일치해야 합니다.
3-2. IAM OIDC provider가 존재하는지
aws iam list-open-id-connect-providers
필요하면 다음으로 상세 확인:
aws iam get-open-id-connect-provider --open-id-connect-provider-arn arn:aws:iam::123456789012:oidc-provider/oidc.eks.ap-northeast-2.amazonaws.com/id/XXXX
- issuer URL의
id/XXXX가 다르면, 다른 클러스터의 OIDC를 보고 있는 겁니다. - Terraform/eksctl로 클러스터를 재생성했는데 OIDC provider를 갱신하지 않아 엇갈리는 케이스가 흔합니다.
3-3. Trust policy에서 sub 오타/네임스페이스 불일치 잡기
IRSA 역할의 신뢰 정책은 보통 이런 형태입니다. (핵심은 sub 와 aud 조건)
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::123456789012:oidc-provider/oidc.eks.ap-northeast-2.amazonaws.com/id/XXXX"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"oidc.eks.ap-northeast-2.amazonaws.com/id/XXXX:aud": "sts.amazonaws.com",
"oidc.eks.ap-northeast-2.amazonaws.com/id/XXXX:sub": "system:serviceaccount:myns:mysa"
}
}
}
]
}
가장 많이 틀리는 지점:
sub의 네임스페이스가 다름:default로 돼 있거나, 배포 환경별로myns-prod처럼 바뀌었는데 trust policy가 고정됨sub의 서비스어카운트 이름이 다름: Helm 릴리스 접두사로 SA 이름이 바뀌는 경우aud누락 또는 값 불일치: 기본은sts.amazonaws.com
여기까지 고치면 A 유형(AssumeRole 단계) AccessDenied 는 대부분 끝납니다.
4) 9~12분: AssumeRole은 되는데 서비스 권한이 막힐 때
aws sts get-caller-identity 가 성공했는데도 앱이 AccessDenied 를 내면, 이제는 권한 정책(permissions policy) 을 봐야 합니다.
4-1. CloudTrail로 “거절된 액션”을 정확히 찾기
가장 빠른 건 CloudTrail Event history에서 ErrorCode 가 AccessDenied 인 이벤트를 보고, eventName 과 resources 를 확인하는 겁니다.
CLI로도 좁힐 수 있습니다.
aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=Username,AttributeValue=mysa-irsa-role \
--max-results 20
환경에 따라 Username 매칭이 애매할 수 있어, 시간 범위를 줄여 콘솔에서 보는 편이 더 빠를 때도 많습니다.
4-2. 자주 빠지는 권한 6종 세트
- S3:
s3:ListBucket(버킷 ARN),s3:GetObject(오브젝트 ARN) - KMS:
kms:Decrypt(특히 Secrets Manager, S3 SSE-KMS 사용 시) - Secrets Manager:
secretsmanager:GetSecretValue - SQS:
sqs:ReceiveMessage,sqs:DeleteMessage,sqs:GetQueueAttributes - ECR:
ecr:GetAuthorizationToken(이미지 풀은 노드 IAM이지만, 앱이 ECR API 호출하면 필요) - STS: 크로스어카운트 연쇄 AssumeRole이면 추가
sts:AssumeRole
예: Secrets Manager 값을 읽는데 AccessDenied 가 나는 경우의 최소 정책 예시입니다.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"secretsmanager:GetSecretValue"
],
"Resource": "arn:aws:secretsmanager:ap-northeast-2:123456789012:secret:myapp/prod/*"
},
{
"Effect": "Allow",
"Action": [
"kms:Decrypt"
],
"Resource": "arn:aws:kms:ap-northeast-2:123456789012:key/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
}
]
}
4-3. 권한 정책 시뮬레이터로 즉시 검증
정책을 고쳤는데도 여전히 막히면, IAM Policy Simulator로 액션 단위로 확인하는 게 빠릅니다.
CLI 예시:
aws iam simulate-principal-policy \
--policy-source-arn arn:aws:iam::123456789012:role/mysa-irsa-role \
--action-names secretsmanager:GetSecretValue kms:Decrypt \
--resource-arns \
arn:aws:secretsmanager:ap-northeast-2:123456789012:secret:myapp/prod/abc \
arn:aws:kms:ap-northeast-2:123456789012:key/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee
결과가 explicitDeny 면, 다른 정책(예: SCP, permissions boundary, session policy)까지 의심해야 합니다.
5) 그래도 안 풀릴 때: “진짜” 자주 놓치는 함정들
5-1. 조직 SCP 또는 Permissions Boundary
계정이 AWS Organizations 아래에 있고 SCP가 걸려 있으면, 역할 정책을 아무리 열어도 거절됩니다. 징후는 다음과 같습니다.
- 정책 시뮬레이터에서
allowed처럼 보여도 실제 호출은 거절 - CloudTrail에
AccessDenied와 함께explicit deny뉘앙스
이 경우 보안팀/플랫폼팀의 SCP를 확인해야 합니다.
5-2. 잘못된 리전/엔드포인트로 다른 리소스를 때림
예를 들어 AWS_REGION 이 us-east-1 로 잡혀 있으면, 같은 이름의 리소스를 다른 리전에서 찾다가 권한 오류처럼 보일 수 있습니다.
kubectl -n myns exec -it mypod -- sh -lc 'echo $AWS_REGION $AWS_DEFAULT_REGION'
5-3. SDK가 WebIdentity를 안 타는 케이스
- Java: 오래된 AWS SDK v1 설정, 커스텀 credential provider가 우선순위를 먹는 경우
- Node.js:
AWS_SDK_LOAD_CONFIG와 shared config 영향 - Go: 명시적으로 static credentials를 세팅해 둔 경우
증상은 get-caller-identity 는 성공하는데 앱만 실패하거나, 앱 로그에 “환경변수의 키를 사용” 같은 흔적이 보입니다.
6) 운영 팁: 재발 방지용 최소 자동 점검
IRSA는 “설정이 맞는지”를 배포 파이프라인에서 미리 검증하면 재발률이 크게 줄어듭니다.
- 배포 후 smoke test로
aws sts get-caller-identity를 IRSA SA로 실행 - 핵심 API 1개를 실제로 호출해 권한 회귀를 잡기 (예: Secrets Manager 1개 읽기)
- CloudTrail에서
AccessDenied알람 룰 만들기
권한 오류는 보통 재시도(backoff)로 해결되지 않지만, 호출량이 많을수록 장애 파급이 커집니다. 재시도 설계 자체가 필요하다면 OpenAI 429 Rate Limit 재시도·백오프 구현 가이드의 패턴(지수 백오프, 지터, 상한)을 AWS API 호출에도 동일하게 적용할 수 있습니다.
마무리: 가장 많이 맞는 정답은 sub 오타
EKS IRSA AccessDenied 의 1순위는 결국 Trust policy의 sub 불일치입니다. 두 번째가 OIDC provider mismatch, 세 번째가 권한 정책에서 액션/리소스 ARN 누락입니다.
12분 안에 끝내려면, 다음 순서만 기억하면 됩니다.
- 파드가 올바른 SA를 쓰는지
aws sts get-caller-identity로 AssumeRole 단계 분리- OIDC issuer와 trust policy의
aud/sub정합성 - CloudTrail로 거절된 액션을 정확히 보고 정책 보강
원하시면, 현재 보고 있는 에러 로그 한 줄(예: AccessDeniedException 전문)과 사용 중인 SA/Role 이름을 기준으로, 어떤 지점이 가장 유력한지 체크리스트를 더 압축해 드릴게요.