Published on

EKS IRSA 권한 안 먹힘 - AccessDenied 12가지

Authors

서버리스처럼 보이지만, EKS의 IRSA(IAM Roles for Service Accounts)는 사실상 OIDC + ServiceAccount 토큰 + STS AssumeRoleWithWebIdentity 3요소가 정확히 맞물려야만 동작합니다. 한 군데라도 어긋나면 애플리케이션은 "권한이 있는데도" AccessDenied 를 맞습니다.

이 글은 IRSA가 "안 먹히는" 상황을 12가지로 쪼개서, 증상과 확인 명령, 해결책을 한 번에 정리한 실전 체크리스트입니다.

참고: 파드가 재시작되며 원인 파악이 어려우면 먼저 K8s CrashLoopBackOff 원인별 로그·Probe 해결 가이드처럼 로그를 안정적으로 확보한 뒤 IRSA를 진단하는 편이 빠릅니다.

IRSA 동작 흐름 30초 요약

  1. EKS 클러스터는 OIDC Provider를 가짐
  2. Pod는 ServiceAccount에 의해 projected token을 받음
  3. AWS SDK는 AWS_ROLE_ARNAWS_WEB_IDENTITY_TOKEN_FILEAssumeRoleWithWebIdentity 를 호출
  4. STS가 IAM Role의 Trust Policy 조건(sub, aud)을 검증하고 임시 자격증명을 발급
  5. 애플리케이션이 해당 자격증명으로 AWS API 호출

어디가 깨졌는지 찾으려면, 먼저 Pod 안에서 아래를 확인하는 게 가장 확실합니다.

kubectl -n <namespace> exec -it <pod> -- sh -lc '
  echo "ROLE=$AWS_ROLE_ARN";
  echo "TOKEN_FILE=$AWS_WEB_IDENTITY_TOKEN_FILE";
  ls -al $AWS_WEB_IDENTITY_TOKEN_FILE 2>/dev/null || true;
  aws sts get-caller-identity || true
'

위 명령에서 get-caller-identity 가 실패하면 IRSA 체인이 깨진 겁니다. 성공하지만 특정 서비스 호출만 AccessDenied 면 "역할은 Assume 됐지만 권한 정책이 부족"한 케이스입니다.

1) OIDC Provider가 클러스터에 연결되지 않음

증상

  • AssumeRoleWithWebIdentity 호출 자체가 실패
  • 에러에 InvalidIdentityToken 또는 issuer 관련 메시지

확인

aws eks describe-cluster --name <cluster> --query "cluster.identity.oidc.issuer" --output text
aws iam list-open-id-connect-providers | grep -i oidc || true

해결

OIDC provider가 없다면 생성/연결이 필요합니다. eksctl 사용 시 아래가 가장 간단합니다.

eksctl utils associate-iam-oidc-provider --cluster <cluster> --approve

2) Trust Policy의 Principal 이 잘못된 OIDC Provider를 가리킴

증상

  • 같은 계정/리전의 다른 클러스터 OIDC로 설정해둔 경우
  • 특정 클러스터에서만 IRSA 실패

확인

aws iam get-role --role-name <role> --query "Role.AssumeRolePolicyDocument" --output json

Trust Policy의 Federated 값이 클러스터 OIDC ARN과 일치해야 합니다.

해결

OIDC provider ARN을 올바른 것으로 교체합니다.

3) Trust Policy의 sub 조건이 ServiceAccount와 불일치

IRSA에서 가장 흔한 실수입니다.

증상

  • AccessDenied 혹은 Not authorized to perform sts:AssumeRoleWithWebIdentity
  • ServiceAccount를 바꿨거나 namespace를 바꾼 뒤부터 실패

확인

Trust Policy 조건에 보통 아래 형태가 들어갑니다.

  • system:serviceaccount:<namespace>:<serviceaccount>

MDX 빌드 에러 방지를 위해 부등호는 엔티티로 표기합니다.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::123456789012:oidc-provider/oidc.eks.ap-northeast-2.amazonaws.com/id/EXAMPLED539D4633E53DE1B716D3041E"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "oidc.eks.ap-northeast-2.amazonaws.com/id/EXAMPLE:sub": "system:serviceaccount:prod:my-sa"
        }
      }
    }
  ]
}

해결

  • 실제 사용하는 namespace, serviceaccount로 sub 를 맞춥니다.
  • 여러 SA를 허용하려면 StringLike 와 와일드카드를 검토합니다.

4) Trust Policy의 aud 조건 누락/불일치

EKS projected token의 audience는 일반적으로 sts.amazonaws.com 입니다.

증상

  • InvalidIdentityToken 또는 AssumeRole 실패
  • 오래된 예제 그대로 붙여넣은 경우(조건이 누락되거나 다른 audience)

확인

aws iam get-role --role-name <role> --query "Role.AssumeRolePolicyDocument" --output json

해결

aud 조건을 추가하거나 올바르게 수정합니다.

"Condition": {
  "StringEquals": {
    "oidc.eks.ap-northeast-2.amazonaws.com/id/EXAMPLE:aud": "sts.amazonaws.com",
    "oidc.eks.ap-northeast-2.amazonaws.com/id/EXAMPLE:sub": "system:serviceaccount:prod:my-sa"
  }
}

5) ServiceAccount에 role annotation이 없음/오타

증상

  • Pod 환경변수 AWS_ROLE_ARN 이 비어있음
  • SDK가 노드 IAM role(또는 다른 체인)로 떨어짐

확인

kubectl -n <namespace> get sa <sa> -o yaml | sed -n '1,120p'

정상이라면 아래 annotation이 있어야 합니다.

  • eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/<role>

해결

kubectl -n <namespace> annotate sa <sa> eks.amazonaws.com/role-arn=arn:aws:iam::<account>:role/<role> --overwrite

그리고 이미 떠 있는 Pod는 재생성해야 반영됩니다.

kubectl -n <namespace> rollout restart deploy <deployment>

6) Deployment가 다른 ServiceAccount를 참조함

ServiceAccount를 만들고 annotation도 달았는데, 정작 Deployment는 default 를 쓰는 경우가 많습니다.

확인

kubectl -n <namespace> get deploy <deploy> -o jsonpath='{.spec.template.spec.serviceAccountName}{"\n"}'

해결

Deployment(또는 Job, CronJob, StatefulSet)의 serviceAccountName 을 올바르게 지정합니다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: app
spec:
  template:
    spec:
      serviceAccountName: my-sa
      containers:
        - name: app
          image: <image>

7) Pod 토큰 projected volume이 비활성화되었거나 경로가 깨짐

일부 보안 설정이나 커스텀 템플릿에서 automountServiceAccountToken: false 를 넣어버리면 IRSA는 즉시 죽습니다.

증상

  • AWS_WEB_IDENTITY_TOKEN_FILE 는 있는데 파일이 없음
  • 또는 환경변수 자체가 없음

확인

kubectl -n <namespace> get pod <pod> -o jsonpath='{.spec.automountServiceAccountToken}{"\n"}'
kubectl -n <namespace> exec -it <pod> -- sh -lc 'ls -al /var/run/secrets/eks.amazonaws.com/serviceaccount || true'

해결

  • Pod spec 또는 ServiceAccount의 automountServiceAccountTokentrue
  • 보안상 꺼야 한다면 IRSA를 포기하고 다른 인증 전략을 써야 합니다.

8) AWS SDK가 IRSA 대신 다른 자격증명을 먼저 집어감

컨테이너 이미지에 AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY 가 남아있거나, ~/.aws/credentials 가 마운트되면 SDK는 IRSA를 건너뛸 수 있습니다.

증상

  • get-caller-identity 는 되는데 예상한 role이 아님
  • 노드 role 또는 오래된 IAM user로 호출됨

확인

kubectl -n <namespace> exec -it <pod> -- sh -lc '
  env | grep -E "^AWS_(ACCESS_KEY_ID|SECRET_ACCESS_KEY|SESSION_TOKEN|PROFILE)=" || true
  ls -al ~/.aws 2>/dev/null || true
  aws sts get-caller-identity
'

해결

  • 정적 키 환경변수 제거
  • AWS_PROFILE 제거
  • 이미지/차트에서 불필요한 credential 마운트 제거

9) STS 엔드포인트 접근 불가(프라이빗 서브넷, VPC 엔드포인트 이슈)

프라이빗 클러스터에서 NAT 없이 운영하거나, VPC 엔드포인트 정책이 막혀 있으면 STS 호출이 실패합니다.

증상

  • RequestError: send request failed 류 네트워크 에러
  • 또는 STS만 타임아웃

확인

kubectl -n <namespace> exec -it <pod> -- sh -lc '
  nslookup sts.amazonaws.com 2>/dev/null || true
  wget -qO- https://sts.amazonaws.com/ 2>/dev/null | head || true
'

해결

  • NAT Gateway/Instance 구성
  • 또는 STS VPC Endpoint(Interface) 구성
  • 엔드포인트 정책에서 sts:AssumeRoleWithWebIdentity 허용

10) 리전 설정 불일치로 잘못된 STS로 감

일부 SDK/CLI는 리전이 비어 있으면 기본값으로 동작하거나, 다른 리전의 STS로 붙으며 정책/엔드포인트와 충돌할 수 있습니다.

증상

  • 특정 리전에서만 실패
  • InvalidClientTokenId 같은 혼동성 에러

확인

kubectl -n <namespace> exec -it <pod> -- sh -lc '
  echo "AWS_REGION=$AWS_REGION";
  echo "AWS_DEFAULT_REGION=$AWS_DEFAULT_REGION";
  aws configure list || true
'

해결

  • 컨테이너에 AWS_REGION 또는 AWS_DEFAULT_REGION 을 명시
  • 멀티리전 환경이면 서비스별 엔드포인트도 점검

11) 역할은 Assume 됐는데, 실제 IAM Permission Policy가 부족

IRSA가 "안 먹힌다"고 느끼는 많은 케이스가 사실은 여기입니다. sts get-caller-identity 는 성공하지만, S3/ECR/Secrets Manager 호출에서 AccessDenied 가 납니다.

증상

  • AccessDenied 에러 메시지에 호출한 API가 명시됨
    • 예: s3:GetObject, secretsmanager:GetSecretValue, kms:Decrypt

확인

kubectl -n <namespace> exec -it <pod> -- sh -lc 'aws sts get-caller-identity'

그리고 CloudTrail에서 해당 Role이 어떤 API를 거절당했는지 확인합니다.

해결

  • Role에 필요한 action/resource를 정확히 추가
  • KMS가 끼면 KMS Key policy까지 같이 봐야 합니다(다음 항목)

12) KMS/리소스 기반 정책(Resource Policy)에서 Role을 거부

S3 버킷 정책, KMS Key policy, ECR 리포지토리 정책, Secrets Manager 리소스 정책 등은 IAM permission만으로 끝나지 않습니다.

증상

  • IAM policy에 권한을 줬는데도 계속 AccessDenied
  • 특히 kms:Decrypt 는 IAM과 Key policy가 모두 허용해야 성공

확인 포인트

  • KMS Key policy에 해당 Role ARN이 허용되는가
  • S3 bucket policy에 Principal 로 Role이 허용되는가
  • 조직 SCP, Permission Boundary로 차단되는가

해결

  • KMS Key policy에 Role을 추가
  • S3 bucket policy에 Role을 허용
  • SCP/Boundary가 있으면 해당 레이어에서 허용

빠른 진단 체크리스트(운영자용)

아래 순서대로 보면 대부분 10분 안에 원인이 좁혀집니다.

  1. Pod 안에서 aws sts get-caller-identity 가 되나
  2. 결과 ARN이 기대한 Role인가
  3. AWS_ROLE_ARN / AWS_WEB_IDENTITY_TOKEN_FILE 이 존재하나
  4. ServiceAccount annotation과 Deployment의 serviceAccountName 이 일치하나
  5. IAM Role Trust policy의 sub / aud 가 정확한가
  6. STS 네트워크가 열려 있나(NAT 또는 VPC Endpoint)
  7. 권한이 부족한 건 아닌가(CloudTrail로 거절 API 확인)
  8. 리소스 기반 정책(KMS/S3/Secrets)에서 막는 건 아닌가

재현 가능한 테스트용 매니페스트 예시

아래는 IRSA 검증을 위한 최소 Pod 예시입니다. aws-cliget-caller-identity 를 찍어보고, 기대 Role이면 IRSA 파이프라인은 정상입니다.

apiVersion: v1
kind: Pod
metadata:
  name: irsa-debug
  namespace: prod
spec:
  serviceAccountName: my-sa
  restartPolicy: Never
  containers:
    - name: aws
      image: public.ecr.aws/aws-cli/aws-cli:2.15.0
      command: ["sh", "-lc"]
      args:
        - |
          echo "ROLE=$AWS_ROLE_ARN"
          echo "TOKEN_FILE=$AWS_WEB_IDENTITY_TOKEN_FILE"
          aws sts get-caller-identity
          sleep 3600

마무리

IRSA의 AccessDenied 는 "IAM 정책을 더 주면" 해결되는 문제가 아니라, 대개 신원(AssumeRole) 단계에서 막히거나 리소스 정책/KMS 같은 다른 레이어에서 거부되는 문제입니다. 위 12가지를 위에서 아래로 점검하면, 감으로 디버깅하던 시간을 구조적으로 줄일 수 있습니다.

추가로 ECR 인증 문제로 파드가 뜨지도 못하는 상황이라면 IRSA 이전 단계에서 막히는 것이므로 K8s ImagePullBackOff - ECR 인증·토큰 만료 해결도 함께 점검하는 것을 권장합니다.